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