-
-
Notifications
You must be signed in to change notification settings - Fork 308
/
glwindow.jl
244 lines (214 loc) · 9.52 KB
/
glwindow.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
"""
Selection of random objects on the screen is realized by rendering an
object id + plus an arbitrary index into the framebuffer.
The index can be used for e.g. instanced geometries.
"""
struct SelectionID{T <: Integer}
id::T
index::T
end
Base.convert(::Type{SelectionID{T}}, s::SelectionID) where T = SelectionID{T}(T(s.id), T(s.index))
Base.zero(::Type{GLMakie.SelectionID{T}}) where T = SelectionID{T}(T(0), T(0))
mutable struct GLFramebuffer
resolution::Observable{NTuple{2, Int}}
id::GLuint
buffer_ids::Dict{Symbol, GLuint}
buffers::Dict{Symbol, Texture}
render_buffer_ids::Vector{GLuint}
end
# it's guaranteed, that they all have the same size
Base.size(fb::GLFramebuffer) = size(fb.buffers[:color])
Base.haskey(fb::GLFramebuffer, key::Symbol) = haskey(fb.buffers, key)
Base.getindex(fb::GLFramebuffer, key::Symbol) = fb.buffer_ids[key] => fb.buffers[key]
function getfallback(fb::GLFramebuffer, key::Symbol, fallback_key::Symbol)
haskey(fb, key) ? fb[key] : fb[fallback_key]
end
function attach_framebuffer(t::Texture{T, 2}, attachment) where T
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, t.id, 0)
end
# attach texture as color attachment with automatic id picking
function attach_colorbuffer!(fb::GLFramebuffer, key::Symbol, t::Texture{T, 2}) where T
if haskey(fb.buffer_ids, key) || haskey(fb.buffers, key)
error("Key $key already exists.")
end
max_color_id = GL_COLOR_ATTACHMENT0
for id in values(fb.buffer_ids)
if GL_COLOR_ATTACHMENT0 <= id <= GL_COLOR_ATTACHMENT15 && id > max_color_id
max_color_id = id
end
end
next_color_id = max_color_id + 0x1
if next_color_id > GL_COLOR_ATTACHMENT15
error("Ran out of color buffers.")
end
glFramebufferTexture2D(GL_FRAMEBUFFER, next_color_id, GL_TEXTURE_2D, t.id, 0)
push!(fb.buffer_ids, key => next_color_id)
push!(fb.buffers, key => t)
return next_color_id
end
function enum_to_error(s)
s == GL_FRAMEBUFFER_COMPLETE && return
s == GL_FRAMEBUFFER_UNDEFINED &&
error("GL_FRAMEBUFFER_UNDEFINED: The specified framebuffer is the default read or draw framebuffer, but the default framebuffer does not exist.")
s == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT &&
error("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: At least one of the framebuffer attachment points is incomplete.")
s == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT &&
error("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: The framebuffer does not have at least one image attached to it.")
s == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER &&
error("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: The value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for any color attachment point(s) specified by GL_DRAW_BUFFERi.")
s == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER &&
error("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: GL_READ_BUFFER is not GL_NONE and the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color attachment point specified by GL_READ_BUFFER.")
s == GL_FRAMEBUFFER_UNSUPPORTED &&
error("GL_FRAMEBUFFER_UNSUPPORTED: The combination of internal formats of the attached images violates a driver implementation-dependent set of restrictions. Check your OpenGL driver!")
s == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE &&
error("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: The value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers;
if the value of GL_TEXTURE_SAMPLES is not the same for all attached textures; or, if the attached images consist of a mix of renderbuffers and textures,
the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES.
GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is also returned if the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not consistent across all attached textures;
or, if the attached images include a mix of renderbuffers and textures, the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not set to GL_TRUE for all attached textures.")
s == GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS &&
error("GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: Any framebuffer attachment is layered, and any populated attachment is not layered, or if all populated color attachments are not from textures of the same target.")
return error("Unknown framebuffer completion error code: $s")
end
function check_framebuffer()
status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
return enum_to_error(status)
end
function GLFramebuffer(fb_size::NTuple{2, Int})
# Create framebuffer
frambuffer_id = glGenFramebuffers()
glBindFramebuffer(GL_FRAMEBUFFER, frambuffer_id)
# Buffers we always need
# Holds the image that eventually gets displayed
color_buffer = Texture(
RGBA{N0f8}, fb_size, minfilter = :nearest, x_repeat = :clamp_to_edge
)
# Holds a (plot id, element id) for point picking
objectid_buffer = Texture(
Vec{2, GLuint}, fb_size, minfilter = :nearest, x_repeat = :clamp_to_edge
)
# holds depth and stencil values
depth_buffer = Texture(
Ptr{GLAbstraction.DepthStencil_24_8}(C_NULL), fb_size,
minfilter = :nearest, x_repeat = :clamp_to_edge,
internalformat = GL_DEPTH24_STENCIL8,
format = GL_DEPTH_STENCIL
)
# Order Independent Transparency
HDR_color_buffer = Texture(
RGBA{Float16}, fb_size, minfilter = :linear, x_repeat = :clamp_to_edge
)
OIT_weight_buffer = Texture(
N0f8, fb_size, minfilter = :nearest, x_repeat = :clamp_to_edge
)
attach_framebuffer(color_buffer, GL_COLOR_ATTACHMENT0)
attach_framebuffer(objectid_buffer, GL_COLOR_ATTACHMENT1)
attach_framebuffer(HDR_color_buffer, GL_COLOR_ATTACHMENT2)
attach_framebuffer(OIT_weight_buffer, GL_COLOR_ATTACHMENT3)
attach_framebuffer(depth_buffer, GL_DEPTH_ATTACHMENT)
attach_framebuffer(depth_buffer, GL_STENCIL_ATTACHMENT)
check_framebuffer()
fb_size_node = Observable(fb_size)
# To allow adding postprocessors in various combinations we need to keep
# track of the buffer ids that are already in use. We may also want to reuse
# buffers so we give them names for easy fetching.
buffer_ids = Dict{Symbol,GLuint}(
:color => GL_COLOR_ATTACHMENT0,
:objectid => GL_COLOR_ATTACHMENT1,
:HDR_color => GL_COLOR_ATTACHMENT2,
:OIT_weight => GL_COLOR_ATTACHMENT3,
:depth => GL_DEPTH_ATTACHMENT,
:stencil => GL_STENCIL_ATTACHMENT,
)
buffers = Dict{Symbol, Texture}(
:color => color_buffer,
:objectid => objectid_buffer,
:HDR_color => HDR_color_buffer,
:OIT_weight => OIT_weight_buffer,
:depth => depth_buffer,
:stencil => depth_buffer
)
return GLFramebuffer(
fb_size_node, frambuffer_id,
buffer_ids, buffers,
[GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1]
)::GLFramebuffer
end
function Base.resize!(fb::GLFramebuffer, w::Int, h::Int)
(w > 0 && h > 0 && (w, h) != size(fb)) || return
for (name, buffer) in fb.buffers
resize_nocopy!(buffer, (w, h))
end
fb.resolution[] = (w, h)
return nothing
end
struct MonitorProperties
name::String
isprimary::Bool
position::Vec{2, Int}
physicalsize::Vec{2, Int}
videomode::GLFW.VidMode
videomode_supported::Vector{GLFW.VidMode}
dpi::Vec{2, Float64}
monitor::GLFW.Monitor
end
function MonitorProperties(monitor::GLFW.Monitor)
name = GLFW.GetMonitorName(monitor)
isprimary = GLFW.GetPrimaryMonitor() == monitor
position = Vec{2, Int}(GLFW.GetMonitorPos(monitor)...)
physicalsize = Vec{2, Int}(GLFW.GetMonitorPhysicalSize(monitor)...)
videomode = GLFW.GetVideoMode(monitor)
sfactor = Sys.isapple() ? 2.0 : 1.0
dpi = Vec(videomode.width * 25.4, videomode.height * 25.4) * sfactor ./ Vec{2, Float64}(physicalsize)
videomode_supported = GLFW.GetVideoModes(monitor)
MonitorProperties(name, isprimary, position, physicalsize, videomode, videomode_supported, dpi, monitor)
end
was_destroyed(nw::GLFW.Window) = nw.handle == C_NULL
function GLContext()
context = GLFW.GetCurrentContext()
version = opengl_version_number()
glsl_version = glsl_version_number()
return GLContext(context, version, glsl_version, unique_context_counter())
end
function ShaderAbstractions.native_switch_context!(x::GLFW.Window)
GLFW.MakeContextCurrent(x)
end
function ShaderAbstractions.native_context_alive(x::GLFW.Window)
GLFW.is_initialized() && !was_destroyed(x)
end
function destroy!(nw::GLFW.Window)
was_current = ShaderAbstractions.is_current_context(nw)
if !was_destroyed(nw)
GLFW.SetWindowShouldClose(nw, true)
GLFW.PollEvents()
GLFW.DestroyWindow(nw)
nw.handle = C_NULL
end
was_current && ShaderAbstractions.switch_context!()
end
function window_size(nw::GLFW.Window)
was_destroyed(nw) && return (0, 0)
return Tuple(GLFW.GetWindowSize(nw))
end
function window_position(nw::GLFW.Window)
was_destroyed(nw) && return (0, 0)
return Tuple(GLFW.GetWindowPos(window))
end
function framebuffer_size(nw::GLFW.Window)
was_destroyed(nw) && return (0, 0)
return Tuple(GLFW.GetFramebufferSize(nw))
end
function scale_factor(nw::GLFW.Window)
was_destroyed(nw) && return 1f0
return minimum(GLFW.GetWindowContentScale(nw))
end
function Base.isopen(window::GLFW.Window)
was_destroyed(window) && return false
try
return !GLFW.WindowShouldClose(window)
catch e
# can't be open if GLFW is already terminated
e.code == GLFW.NOT_INITIALIZED && return false
rethrow(e)
end
end