Blender  V3.3
app/opengl/display_driver.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright 2011-2022 Blender Foundation */
3 
5 #include "app/opengl/shader.h"
6 
7 #include "util/log.h"
8 #include "util/string.h"
9 
10 #include <GL/glew.h>
11 #include <SDL.h>
12 
14 
15 /* --------------------------------------------------------------------
16  * OpenGLDisplayDriver.
17  */
18 
19 OpenGLDisplayDriver::OpenGLDisplayDriver(const function<bool()> &gl_context_enable,
20  const function<void()> &gl_context_disable)
21  : gl_context_enable_(gl_context_enable), gl_context_disable_(gl_context_disable)
22 {
23 }
24 
26 {
27 }
28 
29 /* --------------------------------------------------------------------
30  * Update procedure.
31  */
32 
34 {
35  /* Assuming no tiles used in interactive display. */
36 }
37 
38 bool OpenGLDisplayDriver::update_begin(const Params &params, int texture_width, 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(
66  GL_TEXTURE_2D, 0, GL_RGBA16F, texture_width, texture_height, 0, GL_RGBA, GL_HALF_FLOAT, 0);
67  texture_.width = texture_width;
68  texture_.height = texture_height;
69  glBindTexture(GL_TEXTURE_2D, 0);
70 
71  /* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to
72  * avoid undefined content. */
73  texture_.need_clear = true;
74  }
75 
76  /* Update PBO dimensions if needed.
77  *
78  * NOTE: Allocate the PBO for the size which will fit the final render resolution (as in,
79  * at a resolution divider 1. This was we don't need to recreate graphics interoperability
80  * objects which are costly and which are tied to the specific underlying buffer size.
81  * The downside of this approach is that when graphics interoperability is not used we are
82  * sending too much data to GPU when resolution divider is not 1. */
83  const int buffer_width = params.full_size.x;
84  const int buffer_height = params.full_size.y;
85  if (texture_.buffer_width != buffer_width || texture_.buffer_height != buffer_height) {
86  const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height;
87  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
88  glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, 0, GL_DYNAMIC_DRAW);
89  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
90 
91  texture_.buffer_width = buffer_width;
92  texture_.buffer_height = buffer_height;
93  }
94 
95  /* New content will be provided to the texture in one way or another, so mark this in a
96  * centralized place. */
97  texture_.need_update = true;
98 
99  return true;
100 }
101 
103 {
104  gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
105  glFlush();
106 
108 }
109 
110 /* --------------------------------------------------------------------
111  * Texture buffer mapping.
112  */
113 
115 {
116  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
117 
118  half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(
119  glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
120  if (!mapped_rgba_pixels) {
121  LOG(ERROR) << "Error mapping OpenGLDisplayDriver pixel buffer object.";
122  }
123 
124  if (texture_.need_clear) {
125  const int64_t texture_width = texture_.width;
126  const int64_t texture_height = texture_.height;
127  memset(reinterpret_cast<void *>(mapped_rgba_pixels),
128  0,
129  texture_width * texture_height * sizeof(half4));
130  texture_.need_clear = false;
131  }
132 
133  return mapped_rgba_pixels;
134 }
135 
137 {
138  glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
139 
140  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
141 }
142 
143 /* --------------------------------------------------------------------
144  * Graphics interoperability.
145  */
146 
148 {
149  GraphicsInterop interop_dst;
150 
151  interop_dst.buffer_width = texture_.buffer_width;
152  interop_dst.buffer_height = texture_.buffer_height;
153  interop_dst.opengl_pbo_id = texture_.gl_pbo_id;
154 
155  interop_dst.need_clear = texture_.need_clear;
156  texture_.need_clear = false;
157 
158  return interop_dst;
159 }
160 
162 {
164 }
165 
167 {
169 }
170 
171 /* --------------------------------------------------------------------
172  * Drawing.
173  */
174 
176 {
177  texture_.need_clear = true;
178 }
179 
181 {
182  /* See do_update_begin() for why no locking is required here. */
183  if (texture_.need_clear) {
184  /* Texture is requested to be cleared and was not yet cleared.
185  * Do early return which should be equivalent of drawing all-zero texture. */
186  return;
187  }
188 
189  if (!gl_draw_resources_ensure()) {
190  return;
191  }
192 
193  if (gl_upload_sync_) {
194  glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
195  }
196 
197  glEnable(GL_BLEND);
198  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
199 
200  display_shader_.bind(params.full_size.x, params.full_size.y);
201 
202  glActiveTexture(GL_TEXTURE0);
203  glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
204 
205  if (texture_.width != params.size.x || texture_.height != params.size.y) {
206  /* Resolution divider is different from 1, force nearest interpolation. */
207  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
208  }
209  else {
210  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
211  }
212 
213  glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
214 
217 
218  GLuint vertex_array_object;
219  glGenVertexArrays(1, &vertex_array_object);
220  glBindVertexArray(vertex_array_object);
221 
222  const int texcoord_attribute = display_shader_.get_tex_coord_attrib_location();
223  const int position_attribute = display_shader_.get_position_attrib_location();
224 
225  glEnableVertexAttribArray(texcoord_attribute);
226  glEnableVertexAttribArray(position_attribute);
227 
228  glVertexAttribPointer(
229  texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0);
230  glVertexAttribPointer(position_attribute,
231  2,
232  GL_FLOAT,
233  GL_FALSE,
234  4 * sizeof(float),
235  (const GLvoid *)(sizeof(float) * 2));
236 
237  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
238 
239  glBindBuffer(GL_ARRAY_BUFFER, 0);
240  glBindTexture(GL_TEXTURE_2D, 0);
241 
242  glDeleteVertexArrays(1, &vertex_array_object);
243 
245 
246  glDisable(GL_BLEND);
247 
248  gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
249  glFlush();
250 }
251 
253 {
254  if (!texture_.gl_id) {
255  /* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can
256  * can not continue. Note that this is not an unrecoverable error, so once the texture is known
257  * we will come back here and create all the GPU resources needed for draw. */
258  return false;
259  }
260 
263  }
265 
266  if (!vertex_buffer_) {
267  glGenBuffers(1, &vertex_buffer_);
268  if (!vertex_buffer_) {
269  LOG(ERROR) << "Error creating vertex buffer.";
270  return false;
271  }
272  }
273 
275 
276  return true;
277 }
278 
280 {
282 
283  if (vertex_buffer_ != 0) {
284  glDeleteBuffers(1, &vertex_buffer_);
285  }
286 
287  if (texture_.gl_pbo_id) {
288  glDeleteBuffers(1, &texture_.gl_pbo_id);
289  texture_.gl_pbo_id = 0;
290  }
291 
292  if (texture_.gl_id) {
293  glDeleteTextures(1, &texture_.gl_id);
294  texture_.gl_id = 0;
295  }
296 
298 }
299 
301 {
302  if (texture_.creation_attempted) {
303  return texture_.is_created;
304  }
305  texture_.creation_attempted = true;
306 
307  DCHECK(!texture_.gl_id);
308  DCHECK(!texture_.gl_pbo_id);
309 
310  /* Create texture. */
311  glGenTextures(1, &texture_.gl_id);
312  if (!texture_.gl_id) {
313  LOG(ERROR) << "Error creating texture.";
314  return false;
315  }
316 
317  /* Configure the texture. */
318  glActiveTexture(GL_TEXTURE0);
319  glBindTexture(GL_TEXTURE_2D, texture_.gl_id);
320  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
321  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
322  glBindTexture(GL_TEXTURE_2D, 0);
323 
324  /* Create PBO for the texture. */
325  glGenBuffers(1, &texture_.gl_pbo_id);
326  if (!texture_.gl_pbo_id) {
327  LOG(ERROR) << "Error creating texture pixel buffer object.";
328  return false;
329  }
330 
331  /* Creation finished with a success. */
332  texture_.is_created = true;
333 
334  return true;
335 }
336 
338 {
339  if (!texture_.need_update) {
340  return;
341  }
342 
343  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id);
344  glTexSubImage2D(
345  GL_TEXTURE_2D, 0, 0, 0, texture_.width, texture_.height, GL_RGBA, GL_HALF_FLOAT, 0);
346  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
347 
348  texture_.need_update = false;
349 }
350 
352 {
353  /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be
354  * rendered. */
355  glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW);
356 
357  float *vpointer = reinterpret_cast<float *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
358  if (!vpointer) {
359  return;
360  }
361 
362  vpointer[0] = 0.0f;
363  vpointer[1] = 0.0f;
364  vpointer[2] = params.full_offset.x;
365  vpointer[3] = params.full_offset.y;
366 
367  vpointer[4] = 1.0f;
368  vpointer[5] = 0.0f;
369  vpointer[6] = (float)params.size.x + params.full_offset.x;
370  vpointer[7] = params.full_offset.y;
371 
372  vpointer[8] = 1.0f;
373  vpointer[9] = 1.0f;
374  vpointer[10] = (float)params.size.x + params.full_offset.x;
375  vpointer[11] = (float)params.size.y + params.full_offset.y;
376 
377  vpointer[12] = 0.0f;
378  vpointer[13] = 1.0f;
379  vpointer[14] = params.full_offset.x;
380  vpointer[15] = (float)params.size.y + params.full_offset.y;
381 
382  glUnmapBuffer(GL_ARRAY_BUFFER);
383 }
384 
typedef float(TangentPoint)[2]
#define glEnable
#define glDisable
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
struct OpenGLDisplayDriver::@1225 texture_
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
Definition: cuda/compat.h:9
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define DCHECK(expression)
Definition: log.h:55
#define LOG(severity)
Definition: log.h:36
__int64 int64_t
Definition: stdint.h:89
Definition: half.h:64