Blender V5.0
app/opengl/display_driver.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
6#include "app/opengl/shader.h"
7
8#include "util/log.h"
9
10#include <SDL.h>
11#include <epoxy/gl.h>
12
14
15/* --------------------------------------------------------------------
16 * OpenGLDisplayDriver.
17 */
18
19OpenGLDisplayDriver::OpenGLDisplayDriver(const std::function<bool()> &gl_context_enable,
20 const std::function<void()> &gl_context_disable)
21 : gl_context_enable_(gl_context_enable), gl_context_disable_(gl_context_disable)
22{
23}
24
26
27/* --------------------------------------------------------------------
28 * Update procedure.
29 */
30
32{
33 /* Assuming no tiles used in interactive display. */
34}
35
37 const int texture_width,
38 const int texture_height)
39{
40 /* Note that it's the responsibility of OpenGLDisplayDriver to ensure updating and drawing
41 * the texture does not happen at the same time. This is achieved indirectly.
42 *
43 * When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock.
44 * This same lock is also held when do_draw() is called, which together ensure mutual
45 * exclusion.
46 *
47 * This locking is not performed on the Cycles side, because that would cause lock inversion. */
48 if (!gl_context_enable_()) {
49 return false;
50 }
51
52 if (gl_render_sync_) {
53 glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
54 }
55
58 return false;
59 }
60
61 /* Update texture dimensions if needed. */
62 if (texture_.width != texture_width || texture_.height != texture_height) {
63 glActiveTexture(GL_TEXTURE0);
64 glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
65 glTexImage2D(GL_TEXTURE_2D,
66 0,
67 GL_RGBA16F,
68 texture_width,
69 texture_height,
70 0,
71 GL_RGBA,
72 GL_HALF_FLOAT,
73 nullptr);
74 texture_.width = texture_width;
75 texture_.height = texture_height;
76 glBindTexture(GL_TEXTURE_2D, 0);
77
78 /* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to
79 * avoid undefined content. */
80 texture_.need_zero = true;
82 }
83
84 /* Update PBO dimensions if needed.
85 *
86 * NOTE: Allocate the PBO for the size which will fit the final render resolution (as in,
87 * at a resolution divider 1. This was we don't need to recreate graphics interoperability
88 * objects which are costly and which are tied to the specific underlying buffer size.
89 * The downside of this approach is that when graphics interoperability is not used we are
90 * sending too much data to GPU when resolution divider is not 1. */
91 const int buffer_width = params.full_size.x;
92 const int buffer_height = params.full_size.y;
93 if (texture_.buffer_width != buffer_width || texture_.buffer_height != buffer_height) {
94 const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height;
95 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
96 glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, nullptr, GL_DYNAMIC_DRAW);
97 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
98
99 texture_.buffer_width = buffer_width;
100 texture_.buffer_height = buffer_height;
101 }
102
103 /* New content will be provided to the texture in one way or another, so mark this in a
104 * centralized place. */
105 texture_.need_update = true;
106
107 return true;
108}
109
111{
112 gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
113 glFlush();
114
116}
117
118/* --------------------------------------------------------------------
119 * Texture buffer mapping.
120 */
121
123{
124 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
125
126 half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(
127 glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
128 if (!mapped_rgba_pixels) {
129 LOG_ERROR << "Error mapping OpenGLDisplayDriver pixel buffer object.";
130 }
131
132 if (texture_.need_zero) {
133 const int64_t texture_width = texture_.width;
134 const int64_t texture_height = texture_.height;
135 memset(reinterpret_cast<void *>(mapped_rgba_pixels),
136 0,
137 texture_width * texture_height * sizeof(half4));
138 texture_.need_zero = false;
139 }
140
141 return mapped_rgba_pixels;
142}
143
145{
146 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
147
148 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
149}
150
151/* --------------------------------------------------------------------
152 * Graphics interoperability.
153 */
154
156{
157 GraphicsInteropDevice interop_device;
158 interop_device.type = GraphicsInteropDevice::OPENGL;
159 return interop_device;
160}
161
163{
164 if (graphics_interop_buffer_.is_empty()) {
166 texture_.gl_pbo_id,
167 texture_.buffer_width * texture_.buffer_height *
168 sizeof(half4));
169 }
170
171 if (texture_.need_zero) {
173 texture_.need_zero = false;
174 }
175}
176
181
186
187/* --------------------------------------------------------------------
188 * Drawing.
189 */
190
192{
193 texture_.need_zero = true;
194}
195
197{
198 /* See do_update_begin() for why no locking is required here. */
199 if (texture_.need_zero) {
200 /* Texture is requested to be cleared and was not yet cleared.
201 * Do early return which should be equivalent of drawing all-zero texture. */
202 return;
203 }
204
206 return;
207 }
208
209 if (gl_upload_sync_) {
210 glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
211 }
212
213 glEnable(GL_BLEND);
214 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
215
216 display_shader_.bind(params.full_size.x, params.full_size.y);
217
218 glActiveTexture(GL_TEXTURE0);
219 glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
220
221 if (texture_.width != params.size.x || texture_.height != params.size.y) {
222 /* Resolution divider is different from 1, force nearest interpolation. */
223 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
224 }
225 else {
226 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
227 }
228
229 glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
230
233
234 GLuint vertex_array_object;
235 glGenVertexArrays(1, &vertex_array_object);
236 glBindVertexArray(vertex_array_object);
237
238 const int texcoord_attribute = display_shader_.get_tex_coord_attrib_location();
239 const int position_attribute = display_shader_.get_position_attrib_location();
240
241 glEnableVertexAttribArray(texcoord_attribute);
242 glEnableVertexAttribArray(position_attribute);
243
244 glVertexAttribPointer(
245 texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)nullptr);
246 glVertexAttribPointer(position_attribute,
247 2,
248 GL_FLOAT,
249 GL_FALSE,
250 4 * sizeof(float),
251 (const GLvoid *)(sizeof(float) * 2));
252
253 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
254
255 glBindBuffer(GL_ARRAY_BUFFER, 0);
256 glBindTexture(GL_TEXTURE_2D, 0);
257
258 glDeleteVertexArrays(1, &vertex_array_object);
259
260 display_shader_.unbind();
261
262 glDisable(GL_BLEND);
263
264 gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
265 glFlush();
266}
267
269{
270 if (!texture_.gl_id) {
271 /* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can
272 * can not continue. Note that this is not an unrecoverable error, so once the texture is known
273 * we will come back here and create all the GPU resources needed for draw. */
274 return false;
275 }
276
279 }
281
282 if (!vertex_buffer_) {
283 glGenBuffers(1, &vertex_buffer_);
284 if (!vertex_buffer_) {
285 LOG_ERROR << "Error creating vertex buffer.";
286 return false;
287 }
288 }
289
291
292 return true;
293}
294
296{
298
299 if (vertex_buffer_ != 0) {
300 glDeleteBuffers(1, &vertex_buffer_);
301 }
302
303 if (texture_.gl_pbo_id) {
304 glDeleteBuffers(1, &texture_.gl_pbo_id);
305 texture_.gl_pbo_id = 0;
306 }
307
308 if (texture_.gl_id) {
309 glDeleteTextures(1, &texture_.gl_id);
310 texture_.gl_id = 0;
311 }
312
314}
315
317{
318 if (texture_.creation_attempted) {
319 return texture_.is_created;
320 }
321 texture_.creation_attempted = true;
322
323 DCHECK(!texture_.gl_id);
324 DCHECK(!texture_.gl_pbo_id);
325
326 /* Create texture. */
327 glGenTextures(1, &texture_.gl_id);
328 if (!texture_.gl_id) {
329 LOG_ERROR << "Error creating texture.";
330 return false;
331 }
332
333 /* Configure the texture. */
334 glActiveTexture(GL_TEXTURE0);
335 glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
336 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
337 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
338 glBindTexture(GL_TEXTURE_2D, 0);
339
340 /* Create PBO for the texture. */
341 glGenBuffers(1, &texture_.gl_pbo_id);
342 if (!texture_.gl_pbo_id) {
343 LOG_ERROR << "Error creating texture pixel buffer object.";
344 return false;
345 }
346
347 /* Creation finished with a success. */
348 texture_.is_created = true;
350
351 return true;
352}
353
355{
356 if (!texture_.need_update) {
357 return;
358 }
359
360 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
361 glTexSubImage2D(
362 GL_TEXTURE_2D, 0, 0, 0, texture_.width, texture_.height, GL_RGBA, GL_HALF_FLOAT, nullptr);
363 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
364
365 texture_.need_update = false;
366}
367
369{
370 /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be
371 * rendered. */
372 glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), nullptr, GL_STREAM_DRAW);
373
374 float *vpointer = reinterpret_cast<float *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
375 if (!vpointer) {
376 return;
377 }
378
379 vpointer[0] = 0.0f;
380 vpointer[1] = 0.0f;
381 vpointer[2] = params.full_offset.x;
382 vpointer[3] = params.full_offset.y;
383
384 vpointer[4] = 1.0f;
385 vpointer[5] = 0.0f;
386 vpointer[6] = (float)params.size.x + params.full_offset.x;
387 vpointer[7] = params.full_offset.y;
388
389 vpointer[8] = 1.0f;
390 vpointer[9] = 1.0f;
391 vpointer[10] = (float)params.size.x + params.full_offset.x;
392 vpointer[11] = (float)params.size.y + params.full_offset.y;
393
394 vpointer[12] = 0.0f;
395 vpointer[13] = 1.0f;
396 vpointer[14] = params.full_offset.x;
397 vpointer[15] = (float)params.size.y + params.full_offset.y;
398
399 glUnmapBuffer(GL_ARRAY_BUFFER);
400}
401
long long int int64_t
GraphicsInteropBuffer graphics_interop_buffer_
void vertex_buffer_update(const Params &params)
~OpenGLDisplayDriver() override
bool update_begin(const Params &params, const int texture_width, const int texture_height) override
std::function< void()> gl_context_disable_
OpenGLDisplayDriver(const std::function< bool()> &gl_context_enable, const std::function< void()> &gl_context_disable)
void graphics_interop_update_buffer() override
GraphicsInteropDevice graphics_interop_get_device() override
void graphics_interop_deactivate() override
std::function< bool()> gl_context_enable_
struct OpenGLDisplayDriver::@156212331323220374276325202330063261145026220273 texture_
half4 * map_texture_buffer() override
void draw(const Params &params) override
nullptr float
#define CCL_NAMESPACE_END
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define DCHECK(expression)
Definition log.h:135
#define LOG_ERROR
Definition log.h:101
Definition half.h:60