Blender  V3.3
blender/display_driver.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright 2021-2022 Blender Foundation */
3 
5 
6 #include "device/device.h"
7 #include "util/log.h"
8 #include "util/opengl.h"
9 
10 #include "GPU_platform.h"
11 
12 extern "C" {
13 struct RenderEngine;
14 
15 bool RE_engine_has_render_context(struct RenderEngine *engine);
18 
20 void DRW_opengl_context_activate(bool drw_state);
21 
23 void WM_opengl_context_activate(void *gl_context);
24 void WM_opengl_context_dispose(void *gl_context);
26 }
27 
29 
30 /* --------------------------------------------------------------------
31  * BlenderDisplayShader.
32  */
33 
34 unique_ptr<BlenderDisplayShader> BlenderDisplayShader::create(BL::RenderEngine &b_engine,
35  BL::Scene &b_scene)
36 {
37  if (b_engine.support_display_space_shader(b_scene)) {
38  return make_unique<BlenderDisplaySpaceShader>(b_engine, b_scene);
39  }
40 
41  return make_unique<BlenderFallbackDisplayShader>();
42 }
43 
45 {
46  if (position_attribute_location_ == -1) {
47  const uint shader_program = get_shader_program();
48  position_attribute_location_ = glGetAttribLocation(shader_program, position_attribute_name);
49  }
51 }
52 
54 {
56  const uint shader_program = get_shader_program();
57  tex_coord_attribute_location_ = glGetAttribLocation(shader_program, tex_coord_attribute_name);
58  }
60 }
61 
62 /* --------------------------------------------------------------------
63  * BlenderFallbackDisplayShader.
64  */
65 
66 /* TODO move shaders to standalone .glsl file. */
67 static const char *FALLBACK_VERTEX_SHADER =
68  "#version 330\n"
69  "uniform vec2 fullscreen;\n"
70  "in vec2 texCoord;\n"
71  "in vec2 pos;\n"
72  "out vec2 texCoord_interp;\n"
73  "\n"
74  "vec2 normalize_coordinates()\n"
75  "{\n"
76  " return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n"
77  "}\n"
78  "\n"
79  "void main()\n"
80  "{\n"
81  " gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n"
82  " texCoord_interp = texCoord;\n"
83  "}\n\0";
84 
85 static const char *FALLBACK_FRAGMENT_SHADER =
86  "#version 330\n"
87  "uniform sampler2D image_texture;\n"
88  "in vec2 texCoord_interp;\n"
89  "out vec4 fragColor;\n"
90  "\n"
91  "void main()\n"
92  "{\n"
93  " fragColor = texture(image_texture, texCoord_interp);\n"
94  "}\n\0";
95 
96 static void shader_print_errors(const char *task, const char *log, const char *code)
97 {
98  LOG(ERROR) << "Shader: " << task << " error:";
99  LOG(ERROR) << "===== shader string ====";
100 
101  stringstream stream(code);
102  string partial;
103 
104  int line = 1;
105  while (getline(stream, partial, '\n')) {
106  if (line < 10) {
107  LOG(ERROR) << " " << line << " " << partial;
108  }
109  else {
110  LOG(ERROR) << line << " " << partial;
111  }
112  line++;
113  }
114  LOG(ERROR) << log;
115 }
116 
117 static int compile_fallback_shader(void)
118 {
119  const struct Shader {
120  const char *source;
121  const GLenum type;
122  } shaders[2] = {{FALLBACK_VERTEX_SHADER, GL_VERTEX_SHADER},
123  {FALLBACK_FRAGMENT_SHADER, GL_FRAGMENT_SHADER}};
124 
125  const GLuint program = glCreateProgram();
126 
127  for (int i = 0; i < 2; i++) {
128  const GLuint shader = glCreateShader(shaders[i].type);
129 
130  string source_str = shaders[i].source;
131  const char *c_str = source_str.c_str();
132 
133  glShaderSource(shader, 1, &c_str, NULL);
134  glCompileShader(shader);
135 
136  GLint compile_status;
137  glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
138 
139  if (!compile_status) {
140  GLchar log[5000];
141  GLsizei length = 0;
142  glGetShaderInfoLog(shader, sizeof(log), &length, log);
143  shader_print_errors("compile", log, c_str);
144  return 0;
145  }
146 
147  glAttachShader(program, shader);
148  }
149 
150  /* Link output. */
151  glBindFragDataLocation(program, 0, "fragColor");
152 
153  /* Link and error check. */
154  glLinkProgram(program);
155 
156  /* TODO(sergey): Find a way to nicely de-duplicate the error checking. */
157  GLint link_status;
158  glGetProgramiv(program, GL_LINK_STATUS, &link_status);
159  if (!link_status) {
160  GLchar log[5000];
161  GLsizei length = 0;
162  /* TODO(sergey): Is it really program passed to glGetShaderInfoLog? */
163  glGetShaderInfoLog(program, sizeof(log), &length, log);
166  return 0;
167  }
168 
169  return program;
170 }
171 
173 {
175 
176  if (!shader_program_) {
177  return;
178  }
179 
180  glUseProgram(shader_program_);
181  glUniform1i(image_texture_location_, 0);
182  glUniform2f(fullscreen_location_, width, height);
183 }
184 
186 {
187 }
188 
190 {
191  return shader_program_;
192 }
193 
195 {
197  return;
198  }
199 
201 
203  if (!shader_program_) {
204  return;
205  }
206 
207  glUseProgram(shader_program_);
208 
209  image_texture_location_ = glGetUniformLocation(shader_program_, "image_texture");
210  if (image_texture_location_ < 0) {
211  LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform.";
212  destroy_shader();
213  return;
214  }
215 
216  fullscreen_location_ = glGetUniformLocation(shader_program_, "fullscreen");
217  if (fullscreen_location_ < 0) {
218  LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform.";
219  destroy_shader();
220  return;
221  }
222 }
223 
225 {
226  glDeleteProgram(shader_program_);
227  shader_program_ = 0;
228 }
229 
230 /* --------------------------------------------------------------------
231  * BlenderDisplaySpaceShader.
232  */
233 
235  BL::Scene &b_scene)
236  : b_engine_(b_engine), b_scene_(b_scene)
237 {
238  DCHECK(b_engine_.support_display_space_shader(b_scene_));
239 }
240 
241 void BlenderDisplaySpaceShader::bind(int /*width*/, int /*height*/)
242 {
243  b_engine_.bind_display_space_shader(b_scene_);
244 }
245 
247 {
248  b_engine_.unbind_display_space_shader();
249 }
250 
252 {
253  if (!shader_program_) {
254  glGetIntegerv(GL_CURRENT_PROGRAM, reinterpret_cast<int *>(&shader_program_));
255  }
256 
257  if (!shader_program_) {
258  LOG(ERROR) << "Error retrieving shader program for display space shader.";
259  }
260 
261  return shader_program_;
262 }
263 
264 /* --------------------------------------------------------------------
265  * DrawTile.
266  */
267 
268 /* Higher level representation of a texture from the graphics library. */
269 class GLTexture {
270  public:
271  /* Global counter for all allocated OpenGL textures used by instances of this class. */
272  static inline std::atomic<int> num_used = 0;
273 
274  GLTexture() = default;
275 
277  {
278  assert(gl_id == 0);
279  }
280 
281  GLTexture(const GLTexture &other) = delete;
282  GLTexture &operator=(GLTexture &other) = delete;
283 
284  GLTexture(GLTexture &&other) noexcept
285  : gl_id(other.gl_id), width(other.width), height(other.height)
286  {
287  other.reset();
288  }
289 
291  {
292  if (this == &other) {
293  return *this;
294  }
295 
296  gl_id = other.gl_id;
297  width = other.width;
298  height = other.height;
299 
300  other.reset();
301 
302  return *this;
303  }
304 
306  {
307  if (gl_id) {
308  return true;
309  }
310 
311  /* Create texture. */
312  glGenTextures(1, &gl_id);
313  if (!gl_id) {
314  LOG(ERROR) << "Error creating texture.";
315  return false;
316  }
317 
318  /* Configure the texture. */
319  glActiveTexture(GL_TEXTURE0);
320  glBindTexture(GL_TEXTURE_2D, gl_id);
321 
322  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
323  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
324 
325  /* Clamp to edge so that precision issues when zoomed out (which forces linear interpolation)
326  * does not cause unwanted repetition. */
327  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
328  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
329 
330  glBindTexture(GL_TEXTURE_2D, 0);
331 
332  ++num_used;
333 
334  return true;
335  }
336 
338  {
339  if (!gl_id) {
340  return;
341  }
342 
343  glDeleteTextures(1, &gl_id);
344 
345  reset();
346 
347  --num_used;
348  }
349 
350  /* OpenGL resource IDs of the texture.
351  *
352  * NOTE: Allocated on the render engine's context. */
353  uint gl_id = 0;
354 
355  /* Dimensions of the texture in pixels. */
356  int width = 0;
357  int height = 0;
358 
359  protected:
360  void reset()
361  {
362  gl_id = 0;
363  width = 0;
364  height = 0;
365  }
366 };
367 
368 /* Higher level representation of a Pixel Buffer Object (PBO) from the graphics library. */
370  public:
371  /* Global counter for all allocated OpenGL PBOs used by instances of this class. */
372  static inline std::atomic<int> num_used = 0;
373 
374  GLPixelBufferObject() = default;
375 
377  {
378  assert(gl_id == 0);
379  }
380 
381  GLPixelBufferObject(const GLPixelBufferObject &other) = delete;
383 
385  : gl_id(other.gl_id), width(other.width), height(other.height)
386  {
387  other.reset();
388  }
389 
391  {
392  if (this == &other) {
393  return *this;
394  }
395 
396  gl_id = other.gl_id;
397  width = other.width;
398  height = other.height;
399 
400  other.reset();
401 
402  return *this;
403  }
404 
406  {
407  if (gl_id) {
408  return true;
409  }
410 
411  glGenBuffers(1, &gl_id);
412  if (!gl_id) {
413  LOG(ERROR) << "Error creating texture pixel buffer object.";
414  return false;
415  }
416 
417  ++num_used;
418 
419  return true;
420  }
421 
423  {
424  if (!gl_id) {
425  return;
426  }
427 
428  glDeleteBuffers(1, &gl_id);
429 
430  reset();
431 
432  --num_used;
433  }
434 
435  /* OpenGL resource IDs of the PBO.
436  *
437  * NOTE: Allocated on the render engine's context. */
438  uint gl_id = 0;
439 
440  /* Dimensions of the PBO. */
441  int width = 0;
442  int height = 0;
443 
444  protected:
445  void reset()
446  {
447  gl_id = 0;
448  width = 0;
449  height = 0;
450  }
451 };
452 
453 class DrawTile {
454  public:
455  DrawTile() = default;
456  ~DrawTile() = default;
457 
458  DrawTile(const DrawTile &other) = delete;
459  DrawTile &operator=(const DrawTile &other) = delete;
460 
461  DrawTile(DrawTile &&other) noexcept = default;
462 
463  DrawTile &operator=(DrawTile &&other) = default;
464 
466  {
467  if (!texture.gl_resources_ensure()) {
469  return false;
470  }
471 
472  return true;
473  }
474 
476  {
478  }
479 
480  inline bool ready_to_draw() const
481  {
482  return texture.gl_id != 0;
483  }
484 
485  /* Texture which contains pixels of the tile. */
487 
488  /* Display parameters the texture of this tile has been updated for. */
490 };
491 
493  public:
495  {
498  return false;
499  }
500 
501  return true;
502  }
503 
505  {
508  }
509 
513 };
514 
515 /* --------------------------------------------------------------------
516  * BlenderDisplayDriver.
517  */
518 
520  /* Resources of a tile which is being currently rendered. */
522 
523  /* All tiles which rendering is finished and which content will not be changed. */
524  struct {
526 
527  void gl_resources_destroy_and_clear()
528  {
529  for (DrawTile &tile : tiles) {
530  tile.gl_resources_destroy();
531  }
532 
533  tiles.clear();
534  }
536 
537  /* OpenGL vertex buffer needed for drawing. */
539 
541  {
542  if (!gl_vertex_buffer) {
543  glGenBuffers(1, &gl_vertex_buffer);
544  if (!gl_vertex_buffer) {
545  LOG(ERROR) << "Error allocating tile VBO.";
546  return false;
547  }
548  }
549 
550  return true;
551  }
552 
554  {
555  if (gl_vertex_buffer) {
556  glDeleteBuffers(1, &gl_vertex_buffer);
557  gl_vertex_buffer = 0;
558  }
559  }
560 };
561 
563  : b_engine_(b_engine),
564  display_shader_(BlenderDisplayShader::create(b_engine, b_scene)),
565  tiles_(make_unique<Tiles>())
566 {
567  /* Create context while on the main thread. */
569 }
570 
572 {
574 }
575 
576 /* --------------------------------------------------------------------
577  * Update procedure.
578  */
579 
581 {
582  if (!tiles_->current_tile.tile.ready_to_draw()) {
583  LOG(ERROR)
584  << "Unexpectedly moving to the next tile without any data provided for current tile.";
585  return;
586  }
587 
588  /* Moving to the next tile without giving render data for the current tile is not an expected
589  * situation. */
591  /* Texture should have been updated from the PBO at this point. */
592  DCHECK(!tiles_->current_tile.need_update_texture_pixels);
593 
594  tiles_->finished_tiles.tiles.emplace_back(std::move(tiles_->current_tile.tile));
595 }
596 
598  int texture_width,
599  int texture_height)
600 {
601  /* Note that it's the responsibility of BlenderDisplayDriver to ensure updating and drawing
602  * the texture does not happen at the same time. This is achieved indirectly.
603  *
604  * When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock.
605  * This same lock is also held when do_draw() is called, which together ensure mutual
606  * exclusion.
607  *
608  * This locking is not performed on the Cycles side, because that would cause lock inversion. */
609  if (!gl_context_enable()) {
610  return false;
611  }
612 
613  if (gl_render_sync_) {
614  glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
615  }
616 
617  DrawTile &current_tile = tiles_->current_tile.tile;
618  GLPixelBufferObject &current_tile_buffer_object = tiles_->current_tile.buffer_object;
619 
620  /* Clear storage of all finished tiles when display clear is requested.
621  * Do it when new tile data is provided to handle the display clear flag in a single place.
622  * It also makes the logic reliable from the whether drawing did happen or not point of view. */
623  if (need_clear_) {
624  tiles_->finished_tiles.gl_resources_destroy_and_clear();
625  need_clear_ = false;
626  }
627 
628  if (!tiles_->gl_resources_ensure()) {
629  tiles_->gl_resources_destroy();
631  return false;
632  }
633 
634  if (!tiles_->current_tile.gl_resources_ensure()) {
635  tiles_->current_tile.gl_resources_destroy();
637  return false;
638  }
639 
640  /* Update texture dimensions if needed. */
641  if (current_tile.texture.width != texture_width ||
642  current_tile.texture.height != texture_height) {
643  glActiveTexture(GL_TEXTURE0);
644  glBindTexture(GL_TEXTURE_2D, current_tile.texture.gl_id);
645  glTexImage2D(
646  GL_TEXTURE_2D, 0, GL_RGBA16F, texture_width, texture_height, 0, GL_RGBA, GL_HALF_FLOAT, 0);
647  current_tile.texture.width = texture_width;
648  current_tile.texture.height = texture_height;
649  glBindTexture(GL_TEXTURE_2D, 0);
650  }
651 
652  /* Update PBO dimensions if needed.
653  *
654  * NOTE: Allocate the PBO for the size which will fit the final render resolution (as in,
655  * at a resolution divider 1. This was we don't need to recreate graphics interoperability
656  * objects which are costly and which are tied to the specific underlying buffer size.
657  * The downside of this approach is that when graphics interoperability is not used we are
658  * sending too much data to GPU when resolution divider is not 1. */
659  /* TODO(sergey): Investigate whether keeping the PBO exact size of the texture makes non-interop
660  * mode faster. */
661  const int buffer_width = params.size.x;
662  const int buffer_height = params.size.y;
663  if (current_tile_buffer_object.width != buffer_width ||
664  current_tile_buffer_object.height != buffer_height) {
665  const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height;
666  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, current_tile_buffer_object.gl_id);
667  glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, 0, GL_DYNAMIC_DRAW);
668  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
669 
670  current_tile_buffer_object.width = buffer_width;
671  current_tile_buffer_object.height = buffer_height;
672  }
673 
674  /* Store an updated parameters of the current tile.
675  * In theory it is only needed once per update of the tile, but doing it on every update is
676  * the easiest and is not expensive. */
677  tiles_->current_tile.tile.params = params;
678 
679  return true;
680 }
681 
683 {
684  const GLTexture &texture = tile.tile.texture;
685 
686  DCHECK_NE(tile.buffer_object.gl_id, 0);
687 
688  glActiveTexture(GL_TEXTURE0);
689  glBindTexture(GL_TEXTURE_2D, texture.gl_id);
690  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, tile.buffer_object.gl_id);
691 
692  glTexSubImage2D(
693  GL_TEXTURE_2D, 0, 0, 0, texture.width, texture.height, GL_RGBA, GL_HALF_FLOAT, 0);
694 
695  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
696  glBindTexture(GL_TEXTURE_2D, 0);
697 }
698 
700 {
701  /* Unpack the PBO into the texture as soon as the new content is provided.
702  *
703  * This allows to ensure that the unpacking happens while resources like graphics interop (which
704  * lifetime is outside of control of the display driver) are still valid, as well as allows to
705  * move the tile from being current to finished immediately after this call.
706  *
707  * One concern with this approach is that if the update happens more often than drawing then
708  * doing the unpack here occupies GPU transfer for no good reason. However, the render scheduler
709  * takes care of ensuring updates don't happen that often. In regular applications redraw will
710  * happen much more often than this update.
711  *
712  * On some older GPUs on macOS, there is a driver crash when updating the texture for viewport
713  * renders while Blender is drawing. As a workaround update texture during draw, under assumption
714  * that there is no graphics interop on macOS and viewport render has a single tile. */
715  if (use_gl_context_ &&
717  tiles_->current_tile.need_update_texture_pixels = true;
718  }
719  else {
720  update_tile_texture_pixels(tiles_->current_tile);
721  }
722 
723  gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
724  glFlush();
725 
727 }
728 
729 /* --------------------------------------------------------------------
730  * Texture buffer mapping.
731  */
732 
734 {
735  const uint pbo_gl_id = tiles_->current_tile.buffer_object.gl_id;
736 
737  DCHECK_NE(pbo_gl_id, 0);
738 
739  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_gl_id);
740 
741  half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>(
742  glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
743  if (!mapped_rgba_pixels) {
744  LOG(ERROR) << "Error mapping BlenderDisplayDriver pixel buffer object.";
745  }
746 
747  return mapped_rgba_pixels;
748 }
749 
751 {
752  glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
753 
754  glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
755 }
756 
757 /* --------------------------------------------------------------------
758  * Graphics interoperability.
759  */
760 
762 {
763  GraphicsInterop interop_dst;
764 
765  interop_dst.buffer_width = tiles_->current_tile.buffer_object.width;
766  interop_dst.buffer_height = tiles_->current_tile.buffer_object.height;
767  interop_dst.opengl_pbo_id = tiles_->current_tile.buffer_object.gl_id;
768 
769  return interop_dst;
770 }
771 
773 {
775 }
776 
778 {
780 }
781 
782 /* --------------------------------------------------------------------
783  * Drawing.
784  */
785 
787 {
788  need_clear_ = true;
789 }
790 
791 void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y)
792 {
793  zoom_ = make_float2(zoom_x, zoom_y);
794 }
795 
796 /* Update vertex buffer with new coordinates of vertex positions and texture coordinates.
797  * This buffer is used to render texture in the viewport.
798  *
799  * NOTE: The buffer needs to be bound. */
801 {
802  const int x = params.full_offset.x;
803  const int y = params.full_offset.y;
804 
805  const int width = params.size.x;
806  const int height = params.size.y;
807 
808  /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be
809  * rendered. */
810  glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW);
811 
812  float *vpointer = reinterpret_cast<float *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
813  if (!vpointer) {
814  return;
815  }
816 
817  vpointer[0] = 0.0f;
818  vpointer[1] = 0.0f;
819  vpointer[2] = x;
820  vpointer[3] = y;
821 
822  vpointer[4] = 1.0f;
823  vpointer[5] = 0.0f;
824  vpointer[6] = x + width;
825  vpointer[7] = y;
826 
827  vpointer[8] = 1.0f;
828  vpointer[9] = 1.0f;
829  vpointer[10] = x + width;
830  vpointer[11] = y + height;
831 
832  vpointer[12] = 0.0f;
833  vpointer[13] = 1.0f;
834  vpointer[14] = x;
835  vpointer[15] = y + height;
836 
837  glUnmapBuffer(GL_ARRAY_BUFFER);
838 }
839 
840 static void draw_tile(const float2 &zoom,
841  const int texcoord_attribute,
842  const int position_attribute,
843  const DrawTile &draw_tile,
844  const uint gl_vertex_buffer)
845 {
846  if (!draw_tile.ready_to_draw()) {
847  return;
848  }
849 
850  const GLTexture &texture = draw_tile.texture;
851 
852  DCHECK_NE(texture.gl_id, 0);
853  DCHECK_NE(gl_vertex_buffer, 0);
854 
855  glBindBuffer(GL_ARRAY_BUFFER, gl_vertex_buffer);
856 
857  /* Draw at the parameters for which the texture has been updated for. This allows to always draw
858  * texture during bordered-rendered camera view without flickering. The validness of the display
859  * parameters for a texture is guaranteed by the initial "clear" state which makes drawing to
860  * have an early output.
861  *
862  * Such approach can cause some extra "jelly" effect during panning, but it is not more jelly
863  * than overlay of selected objects. Also, it's possible to redraw texture at an intersection of
864  * the texture draw parameters and the latest updated draw parameters (although, complexity of
865  * doing it might not worth it. */
867 
868  glBindTexture(GL_TEXTURE_2D, texture.gl_id);
869 
870  /* Trick to keep sharp rendering without jagged edges on all GPUs.
871  *
872  * The idea here is to enforce driver to use linear interpolation when the image is not zoomed
873  * in.
874  * For the render result with a resolution divider in effect we always use nearest interpolation.
875  *
876  * Use explicit MIN assignment to make sure the driver does not have an undefined behavior at
877  * the zoom level 1. The MAG filter is always NEAREST. */
878  const float zoomed_width = draw_tile.params.size.x * zoom.x;
879  const float zoomed_height = draw_tile.params.size.y * zoom.y;
880  if (texture.width != draw_tile.params.size.x || texture.height != draw_tile.params.size.y) {
881  /* Resolution divider is different from 1, force nearest interpolation. */
882  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
883  }
884  else if (zoomed_width - draw_tile.params.size.x > 0.5f ||
885  zoomed_height - draw_tile.params.size.y > 0.5f) {
886  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
887  }
888  else {
889  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
890  }
891 
892  glVertexAttribPointer(
893  texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0);
894  glVertexAttribPointer(position_attribute,
895  2,
896  GL_FLOAT,
897  GL_FALSE,
898  4 * sizeof(float),
899  (const GLvoid *)(sizeof(float) * 2));
900 
901  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
902 }
903 
905 {
906  /* This is called from the render thread that also calls update_begin/end, right before ending
907  * the render loop. We wait for any queued PBO and render commands to be done, before destroying
908  * the render thread and activating the context in the main thread to destroy resources.
909  *
910  * If we don't do this, the NVIDIA driver hangs for a few seconds for when ending 3D viewport
911  * rendering, for unknown reasons. This was found with NVIDIA driver version 470.73 and a Quadro
912  * RTX 6000 on Linux. */
913  if (!gl_context_enable()) {
914  return;
915  }
916 
917  if (gl_upload_sync_) {
918  glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
919  }
920 
921  if (gl_render_sync_) {
922  glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
923  }
924 
926 }
927 
929 {
930  /* See do_update_begin() for why no locking is required here. */
931  if (use_gl_context_) {
932  gl_context_mutex_.lock();
933  }
934 
935  if (need_clear_) {
936  /* Texture is requested to be cleared and was not yet cleared.
937  *
938  * Do early return which should be equivalent of drawing all-zero texture.
939  * Watch out for the lock though so that the clear happening during update is properly
940  * synchronized here. */
941  if (use_gl_context_) {
942  gl_context_mutex_.unlock();
943  }
944  return;
945  }
946 
947  if (gl_upload_sync_) {
948  glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
949  }
950 
951  glEnable(GL_BLEND);
952  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
953 
954  glActiveTexture(GL_TEXTURE0);
955 
956  /* NOTE: The VAO is to be allocated on the drawing context as it is not shared across contexts.
957  * Simplest is to allocate it on every redraw so that it is possible to destroy it from a
958  * correct context. */
959  GLuint vertex_array_object;
960  glGenVertexArrays(1, &vertex_array_object);
961  glBindVertexArray(vertex_array_object);
962 
963  display_shader_->bind(params.full_size.x, params.full_size.y);
964 
965  const int texcoord_attribute = display_shader_->get_tex_coord_attrib_location();
966  const int position_attribute = display_shader_->get_position_attrib_location();
967 
968  glEnableVertexAttribArray(texcoord_attribute);
969  glEnableVertexAttribArray(position_attribute);
970 
971  if (tiles_->current_tile.need_update_texture_pixels) {
972  update_tile_texture_pixels(tiles_->current_tile);
973  tiles_->current_tile.need_update_texture_pixels = false;
974  }
975 
977  texcoord_attribute,
978  position_attribute,
979  tiles_->current_tile.tile,
980  tiles_->gl_vertex_buffer);
981 
982  for (const DrawTile &tile : tiles_->finished_tiles.tiles) {
983  draw_tile(zoom_, texcoord_attribute, position_attribute, tile, tiles_->gl_vertex_buffer);
984  }
985 
986  display_shader_->unbind();
987 
988  glBindTexture(GL_TEXTURE_2D, 0);
989  glBindVertexArray(0);
990  glBindBuffer(GL_ARRAY_BUFFER, 0);
991 
992  glDeleteVertexArrays(1, &vertex_array_object);
993 
994  glDisable(GL_BLEND);
995 
996  gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
997  glFlush();
998 
999  VLOG_DEVICE_STATS << "Display driver number of textures: " << GLTexture::num_used;
1000  VLOG_DEVICE_STATS << "Display driver number of PBOs: " << GLPixelBufferObject::num_used;
1001 
1002  if (use_gl_context_) {
1003  gl_context_mutex_.unlock();
1004  }
1005 }
1006 
1008 {
1009  /* When rendering in viewport there is no render context available via engine.
1010  * Check whether own context is to be created here.
1011  *
1012  * NOTE: If the `b_engine_`'s context is not available, we are expected to be on a main thread
1013  * here. */
1015  reinterpret_cast<RenderEngine *>(b_engine_.ptr.data));
1016 
1017  if (use_gl_context_) {
1018  const bool drw_state = DRW_opengl_context_release();
1020  if (gl_context_) {
1021  /* On Windows an old context is restored after creation, and subsequent release of context
1022  * generates a Win32 error. Harmless for users, but annoying to have possible misleading
1023  * error prints in the console. */
1024 #ifndef _WIN32
1026 #endif
1027  }
1028  else {
1029  LOG(ERROR) << "Error creating OpenGL context.";
1030  }
1031 
1032  DRW_opengl_context_activate(drw_state);
1033  }
1034 }
1035 
1037 {
1038  if (use_gl_context_) {
1039  if (!gl_context_) {
1040  return false;
1041  }
1042  gl_context_mutex_.lock();
1044  return true;
1045  }
1046 
1047  RE_engine_render_context_enable(reinterpret_cast<RenderEngine *>(b_engine_.ptr.data));
1048  return true;
1049 }
1050 
1052 {
1053  if (use_gl_context_) {
1054  if (gl_context_) {
1056  gl_context_mutex_.unlock();
1057  }
1058  return;
1059  }
1060 
1061  RE_engine_render_context_disable(reinterpret_cast<RenderEngine *>(b_engine_.ptr.data));
1062 }
1063 
1065 {
1066  if (gl_context_) {
1067  const bool drw_state = DRW_opengl_context_release();
1068 
1071 
1072  DRW_opengl_context_activate(drw_state);
1073  }
1074 }
1075 
1077 {
1079 
1080  tiles_->current_tile.gl_resources_destroy();
1081  tiles_->finished_tiles.gl_resources_destroy_and_clear();
1082  tiles_->gl_resources_destroy();
1083 
1085 
1087 }
1088 
unsigned int uint
Definition: BLI_sys_types.h:67
struct Scene Scene
#define glEnable
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
#define glDisable
@ GPU_BACKEND_ANY
Definition: GPU_platform.h:19
@ GPU_DRIVER_ANY
Definition: GPU_platform.h:47
bool GPU_type_matches_ex(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver, eGPUBackendType backend)
@ GPU_OS_MAC
Definition: GPU_platform.h:38
@ GPU_DEVICE_NVIDIA
Definition: GPU_platform.h:24
struct RenderEngine RenderEngine
static void vertex_buffer_update(const DisplayDriver::Params &params)
static int compile_fallback_shader(void)
bool DRW_opengl_context_release()
void WM_opengl_context_dispose(void *gl_context)
Definition: wm_window.c:2374
static void update_tile_texture_pixels(const DrawTileAndPBO &tile)
void RE_engine_render_context_enable(struct RenderEngine *engine)
Definition: engine.c:1265
void WM_opengl_context_activate(void *gl_context)
Definition: wm_window.c:2380
void RE_engine_render_context_disable(struct RenderEngine *engine)
Definition: engine.c:1270
static void draw_tile(const float2 &zoom, const int texcoord_attribute, const int position_attribute, const DrawTile &draw_tile, const uint gl_vertex_buffer)
static const char * FALLBACK_VERTEX_SHADER
void WM_opengl_context_release(void *context)
Definition: wm_window.c:2386
void DRW_opengl_context_activate(bool drw_state)
bool RE_engine_has_render_context(struct RenderEngine *engine)
Definition: engine.c:1256
static const char * FALLBACK_FRAGMENT_SHADER
static void shader_print_errors(const char *task, const char *log, const char *code)
void * WM_opengl_context_create()
Definition: wm_window.c:2353
BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene)
virtual half4 * map_texture_buffer() override
virtual void unmap_texture_buffer() override
virtual void clear() override
virtual bool update_begin(const Params &params, int texture_width, int texture_height) override
std::atomic< bool > need_clear_
virtual void graphics_interop_activate() override
virtual GraphicsInterop graphics_interop_get() override
virtual void draw(const Params &params) override
void set_zoom(float zoom_x, float zoom_y)
virtual void flush() override
virtual void update_end() override
unique_ptr< Tiles > tiles_
unique_ptr< BlenderDisplayShader > display_shader_
virtual void graphics_interop_deactivate() override
virtual void next_tile_begin() override
virtual int get_position_attrib_location()
static unique_ptr< BlenderDisplayShader > create(BL::RenderEngine &b_engine, BL::Scene &b_scene)
static constexpr const char * tex_coord_attribute_name
static constexpr const char * position_attribute_name
virtual uint get_shader_program()=0
virtual int get_tex_coord_attrib_location()
virtual void unbind() override
virtual void bind(int width, int height) override
BlenderDisplaySpaceShader(BL::RenderEngine &b_engine, BL::Scene &b_scene)
virtual uint get_shader_program() override
virtual void bind(int width, int height) override
virtual uint get_shader_program() override
GLPixelBufferObject buffer_object
DrawTile(const DrawTile &other)=delete
~DrawTile()=default
DrawTile & operator=(const DrawTile &other)=delete
DrawTile(DrawTile &&other) noexcept=default
DrawTile & operator=(DrawTile &&other)=default
bool ready_to_draw() const
DrawTile()=default
BlenderDisplayDriver::Params params
GLPixelBufferObject & operator=(GLPixelBufferObject &other)=delete
GLPixelBufferObject(GLPixelBufferObject &&other) noexcept
GLPixelBufferObject(const GLPixelBufferObject &other)=delete
GLPixelBufferObject()=default
static std::atomic< int > num_used
GLPixelBufferObject & operator=(GLPixelBufferObject &&other)
GLTexture & operator=(GLTexture &&other)
GLTexture(const GLTexture &other)=delete
GLTexture & operator=(GLTexture &other)=delete
static std::atomic< int > num_used
GLTexture(GLTexture &&other) noexcept
GLTexture()=default
#define CCL_NAMESPACE_END
Definition: cuda/compat.h:9
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_global const KernelWorkTile * tile
#define DCHECK(expression)
Definition: log.h:55
#define VLOG_DEVICE_STATS
Definition: log.h:83
#define LOG(severity)
Definition: log.h:36
#define DCHECK_NE(a, b)
Definition: log.h:63
ccl_device_inline float3 log(float3 v)
Definition: math_float3.h:397
#define make_float2(x, y)
Definition: metal/compat.h:203
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value)
Allocate a new IDProperty of type IDP_INT, set its name and value.
struct blender::compositor::@179::@181 task
T length(const vec_base< T, Size > &a)
struct BlenderDisplayDriver::Tiles::@1227 finished_tiles
const NodeType * type
Definition: graph/node.h:175
float x
Definition: types_float2.h:15
float y
Definition: types_float2.h:15
Definition: half.h:64