diff --git a/intern/cycles/blender/display_driver.cpp b/intern/cycles/blender/display_driver.cpp index e2be4f85a9b..2469046eccf 100644 --- a/intern/cycles/blender/display_driver.cpp +++ b/intern/cycles/blender/display_driver.cpp @@ -5,9 +5,14 @@ #include "device/device.h" #include "util/log.h" +#include "util/math.h" #include "util/opengl.h" -#include "GPU_platform.h" +#include "GPU_context.h" +#include "GPU_immediate.h" +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" #include "RE_engine.h" @@ -30,8 +35,9 @@ unique_ptr BlenderDisplayShader::create(BL::RenderEngine & int BlenderDisplayShader::get_position_attrib_location() { if (position_attribute_location_ == -1) { - const uint shader_program = get_shader_program(); - position_attribute_location_ = glGetAttribLocation(shader_program, position_attribute_name); + GPUShader *shader_program = get_shader_program(); + position_attribute_location_ = GPU_shader_get_attribute(shader_program, + position_attribute_name); } return position_attribute_location_; } @@ -39,8 +45,9 @@ int BlenderDisplayShader::get_position_attrib_location() int BlenderDisplayShader::get_tex_coord_attrib_location() { if (tex_coord_attribute_location_ == -1) { - const uint shader_program = get_shader_program(); - tex_coord_attribute_location_ = glGetAttribLocation(shader_program, tex_coord_attribute_name); + GPUShader *shader_program = get_shader_program(); + tex_coord_attribute_location_ = GPU_shader_get_attribute(shader_program, + tex_coord_attribute_name); } return tex_coord_attribute_location_; } @@ -79,100 +86,42 @@ static const char *FALLBACK_FRAGMENT_SHADER = " fragColor = texture(image_texture, texCoord_interp);\n" "}\n\0"; -static void shader_print_errors(const char *task, const char *log, const char *code) +static GPUShader *compile_fallback_shader(void) { - LOG(ERROR) << "Shader: " << task << " error:"; - LOG(ERROR) << "===== shader string ===="; - - stringstream stream(code); - string partial; - - int line = 1; - while (getline(stream, partial, '\n')) { - if (line < 10) { - LOG(ERROR) << " " << line << " " << partial; - } - else { - LOG(ERROR) << line << " " << partial; - } - line++; - } - LOG(ERROR) << log; + /* NOTE: Compilation errors are logged to console. */ + GPUShader *shader = GPU_shader_create(FALLBACK_VERTEX_SHADER, + FALLBACK_FRAGMENT_SHADER, + nullptr, + nullptr, + nullptr, + "FallbackCyclesBlitShader"); + return shader; } -static int compile_fallback_shader(void) -{ - const struct Shader { - const char *source; - const GLenum type; - } shaders[2] = {{FALLBACK_VERTEX_SHADER, GL_VERTEX_SHADER}, - {FALLBACK_FRAGMENT_SHADER, GL_FRAGMENT_SHADER}}; - - const GLuint program = glCreateProgram(); - - for (int i = 0; i < 2; i++) { - const GLuint shader = glCreateShader(shaders[i].type); - - string source_str = shaders[i].source; - const char *c_str = source_str.c_str(); - - glShaderSource(shader, 1, &c_str, NULL); - glCompileShader(shader); - - GLint compile_status; - glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); - - if (!compile_status) { - GLchar log[5000]; - GLsizei length = 0; - glGetShaderInfoLog(shader, sizeof(log), &length, log); - shader_print_errors("compile", log, c_str); - return 0; - } - - glAttachShader(program, shader); - } - - /* Link output. */ - glBindFragDataLocation(program, 0, "fragColor"); - - /* Link and error check. */ - glLinkProgram(program); - - /* TODO(sergey): Find a way to nicely de-duplicate the error checking. */ - GLint link_status; - glGetProgramiv(program, GL_LINK_STATUS, &link_status); - if (!link_status) { - GLchar log[5000]; - GLsizei length = 0; - /* TODO(sergey): Is it really program passed to glGetShaderInfoLog? */ - glGetShaderInfoLog(program, sizeof(log), &length, log); - shader_print_errors("linking", log, FALLBACK_VERTEX_SHADER); - shader_print_errors("linking", log, FALLBACK_FRAGMENT_SHADER); - return 0; - } - - return program; -} - -void BlenderFallbackDisplayShader::bind(int width, int height) +GPUShader *BlenderFallbackDisplayShader::bind(int width, int height) { create_shader_if_needed(); if (!shader_program_) { - return; + return nullptr; } - glUseProgram(shader_program_); - glUniform1i(image_texture_location_, 0); - glUniform2f(fullscreen_location_, width, height); + /* Bind shader now to enable uniform assignment. */ + GPU_shader_bind(shader_program_); + GPU_shader_uniform_int(shader_program_, image_texture_location_, 0); + float size[2]; + size[0] = width; + size[1] = height; + GPU_shader_uniform_vector(shader_program_, fullscreen_location_, 2, 1, size); + return shader_program_; } void BlenderFallbackDisplayShader::unbind() { + GPU_shader_unbind(); } -uint BlenderFallbackDisplayShader::get_shader_program() +GPUShader *BlenderFallbackDisplayShader::get_shader_program() { return shader_program_; } @@ -187,19 +136,18 @@ void BlenderFallbackDisplayShader::create_shader_if_needed() shader_program_ = compile_fallback_shader(); if (!shader_program_) { + LOG(ERROR) << "Failed to compile fallback shader"; return; } - glUseProgram(shader_program_); - - image_texture_location_ = glGetUniformLocation(shader_program_, "image_texture"); + image_texture_location_ = GPU_shader_get_uniform(shader_program_, "image_texture"); if (image_texture_location_ < 0) { LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform."; destroy_shader(); return; } - fullscreen_location_ = glGetUniformLocation(shader_program_, "fullscreen"); + fullscreen_location_ = GPU_shader_get_uniform(shader_program_, "fullscreen"); if (fullscreen_location_ < 0) { LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform."; destroy_shader(); @@ -209,8 +157,10 @@ void BlenderFallbackDisplayShader::create_shader_if_needed() void BlenderFallbackDisplayShader::destroy_shader() { - glDeleteProgram(shader_program_); - shader_program_ = 0; + if (shader_program_) { + GPU_shader_free(shader_program_); + shader_program_ = nullptr; + } } /* -------------------------------------------------------------------- @@ -224,9 +174,10 @@ BlenderDisplaySpaceShader::BlenderDisplaySpaceShader(BL::RenderEngine &b_engine, DCHECK(b_engine_.support_display_space_shader(b_scene_)); } -void BlenderDisplaySpaceShader::bind(int /*width*/, int /*height*/) +GPUShader *BlenderDisplaySpaceShader::bind(int /*width*/, int /*height*/) { b_engine_.bind_display_space_shader(b_scene_); + return GPU_shader_get_bound(); } void BlenderDisplaySpaceShader::unbind() @@ -234,12 +185,11 @@ void BlenderDisplaySpaceShader::unbind() b_engine_.unbind_display_space_shader(); } -uint BlenderDisplaySpaceShader::get_shader_program() +GPUShader *BlenderDisplaySpaceShader::get_shader_program() { if (!shader_program_) { - glGetIntegerv(GL_CURRENT_PROGRAM, reinterpret_cast(&shader_program_)); + shader_program_ = GPU_shader_get_bound(); } - if (!shader_program_) { LOG(ERROR) << "Error retrieving shader program for display space shader."; } @@ -252,34 +202,34 @@ uint BlenderDisplaySpaceShader::get_shader_program() */ /* Higher level representation of a texture from the graphics library. */ -class GLTexture { +class DisplayGPUTexture { public: - /* Global counter for all allocated OpenGL textures used by instances of this class. */ + /* Global counter for all allocated GPUTextures used by instances of this class. */ static inline std::atomic num_used = 0; - GLTexture() = default; + DisplayGPUTexture() = default; - ~GLTexture() + ~DisplayGPUTexture() { - assert(gl_id == 0); + assert(gpu_texture == nullptr); } - GLTexture(const GLTexture &other) = delete; - GLTexture &operator=(GLTexture &other) = delete; + DisplayGPUTexture(const DisplayGPUTexture &other) = delete; + DisplayGPUTexture &operator=(DisplayGPUTexture &other) = delete; - GLTexture(GLTexture &&other) noexcept - : gl_id(other.gl_id), width(other.width), height(other.height) + DisplayGPUTexture(DisplayGPUTexture &&other) noexcept + : gpu_texture(other.gpu_texture), width(other.width), height(other.height) { other.reset(); } - GLTexture &operator=(GLTexture &&other) + DisplayGPUTexture &operator=(DisplayGPUTexture &&other) { if (this == &other) { return *this; } - gl_id = other.gl_id; + gpu_texture = other.gpu_texture; width = other.width; height = other.height; @@ -288,55 +238,56 @@ class GLTexture { return *this; } - bool gl_resources_ensure() + bool gpu_resources_ensure() { - if (gl_id) { + if (gpu_texture) { return true; } - /* Create texture. */ - glGenTextures(1, &gl_id); - if (!gl_id) { + /* Texture must have a minimum size of 1x1. */ + gpu_texture = GPU_texture_create_2d( + "CyclesBlitTexture", max(width, 1), max(height, 1), 1, GPU_RGBA16F, nullptr); + + if (!gpu_texture) { LOG(ERROR) << "Error creating texture."; return false; } - /* Configure the texture. */ - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, gl_id); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - /* Clamp to edge so that precision issues when zoomed out (which forces linear interpolation) - * does not cause unwanted repetition. */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glBindTexture(GL_TEXTURE_2D, 0); + GPU_texture_filter_mode(gpu_texture, false); + GPU_texture_wrap_mode(gpu_texture, false, true); ++num_used; return true; } - void gl_resources_destroy() + void gpu_resources_destroy() { - if (!gl_id) { + if (gpu_texture == nullptr) { return; } - glDeleteTextures(1, &gl_id); + GPU_TEXTURE_FREE_SAFE(gpu_texture); reset(); --num_used; } - /* OpenGL resource IDs of the texture. + void ensure_size(uint texture_width, uint texture_height) + { + if (width != texture_width || height != texture_height) { + gpu_resources_destroy(); + width = texture_width; + height = texture_height; + gpu_resources_ensure(); + } + } + + /* Texture resource allocated by the GPU module. * * NOTE: Allocated on the render engine's context. */ - uint gl_id = 0; + GPUTexture *gpu_texture = nullptr; /* Dimensions of the texture in pixels. */ int width = 0; @@ -345,41 +296,41 @@ class GLTexture { protected: void reset() { - gl_id = 0; + gpu_texture = nullptr; width = 0; height = 0; } }; /* Higher level representation of a Pixel Buffer Object (PBO) from the graphics library. */ -class GLPixelBufferObject { +class DisplayGPUPixelBuffer { public: - /* Global counter for all allocated OpenGL PBOs used by instances of this class. */ + /* Global counter for all allocated GPU module PBOs used by instances of this class. */ static inline std::atomic num_used = 0; - GLPixelBufferObject() = default; + DisplayGPUPixelBuffer() = default; - ~GLPixelBufferObject() + ~DisplayGPUPixelBuffer() { - assert(gl_id == 0); + assert(gpu_pixel_buffer == nullptr); } - GLPixelBufferObject(const GLPixelBufferObject &other) = delete; - GLPixelBufferObject &operator=(GLPixelBufferObject &other) = delete; + DisplayGPUPixelBuffer(const DisplayGPUPixelBuffer &other) = delete; + DisplayGPUPixelBuffer &operator=(DisplayGPUPixelBuffer &other) = delete; - GLPixelBufferObject(GLPixelBufferObject &&other) noexcept - : gl_id(other.gl_id), width(other.width), height(other.height) + DisplayGPUPixelBuffer(DisplayGPUPixelBuffer &&other) noexcept + : gpu_pixel_buffer(other.gpu_pixel_buffer), width(other.width), height(other.height) { other.reset(); } - GLPixelBufferObject &operator=(GLPixelBufferObject &&other) + DisplayGPUPixelBuffer &operator=(DisplayGPUPixelBuffer &&other) { if (this == &other) { return *this; } - gl_id = other.gl_id; + gpu_pixel_buffer = other.gpu_pixel_buffer; width = other.width; height = other.height; @@ -388,14 +339,34 @@ class GLPixelBufferObject { return *this; } - bool gl_resources_ensure() + void ensure_size(uint new_width, uint new_height) { - if (gl_id) { - return true; + size_t required_size = sizeof(half4) * new_width * new_height * 4; + + if (gpu_pixel_buffer) { + if (new_width != width || new_height != height || + GPU_pixel_buffer_size(gpu_pixel_buffer) < required_size) { + GPU_pixel_buffer_free(gpu_pixel_buffer); + gpu_pixel_buffer = nullptr; + } } - glGenBuffers(1, &gl_id); - if (!gl_id) { + /* Update size. */ + width = new_width; + height = new_height; + + /* Create pixel buffer if not already created. */ + if (!gpu_pixel_buffer) { + gpu_pixel_buffer = GPU_pixel_buffer_create(required_size); + } + } + + bool gpu_resources_ensure() + { + /* Create pixel buffer using current size. */ + ensure_size(width, height); + + if (gpu_pixel_buffer == nullptr) { LOG(ERROR) << "Error creating texture pixel buffer object."; return false; } @@ -405,23 +376,24 @@ class GLPixelBufferObject { return true; } - void gl_resources_destroy() + void gpu_resources_destroy() { - if (!gl_id) { + if (!gpu_pixel_buffer) { return; } - glDeleteBuffers(1, &gl_id); + GPU_pixel_buffer_free(gpu_pixel_buffer); + gpu_pixel_buffer = nullptr; reset(); --num_used; } - /* OpenGL resource IDs of the PBO. + /* Pixel Buffer Object allocated by the GPU module. * * NOTE: Allocated on the render engine's context. */ - uint gl_id = 0; + GPUPixelBuffer *gpu_pixel_buffer = nullptr; /* Dimensions of the PBO. */ int width = 0; @@ -430,7 +402,7 @@ class GLPixelBufferObject { protected: void reset() { - gl_id = 0; + gpu_pixel_buffer = 0; width = 0; height = 0; } @@ -448,28 +420,28 @@ class DrawTile { DrawTile &operator=(DrawTile &&other) = default; - bool gl_resources_ensure() + bool gpu_resources_ensure() { - if (!texture.gl_resources_ensure()) { - gl_resources_destroy(); + if (!texture.gpu_resources_ensure()) { + gpu_resources_destroy(); return false; } return true; } - void gl_resources_destroy() + void gpu_resources_destroy() { - texture.gl_resources_destroy(); + texture.gpu_resources_destroy(); } inline bool ready_to_draw() const { - return texture.gl_id != 0; + return texture.gpu_texture != 0; } /* Texture which contains pixels of the tile. */ - GLTexture texture; + DisplayGPUTexture texture; /* Display parameters the texture of this tile has been updated for. */ BlenderDisplayDriver::Params params; @@ -477,24 +449,24 @@ class DrawTile { class DrawTileAndPBO { public: - bool gl_resources_ensure() + bool gpu_resources_ensure() { - if (!tile.gl_resources_ensure() || !buffer_object.gl_resources_ensure()) { - gl_resources_destroy(); + if (!tile.gpu_resources_ensure() || !buffer_object.gpu_resources_ensure()) { + gpu_resources_destroy(); return false; } return true; } - void gl_resources_destroy() + void gpu_resources_destroy() { - tile.gl_resources_destroy(); - buffer_object.gl_resources_destroy(); + tile.gpu_resources_destroy(); + buffer_object.gpu_resources_destroy(); } DrawTile tile; - GLPixelBufferObject buffer_object; + DisplayGPUPixelBuffer buffer_object; bool need_update_texture_pixels = false; }; @@ -513,36 +485,12 @@ struct BlenderDisplayDriver::Tiles { void gl_resources_destroy_and_clear() { for (DrawTile &tile : tiles) { - tile.gl_resources_destroy(); + tile.gpu_resources_destroy(); } tiles.clear(); } } finished_tiles; - - /* OpenGL vertex buffer needed for drawing. */ - uint gl_vertex_buffer = 0; - - bool gl_resources_ensure() - { - if (!gl_vertex_buffer) { - glGenBuffers(1, &gl_vertex_buffer); - if (!gl_vertex_buffer) { - LOG(ERROR) << "Error allocating tile VBO."; - return false; - } - } - - return true; - } - - void gl_resources_destroy() - { - if (gl_vertex_buffer) { - glDeleteBuffers(1, &gl_vertex_buffer); - gl_vertex_buffer = 0; - } - } }; BlenderDisplayDriver::BlenderDisplayDriver(BL::RenderEngine &b_engine, @@ -590,7 +538,7 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms, /* Note that it's the responsibility of BlenderDisplayDriver to ensure updating and drawing * the texture does not happen at the same time. This is achieved indirectly. * - * When enabling the OpenGL context, it uses an internal mutex lock DST.gpu_context_lock. + * When enabling the OpenGL/GPU context, it uses an internal mutex lock DST.gpu_context_lock. * This same lock is also held when do_draw() is called, which together ensure mutual * exclusion. * @@ -599,12 +547,10 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms, return false; } - if (gl_render_sync_) { - glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED); - } + GPU_fence_wait(gpu_render_sync_); DrawTile ¤t_tile = tiles_->current_tile.tile; - GLPixelBufferObject ¤t_tile_buffer_object = tiles_->current_tile.buffer_object; + DisplayGPUPixelBuffer ¤t_tile_buffer_object = tiles_->current_tile.buffer_object; /* Clear storage of all finished tiles when display clear is requested. * Do it when new tile data is provided to handle the display clear flag in a single place. @@ -614,29 +560,14 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms, need_clear_ = false; } - if (!tiles_->gl_resources_ensure()) { - tiles_->gl_resources_destroy(); - gpu_context_disable(); - return false; - } - - if (!tiles_->current_tile.gl_resources_ensure()) { - tiles_->current_tile.gl_resources_destroy(); + if (!tiles_->current_tile.gpu_resources_ensure()) { + tiles_->current_tile.gpu_resources_destroy(); gpu_context_disable(); return false; } /* Update texture dimensions if needed. */ - if (current_tile.texture.width != texture_width || - current_tile.texture.height != texture_height) { - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, current_tile.texture.gl_id); - glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA16F, texture_width, texture_height, 0, GL_RGBA, GL_HALF_FLOAT, 0); - current_tile.texture.width = texture_width; - current_tile.texture.height = texture_height; - glBindTexture(GL_TEXTURE_2D, 0); - } + current_tile.texture.ensure_size(texture_width, texture_height); /* Update PBO dimensions if needed. * @@ -649,16 +580,7 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms, * mode faster. */ const int buffer_width = params.size.x; const int buffer_height = params.size.y; - if (current_tile_buffer_object.width != buffer_width || - current_tile_buffer_object.height != buffer_height) { - const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height; - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, current_tile_buffer_object.gl_id); - glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, 0, GL_DYNAMIC_DRAW); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - - current_tile_buffer_object.width = buffer_width; - current_tile_buffer_object.height = buffer_height; - } + current_tile_buffer_object.ensure_size(buffer_width, buffer_height); /* Store an updated parameters of the current tile. * In theory it is only needed once per update of the tile, but doing it on every update is @@ -670,19 +592,21 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms, static void update_tile_texture_pixels(const DrawTileAndPBO &tile) { - const GLTexture &texture = tile.tile.texture; + const DisplayGPUTexture &texture = tile.tile.texture; - DCHECK_NE(tile.buffer_object.gl_id, 0); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture.gl_id); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, tile.buffer_object.gl_id); - - glTexSubImage2D( - GL_TEXTURE_2D, 0, 0, 0, texture.width, texture.height, GL_RGBA, GL_HALF_FLOAT, 0); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); + if (!DCHECK_NOTNULL(tile.buffer_object.gpu_pixel_buffer)) { + LOG(ERROR) << "Display driver tile pixel buffer unavailable."; + return; + } + GPU_texture_update_sub_from_pixel_buffer(texture.gpu_texture, + GPU_DATA_HALF_FLOAT, + tile.buffer_object.gpu_pixel_buffer, + 0, + 0, + 0, + texture.width, + texture.height, + 0); } void BlenderDisplayDriver::update_end() @@ -709,8 +633,10 @@ void BlenderDisplayDriver::update_end() update_tile_texture_pixels(tiles_->current_tile); } - gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glFlush(); + /* Ensure GPU fence exists to synchronize upload. */ + GPU_fence_signal(gpu_upload_sync_); + + GPU_flush(); gpu_context_disable(); } @@ -721,26 +647,26 @@ void BlenderDisplayDriver::update_end() half4 *BlenderDisplayDriver::map_texture_buffer() { - const uint pbo_gl_id = tiles_->current_tile.buffer_object.gl_id; - - DCHECK_NE(pbo_gl_id, 0); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_gl_id); - - half4 *mapped_rgba_pixels = reinterpret_cast( - glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); + GPUPixelBuffer *pix_buf = tiles_->current_tile.buffer_object.gpu_pixel_buffer; + if (!DCHECK_NOTNULL(pix_buf)) { + LOG(ERROR) << "Display driver tile pixel buffer unavailable."; + return nullptr; + } + half4 *mapped_rgba_pixels = reinterpret_cast(GPU_pixel_buffer_map(pix_buf)); if (!mapped_rgba_pixels) { LOG(ERROR) << "Error mapping BlenderDisplayDriver pixel buffer object."; } - return mapped_rgba_pixels; } void BlenderDisplayDriver::unmap_texture_buffer() { - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + GPUPixelBuffer *pix_buf = tiles_->current_tile.buffer_object.gpu_pixel_buffer; + if (!DCHECK_NOTNULL(pix_buf)) { + LOG(ERROR) << "Display driver tile pixel buffer unavailable."; + return; + } + GPU_pixel_buffer_unmap(pix_buf); } /* -------------------------------------------------------------------- @@ -753,7 +679,8 @@ BlenderDisplayDriver::GraphicsInterop BlenderDisplayDriver::graphics_interop_get interop_dst.buffer_width = tiles_->current_tile.buffer_object.width; interop_dst.buffer_height = tiles_->current_tile.buffer_object.height; - interop_dst.opengl_pbo_id = tiles_->current_tile.buffer_object.gl_id; + interop_dst.opengl_pbo_id = (int)GPU_pixel_buffer_get_native_handle( + tiles_->current_tile.buffer_object.gpu_pixel_buffer); return interop_dst; } @@ -786,7 +713,9 @@ void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y) * This buffer is used to render texture in the viewport. * * NOTE: The buffer needs to be bound. */ -static void vertex_buffer_update(const DisplayDriver::Params ¶ms) +static void vertex_draw(const DisplayDriver::Params ¶ms, + int texcoord_attribute, + int position_attribute) { const int x = params.full_offset.x; const int y = params.full_offset.y; @@ -794,67 +723,40 @@ static void vertex_buffer_update(const DisplayDriver::Params ¶ms) const int width = params.size.x; const int height = params.size.y; - /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be - * rendered. */ - glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW); + immBegin(GPU_PRIM_TRI_STRIP, 4); - float *vpointer = reinterpret_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); - if (!vpointer) { - return; - } + immAttr2f(texcoord_attribute, 1.0f, 0.0f); + immVertex2f(position_attribute, x + width, y); - vpointer[0] = 0.0f; - vpointer[1] = 0.0f; - vpointer[2] = x; - vpointer[3] = y; + immAttr2f(texcoord_attribute, 1.0f, 1.0f); + immVertex2f(position_attribute, x + width, y + height); - vpointer[4] = 1.0f; - vpointer[5] = 0.0f; - vpointer[6] = x + width; - vpointer[7] = y; + immAttr2f(texcoord_attribute, 0.0f, 0.0f); + immVertex2f(position_attribute, x, y); - vpointer[8] = 1.0f; - vpointer[9] = 1.0f; - vpointer[10] = x + width; - vpointer[11] = y + height; + immAttr2f(texcoord_attribute, 0.0f, 1.0f); + immVertex2f(position_attribute, x, y + height); - vpointer[12] = 0.0f; - vpointer[13] = 1.0f; - vpointer[14] = x; - vpointer[15] = y + height; - - glUnmapBuffer(GL_ARRAY_BUFFER); + immEnd(); } static void draw_tile(const float2 &zoom, const int texcoord_attribute, const int position_attribute, - const DrawTile &draw_tile, - const uint gl_vertex_buffer) + const DrawTile &draw_tile) { if (!draw_tile.ready_to_draw()) { return; } - const GLTexture &texture = draw_tile.texture; + const DisplayGPUTexture &texture = draw_tile.texture; - DCHECK_NE(texture.gl_id, 0); - DCHECK_NE(gl_vertex_buffer, 0); + if (!DCHECK_NOTNULL(texture.gpu_texture)) { + LOG(ERROR) << "Display driver tile GPU texture resource unavailable."; + return; + } - glBindBuffer(GL_ARRAY_BUFFER, gl_vertex_buffer); - - /* Draw at the parameters for which the texture has been updated for. This allows to always draw - * texture during bordered-rendered camera view without flickering. The validness of the display - * parameters for a texture is guaranteed by the initial "clear" state which makes drawing to - * have an early output. - * - * Such approach can cause some extra "jelly" effect during panning, but it is not more jelly - * than overlay of selected objects. Also, it's possible to redraw texture at an intersection of - * the texture draw parameters and the latest updated draw parameters (although, complexity of - * doing it might not worth it. */ - vertex_buffer_update(draw_tile.params); - - glBindTexture(GL_TEXTURE_2D, texture.gl_id); + GPU_texture_bind(texture.gpu_texture, 0); /* Trick to keep sharp rendering without jagged edges on all GPUs. * @@ -868,26 +770,26 @@ static void draw_tile(const float2 &zoom, const float zoomed_height = draw_tile.params.size.y * zoom.y; if (texture.width != draw_tile.params.size.x || texture.height != draw_tile.params.size.y) { /* Resolution divider is different from 1, force nearest interpolation. */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + GPU_texture_filter_mode(texture.gpu_texture, false); } else if (zoomed_width - draw_tile.params.size.x > 0.5f || zoomed_height - draw_tile.params.size.y > 0.5f) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + GPU_texture_filter_mode(texture.gpu_texture, false); } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + GPU_texture_filter_mode(texture.gpu_texture, true); } - glVertexAttribPointer( - texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0); - glVertexAttribPointer(position_attribute, - 2, - GL_FLOAT, - GL_FALSE, - 4 * sizeof(float), - (const GLvoid *)(sizeof(float) * 2)); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + /* Draw at the parameters for which the texture has been updated for. This allows to always draw + * texture during bordered-rendered camera view without flickering. The validness of the display + * parameters for a texture is guaranteed by the initial "clear" state which makes drawing to + * have an early output. + * + * Such approach can cause some extra "jelly" effect during panning, but it is not more jelly + * than overlay of selected objects. Also, it's possible to redraw texture at an intersection of + * the texture draw parameters and the latest updated draw parameters (although, complexity of + * doing it might not worth it. */ + vertex_draw(draw_tile.params, texcoord_attribute, position_attribute); } void BlenderDisplayDriver::flush() @@ -903,13 +805,8 @@ void BlenderDisplayDriver::flush() return; } - if (gl_upload_sync_) { - glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); - } - - if (gl_render_sync_) { - glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED); - } + GPU_fence_wait(gpu_upload_sync_); + GPU_fence_wait(gpu_render_sync_); gpu_context_disable(); } @@ -928,68 +825,56 @@ void BlenderDisplayDriver::draw(const Params ¶ms) return; } - if (gl_upload_sync_) { - glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); - } + GPU_fence_wait(gpu_upload_sync_); + GPU_blend(GPU_BLEND_ALPHA_PREMULT); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + GPUShader *active_shader = display_shader_->bind(params.full_size.x, params.full_size.y); - glActiveTexture(GL_TEXTURE0); + GPUVertFormat *format = immVertexFormat(); + const int texcoord_attribute = GPU_vertformat_attr_add( + format, display_shader_->tex_coord_attribute_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const int position_attribute = GPU_vertformat_attr_add( + format, display_shader_->position_attribute_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - /* NOTE: The VAO is to be allocated on the drawing context as it is not shared across contexts. - * Simplest is to allocate it on every redraw so that it is possible to destroy it from a - * correct context. */ - GLuint vertex_array_object; - glGenVertexArrays(1, &vertex_array_object); - glBindVertexArray(vertex_array_object); + /* Note: Shader is bound again through IMM to register this shader with the imm module + * and perform required setup for IMM rendering. This is required as the IMM module + * needs to be aware of which shader is bound, and the main display shader + * is bound externally. */ + immBindShader(active_shader); - display_shader_->bind(params.full_size.x, params.full_size.y); - - const int texcoord_attribute = display_shader_->get_tex_coord_attrib_location(); - const int position_attribute = display_shader_->get_position_attrib_location(); - - glEnableVertexAttribArray(texcoord_attribute); - glEnableVertexAttribArray(position_attribute); - - if (tiles_->current_tile.need_update_texture_pixels) { - update_tile_texture_pixels(tiles_->current_tile); - tiles_->current_tile.need_update_texture_pixels = false; - } - - draw_tile(zoom_, - texcoord_attribute, - position_attribute, - tiles_->current_tile.tile, - tiles_->gl_vertex_buffer); + draw_tile(zoom_, texcoord_attribute, position_attribute, tiles_->current_tile.tile); for (const DrawTile &tile : tiles_->finished_tiles.tiles) { - draw_tile(zoom_, texcoord_attribute, position_attribute, tile, tiles_->gl_vertex_buffer); + draw_tile(zoom_, texcoord_attribute, position_attribute, tile); } + /* Reset IMM shader bind state. */ + immUnbindProgram(); + display_shader_->unbind(); - glBindTexture(GL_TEXTURE_2D, 0); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + GPU_blend(GPU_BLEND_NONE); - glDeleteVertexArrays(1, &vertex_array_object); - - glDisable(GL_BLEND); - - gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glFlush(); + GPU_fence_signal(gpu_render_sync_); + GPU_flush(); gpu_context_unlock(); - VLOG_DEVICE_STATS << "Display driver number of textures: " << GLTexture::num_used; - VLOG_DEVICE_STATS << "Display driver number of PBOs: " << GLPixelBufferObject::num_used; + VLOG_DEVICE_STATS << "Display driver number of textures: " << DisplayGPUTexture::num_used; + VLOG_DEVICE_STATS << "Display driver number of PBOs: " << DisplayGPUPixelBuffer::num_used; } void BlenderDisplayDriver::gpu_context_create() { if (!RE_engine_gpu_context_create(reinterpret_cast(b_engine_.ptr.data))) { - LOG(ERROR) << "Error creating OpenGL context."; + LOG(ERROR) << "Error creating GPU context."; + return; + } + + /* Create global GPU resources for display driver. */ + if (!gpu_resources_create()) { + LOG(ERROR) << "Error creating GPU resources for Cycles Display Driver."; + return; } } @@ -1018,13 +903,43 @@ void BlenderDisplayDriver::gpu_context_unlock() RE_engine_gpu_context_unlock(reinterpret_cast(b_engine_.ptr.data)); } +bool BlenderDisplayDriver::gpu_resources_create() +{ + /* Ensure context is active for resource creation. */ + if (!gpu_context_enable()) { + LOG(ERROR) << "Error enabling GPU context."; + return false; + } + + gpu_upload_sync_ = GPU_fence_create(); + gpu_render_sync_ = GPU_fence_create(); + + if (!DCHECK_NOTNULL(gpu_upload_sync_) || !DCHECK_NOTNULL(gpu_render_sync_)) { + LOG(ERROR) << "Error creating GPU synchronization primtiives."; + assert(0); + return false; + } + + gpu_context_disable(); + return true; +} + void BlenderDisplayDriver::gpu_resources_destroy() { gpu_context_enable(); - tiles_->current_tile.gl_resources_destroy(); + tiles_->current_tile.gpu_resources_destroy(); tiles_->finished_tiles.gl_resources_destroy_and_clear(); - tiles_->gl_resources_destroy(); + + /* Fences. */ + if (gpu_render_sync_) { + GPU_fence_free(gpu_render_sync_); + gpu_render_sync_ = nullptr; + } + if (gpu_upload_sync_) { + GPU_fence_free(gpu_upload_sync_); + gpu_upload_sync_ = nullptr; + } gpu_context_disable(); diff --git a/intern/cycles/blender/display_driver.h b/intern/cycles/blender/display_driver.h index 4df40269daf..16a5ee681e8 100644 --- a/intern/cycles/blender/display_driver.h +++ b/intern/cycles/blender/display_driver.h @@ -15,6 +15,10 @@ #include "util/unique_ptr.h" #include "util/vector.h" +typedef struct GPUContext GPUContext; +typedef struct GPUFence GPUFence; +typedef struct GPUShader GPUShader; + CCL_NAMESPACE_BEGIN /* Base class of shader used for display driver rendering. */ @@ -29,7 +33,7 @@ class BlenderDisplayShader { BlenderDisplayShader() = default; virtual ~BlenderDisplayShader() = default; - virtual void bind(int width, int height) = 0; + virtual GPUShader *bind(int width, int height) = 0; virtual void unbind() = 0; /* Get attribute location for position and texture coordinate respectively. @@ -40,7 +44,7 @@ class BlenderDisplayShader { protected: /* Get program of this display shader. * NOTE: The shader needs to be bound to have access to this. */ - virtual uint get_shader_program() = 0; + virtual GPUShader *get_shader_program() = 0; /* Cached values of various OpenGL resources. */ int position_attribute_location_ = -1; @@ -51,16 +55,16 @@ class BlenderDisplayShader { * display space shader. */ class BlenderFallbackDisplayShader : public BlenderDisplayShader { public: - virtual void bind(int width, int height) override; + virtual GPUShader *bind(int width, int height) override; virtual void unbind() override; protected: - virtual uint get_shader_program() override; + virtual GPUShader *get_shader_program() override; void create_shader_if_needed(); void destroy_shader(); - uint shader_program_ = 0; + GPUShader *shader_program_ = 0; int image_texture_location_ = -1; int fullscreen_location_ = -1; @@ -73,17 +77,17 @@ class BlenderDisplaySpaceShader : public BlenderDisplayShader { public: BlenderDisplaySpaceShader(BL::RenderEngine &b_engine, BL::Scene &b_scene); - virtual void bind(int width, int height) override; + virtual GPUShader *bind(int width, int height) override; virtual void unbind() override; protected: - virtual uint get_shader_program() override; + virtual GPUShader *get_shader_program() override; BL::RenderEngine b_engine_; BL::Scene &b_scene_; /* Cached values of various OpenGL resources. */ - uint shader_program_ = 0; + GPUShader *shader_program_ = nullptr; }; /* Display driver implementation which is specific for Blender viewport integration. */ @@ -122,6 +126,9 @@ class BlenderDisplayDriver : public DisplayDriver { void gpu_context_lock(); void gpu_context_unlock(); + /* Create GPU resources used by the dispaly driver. */ + bool gpu_resources_create(); + /* Destroy all GPU resources which are being used by this object. */ void gpu_resources_destroy(); @@ -137,8 +144,8 @@ class BlenderDisplayDriver : public DisplayDriver { struct Tiles; unique_ptr tiles_; - void *gl_render_sync_ = nullptr; - void *gl_upload_sync_ = nullptr; + GPUFence *gpu_render_sync_ = nullptr; + GPUFence *gpu_upload_sync_ = nullptr; float2 zoom_ = make_float2(1.0f, 1.0f); }; diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 1148207fc57..5a6db911ee5 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -97,6 +97,7 @@ void GPU_shader_free(GPUShader *shader); void GPU_shader_bind(GPUShader *shader); void GPU_shader_unbind(void); +GPUShader *GPU_shader_get_bound(void); const char *GPU_shader_get_name(GPUShader *shader); diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index b595f076e20..cc3b39fb83d 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -8,6 +8,9 @@ #include "BLI_utildefines.h" +/** Opaque type hiding blender::gpu::Fence. */ +typedef struct GPUFence GPUFence; + typedef enum eGPUWriteMask { GPU_WRITE_NONE = 0, GPU_WRITE_RED = (1 << 0), @@ -196,6 +199,11 @@ bool GPU_bgl_get(void); void GPU_memory_barrier(eGPUBarrier barrier); +GPUFence *GPU_fence_create(void); +void GPU_fence_free(GPUFence *fence); +void GPU_fence_signal(GPUFence *fence); +void GPU_fence_wait(GPUFence *fence); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index e91d4703014..67507369e51 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -16,6 +16,9 @@ struct GPUVertBuf; /** Opaque type hiding blender::gpu::Texture. */ typedef struct GPUTexture GPUTexture; +/** Opaque type hiding blender::gpu::PixelBuffer. */ +typedef struct GPUPixelBuffer GPUPixelBuffer; + /** * GPU Samplers state * - Specify the sampler state to bind a texture with. @@ -284,6 +287,17 @@ void GPU_texture_update_sub(GPUTexture *tex, int width, int height, int depth); + +/* Update from API Buffer. */ +void GPU_texture_update_sub_from_pixel_buffer(GPUTexture *tex, + eGPUDataFormat data_format, + GPUPixelBuffer *pix_buf, + int offset_x, + int offset_y, + int offset_z, + int width, + int height, + int depth); /** * Makes data interpretation aware of the source layout. * Skipping pixels correctly when changing rows when doing partial update. @@ -366,6 +380,15 @@ void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size); size_t GPU_texture_component_len(eGPUTextureFormat format); size_t GPU_texture_dataformat_size(eGPUDataFormat data_format); +/* GPU Pixel Buffer. */ +GPUPixelBuffer *GPU_pixel_buffer_create(uint size); +void GPU_pixel_buffer_free(GPUPixelBuffer *pix_buf); + +void *GPU_pixel_buffer_map(GPUPixelBuffer *pix_buf); +void GPU_pixel_buffer_unmap(GPUPixelBuffer *pix_buf); +uint GPU_pixel_buffer_size(GPUPixelBuffer *pix_buf); +int64_t GPU_pixel_buffer_get_native_handle(GPUPixelBuffer *pix_buf); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 2a545c8114e..518dc1b1744 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -18,8 +18,10 @@ class Context; class Batch; class DrawList; +class Fence; class FrameBuffer; class IndexBuf; +class PixelBuffer; class QueryPool; class Shader; class Texture; @@ -42,8 +44,10 @@ class GPUBackend { virtual Batch *batch_alloc() = 0; virtual DrawList *drawlist_alloc(int list_length) = 0; + virtual Fence *fence_alloc() = 0; virtual FrameBuffer *framebuffer_alloc(const char *name) = 0; virtual IndexBuf *indexbuf_alloc() = 0; + virtual PixelBuffer *pixelbuf_alloc(uint size) = 0; virtual QueryPool *querypool_alloc() = 0; virtual Shader *shader_alloc(const char *name) = 0; virtual Texture *texture_alloc(const char *name) = 0; diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 4d059ae495e..d122982deed 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -527,6 +527,15 @@ void GPU_shader_unbind() #endif } +GPUShader *GPU_shader_get_bound() +{ + Context *ctx = Context::get(); + if (ctx) { + return wrap(ctx->shader); + } + return nullptr; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index 8e9d5cb3184..09a5b0ac6e9 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -16,6 +16,7 @@ #include "GPU_state.h" +#include "gpu_backend.hh" #include "gpu_context_private.hh" #include "gpu_state_private.hh" @@ -373,6 +374,27 @@ void GPU_memory_barrier(eGPUBarrier barrier) Context::get()->state_manager->issue_barrier(barrier); } +GPUFence *GPU_fence_create() +{ + Fence *fence = GPUBackend::get()->fence_alloc(); + return wrap(fence); +} + +void GPU_fence_free(GPUFence *fence) +{ + delete unwrap(fence); +} + +void GPU_fence_signal(GPUFence *fence) +{ + unwrap(fence)->signal(); +} + +void GPU_fence_wait(GPUFence *fence) +{ + unwrap(fence)->wait(); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_state_private.hh b/source/blender/gpu/intern/gpu_state_private.hh index d191d1b282f..964aa660a4d 100644 --- a/source/blender/gpu/intern/gpu_state_private.hh +++ b/source/blender/gpu/intern/gpu_state_private.hh @@ -160,5 +160,34 @@ class StateManager { virtual void texture_unpack_row_length_set(uint len) = 0; }; +/** + * GPUFence. + */ +class Fence { + protected: + bool signalled_ = false; + + public: + Fence(){}; + virtual ~Fence(){}; + + virtual void signal() = 0; + virtual void wait() = 0; +}; + +/* Syntactic sugar. */ +static inline GPUFence *wrap(Fence *pixbuf) +{ + return reinterpret_cast(pixbuf); +} +static inline Fence *unwrap(GPUFence *pixbuf) +{ + return reinterpret_cast(pixbuf); +} +static inline const Fence *unwrap(const GPUFence *pixbuf) +{ + return reinterpret_cast(pixbuf); +} + } // namespace gpu } // namespace blender diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 9867ca22c45..3666422401a 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -455,6 +455,21 @@ void GPU_texture_update_sub(GPUTexture *tex, reinterpret_cast(tex)->update_sub(0, offset, extent, data_format, pixels); } +void GPU_texture_update_sub_from_pixel_buffer(GPUTexture *tex, + eGPUDataFormat data_format, + GPUPixelBuffer *pix_buf, + int offset_x, + int offset_y, + int offset_z, + int width, + int height, + int depth) +{ + int offset[3] = {offset_x, offset_y, offset_z}; + int extent[3] = {width, height, depth}; + reinterpret_cast(tex)->update_sub(offset, extent, data_format, pix_buf); +} + void *GPU_texture_read(GPUTexture *tex_, eGPUDataFormat data_format, int miplvl) { Texture *tex = reinterpret_cast(tex_); @@ -824,6 +839,53 @@ void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *r_size) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name GPU Pixel Buffer + * + * Pixel buffer utility functions. + * \{ */ + +GPUPixelBuffer *GPU_pixel_buffer_create(uint size) +{ + /* Ensure buffer satifies the alignment of 256 bytes for copying + * data between buffers and textures. As specified in: + * https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf + * + * Ensuring minimal size across all platforms handles cases for small-sized + * textures and avoids issues with zero-sized buffers. */ + size = ceil_to_multiple_ul(size, 256); + PixelBuffer *pixbuf = GPUBackend::get()->pixelbuf_alloc(size); + return wrap(pixbuf); +} + +void GPU_pixel_buffer_free(GPUPixelBuffer *pix_buf) +{ + PixelBuffer *handle = unwrap(pix_buf); + delete handle; +} + +void *GPU_pixel_buffer_map(GPUPixelBuffer *pix_buf) +{ + return reinterpret_cast(pix_buf)->map(); +} + +void GPU_pixel_buffer_unmap(GPUPixelBuffer *pix_buf) +{ + reinterpret_cast(pix_buf)->unmap(); +} + +uint GPU_pixel_buffer_size(GPUPixelBuffer *pix_buf) +{ + return reinterpret_cast(pix_buf)->get_size(); +} + +int64_t GPU_pixel_buffer_get_native_handle(GPUPixelBuffer *pix_buf) +{ + return reinterpret_cast(pix_buf)->get_native_handle(); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name GPU Sampler Objects * diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 124b1751b96..40298ce3074 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -129,6 +129,10 @@ class Texture { virtual void update_sub( int mip, int offset[3], int extent[3], eGPUDataFormat format, const void *data) = 0; + virtual void update_sub(int offset[3], + int extent[3], + eGPUDataFormat format, + GPUPixelBuffer *pixbuf) = 0; /* TODO(fclem): Legacy. Should be removed at some point. */ virtual uint gl_bindcode_get() const = 0; @@ -264,6 +268,35 @@ static inline const Texture *unwrap(const GPUTexture *vert) return reinterpret_cast(vert); } +/* GPU pixel Buffer. */ +class PixelBuffer { + protected: + uint size_ = 0; + + public: + PixelBuffer(uint size) : size_(size){}; + virtual ~PixelBuffer(){}; + + virtual void *map() = 0; + virtual void unmap() = 0; + virtual int64_t get_native_handle() = 0; + virtual uint get_size() = 0; +}; + +/* Syntactic sugar. */ +static inline GPUPixelBuffer *wrap(PixelBuffer *pixbuf) +{ + return reinterpret_cast(pixbuf); +} +static inline PixelBuffer *unwrap(GPUPixelBuffer *pixbuf) +{ + return reinterpret_cast(pixbuf); +} +static inline const PixelBuffer *unwrap(const GPUPixelBuffer *pixbuf) +{ + return reinterpret_cast(pixbuf); +} + #undef DEBUG_NAME_LEN inline size_t to_bytesize(eGPUTextureFormat format) @@ -405,6 +438,8 @@ inline size_t to_bytesize(eGPUDataFormat data_format) switch (data_format) { case GPU_DATA_UBYTE: return 1; + case GPU_DATA_HALF_FLOAT: + return 2; case GPU_DATA_FLOAT: case GPU_DATA_INT: case GPU_DATA_UINT: diff --git a/source/blender/gpu/metal/mtl_backend.hh b/source/blender/gpu/metal/mtl_backend.hh index 082fab24ba4..f94f1abae6d 100644 --- a/source/blender/gpu/metal/mtl_backend.hh +++ b/source/blender/gpu/metal/mtl_backend.hh @@ -66,8 +66,10 @@ class MTLBackend : public GPUBackend { Context *context_alloc(void *ghost_window, void *ghost_context) override; Batch *batch_alloc() override; DrawList *drawlist_alloc(int list_length) override; + Fence *fence_alloc() override; FrameBuffer *framebuffer_alloc(const char *name) override; IndexBuf *indexbuf_alloc() override; + PixelBuffer *pixelbuf_alloc(uint size) override; QueryPool *querypool_alloc() override; Shader *shader_alloc(const char *name) override; Texture *texture_alloc(const char *name) override; diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm index 240951c1ebd..89e0409582e 100644 --- a/source/blender/gpu/metal/mtl_backend.mm +++ b/source/blender/gpu/metal/mtl_backend.mm @@ -55,6 +55,11 @@ DrawList *MTLBackend::drawlist_alloc(int list_length) return new MTLDrawList(list_length); }; +Fence *MTLBackend::fence_alloc() +{ + return new MTLFence(); +}; + FrameBuffer *MTLBackend::framebuffer_alloc(const char *name) { MTLContext *mtl_context = static_cast( @@ -67,6 +72,11 @@ IndexBuf *MTLBackend::indexbuf_alloc() return new MTLIndexBuf(); }; +PixelBuffer *MTLBackend::pixelbuf_alloc(uint size) +{ + return new MTLPixelBuffer(size); +}; + QueryPool *MTLBackend::querypool_alloc() { return new MTLQueryPool(); diff --git a/source/blender/gpu/metal/mtl_command_buffer.mm b/source/blender/gpu/metal/mtl_command_buffer.mm index 7e5bce48d56..327f2c0086d 100644 --- a/source/blender/gpu/metal/mtl_command_buffer.mm +++ b/source/blender/gpu/metal/mtl_command_buffer.mm @@ -538,6 +538,24 @@ bool MTLCommandBufferManager::insert_memory_barrier(eGPUBarrier barrier_bits, return false; } +void MTLCommandBufferManager::encode_signal_event(id event, uint64_t signal_value) +{ + /* Ensure active command buffer. */ + id cmd_buf = this->ensure_begin(); + BLI_assert(cmd_buf); + this->end_active_command_encoder(); + [cmd_buf encodeSignalEvent:event value:signal_value]; +} + +void MTLCommandBufferManager::encode_wait_for_event(id event, uint64_t signal_value) +{ + /* Ensure active command buffer. */ + id cmd_buf = this->ensure_begin(); + BLI_assert(cmd_buf); + this->end_active_command_encoder(); + [cmd_buf encodeWaitForEvent:event value:signal_value]; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh index 6229afcef79..39474eeb6fa 100644 --- a/source/blender/gpu/metal/mtl_context.hh +++ b/source/blender/gpu/metal/mtl_context.hh @@ -555,6 +555,8 @@ class MTLCommandBufferManager { bool insert_memory_barrier(eGPUBarrier barrier_bits, eGPUStageBarrierBits before_stages, eGPUStageBarrierBits after_stages); + void encode_signal_event(id event, uint64_t value); + void encode_wait_for_event(id event, uint64_t value); /* TODO(Metal): Support fences in command buffer class. */ /* Debug. */ diff --git a/source/blender/gpu/metal/mtl_state.hh b/source/blender/gpu/metal/mtl_state.hh index 1af56378c5a..6338774eb0e 100644 --- a/source/blender/gpu/metal/mtl_state.hh +++ b/source/blender/gpu/metal/mtl_state.hh @@ -84,4 +84,24 @@ class MTLStateManager : public StateManager { MEM_CXX_CLASS_ALLOC_FUNCS("MTLStateManager") }; +/* Fence synchronization primitive. */ +class MTLFence : public Fence { + private: + /* Using an event in this instance, as this is global for the command stream, rather than being + * inserted at the encoder level. This has the behaviour to match the GL functionality. */ + id mtl_event_ = nil; + /* Events can be re-used multiple times. We can track a counter flagging the latest value + * signalled. */ + uint64_t last_signalled_value_ = 0; + + public: + MTLFence() : Fence(){}; + ~MTLFence(); + + void signal() override; + void wait() override; + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLFence") +}; + } // namespace blender::gpu diff --git a/source/blender/gpu/metal/mtl_state.mm b/source/blender/gpu/metal/mtl_state.mm index 31182cf91d1..b29dcd9af48 100644 --- a/source/blender/gpu/metal/mtl_state.mm +++ b/source/blender/gpu/metal/mtl_state.mm @@ -560,6 +560,44 @@ void MTLStateManager::issue_barrier(eGPUBarrier barrier_bits) ctx->main_command_buffer.insert_memory_barrier(barrier_bits, before_stages, after_stages); } +MTLFence::~MTLFence() +{ + if (mtl_event_) { + [mtl_event_ release]; + mtl_event_ = nil; + } +} + +void MTLFence::signal() +{ + if (mtl_event_ == nil) { + MTLContext *ctx = MTLContext::get(); + BLI_assert(ctx); + mtl_event_ = [ctx->device newEvent]; + } + MTLContext *ctx = MTLContext::get(); + BLI_assert(ctx); + ctx->main_command_buffer.encode_signal_event(mtl_event_, ++last_signalled_value_); + + signalled_ = true; +} + +void MTLFence::wait() +{ + /* do not attempt to wait if event has not yet been signalled for the first time. */ + if (mtl_event_ == nil) { + return; + } + + if (signalled_) { + MTLContext *ctx = MTLContext::get(); + BLI_assert(ctx); + + ctx->main_command_buffer.encode_wait_for_event(mtl_event_, last_signalled_value_); + signalled_ = false; + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh index 28b55306707..68bd02c6761 100644 --- a/source/blender/gpu/metal/mtl_texture.hh +++ b/source/blender/gpu/metal/mtl_texture.hh @@ -235,6 +235,10 @@ class MTLTexture : public Texture { void update_sub( int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data) override; + void update_sub(int offset[3], + int extent[3], + eGPUDataFormat format, + GPUPixelBuffer *pixbuf) override; void generate_mipmap() override; void copy_to(Texture *dst) override; @@ -424,6 +428,24 @@ class MTLTexture : public Texture { MEM_CXX_CLASS_ALLOC_FUNCS("MTLTexture") }; +class MTLPixelBuffer : public PixelBuffer { + private: + id buffer_ = nil; + + public: + MTLPixelBuffer(uint size); + ~MTLPixelBuffer(); + + void *map() override; + void unmap() override; + int64_t get_native_handle() override; + uint get_size() override; + + id get_metal_buffer(); + + MEM_CXX_CLASS_ALLOC_FUNCS("MTLPixelBuffer") +}; + /* Utility */ MTLPixelFormat gpu_texture_format_to_metal(eGPUTextureFormat tex_format); int get_mtl_format_bytesize(MTLPixelFormat tex_format); diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm index 29dcc8d32ee..29f535ea39b 100644 --- a/source/blender/gpu/metal/mtl_texture.mm +++ b/source/blender/gpu/metal/mtl_texture.mm @@ -885,6 +885,61 @@ void gpu::MTLTexture::update_sub( } } +void MTLTexture::update_sub(int offset[3], + int extent[3], + eGPUDataFormat format, + GPUPixelBuffer *pixbuf) +{ + /* Update texture from pixel buffer. */ + BLI_assert(validate_data_format(format_, format)); + BLI_assert(pixbuf != nullptr); + + /* Fetch pixel buffer metal buffer. */ + MTLPixelBuffer *mtl_pix_buf = static_cast(unwrap(pixbuf)); + id buffer = mtl_pix_buf->get_metal_buffer(); + BLI_assert(buffer != nil); + if (buffer == nil) { + return; + } + + /* Ensure texture is ready. */ + this->ensure_baked(); + BLI_assert(texture_ != nil); + + /* Calculate dimensions. */ + int num_image_channels = to_component_len(format_); + + uint bits_per_pixel = num_image_channels * to_bytesize(format); + uint bytes_per_row = bits_per_pixel * extent[0]; + uint bytes_per_image = bytes_per_row * extent[1]; + + /* Currently only required for 2D textures. */ + if (type_ == GPU_TEXTURE_2D) { + + /* Create blit command encoder to copy data. */ + MTLContext *ctx = MTLContext::get(); + BLI_assert(ctx); + + id blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder(); + [blit_encoder copyFromBuffer:buffer + sourceOffset:0 + sourceBytesPerRow:bytes_per_row + sourceBytesPerImage:bytes_per_image + sourceSize:MTLSizeMake(extent[0], extent[1], 1) + toTexture:texture_ + destinationSlice:0 + destinationLevel:0 + destinationOrigin:MTLOriginMake(offset[0], offset[1], 0)]; + + if (texture_.storageMode == MTLStorageModeManaged) { + [blit_encoder synchronizeResource:texture_]; + } + } + else { + BLI_assert(false); + } +} + void gpu::MTLTexture::ensure_mipmaps(int miplvl) { @@ -1797,4 +1852,74 @@ void gpu::MTLTexture::reset() /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Pixel Buffer + * \{ */ + +MTLPixelBuffer::MTLPixelBuffer(uint size) : PixelBuffer(size) +{ + MTLContext *ctx = MTLContext::get(); + BLI_assert(ctx); + /* Ensure buffer satifies the alignment of 256 bytes for copying + * data between buffers and textures. As specified in: + * https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ + BLI_assert(size >= 256); + + MTLResourceOptions resource_options = ([ctx->device hasUnifiedMemory]) ? + MTLResourceStorageModeShared : + MTLResourceStorageModeManaged; + buffer_ = [ctx->device newBufferWithLength:size options:resource_options]; + BLI_assert(buffer_ != nil); +} + +MTLPixelBuffer::~MTLPixelBuffer() +{ + if (buffer_) { + [buffer_ release]; + buffer_ = nil; + } +} + +void *MTLPixelBuffer::map() +{ + if (buffer_ == nil) { + return nullptr; + } + + return [buffer_ contents]; +} + +void MTLPixelBuffer::unmap() +{ + if (buffer_ == nil) { + return; + } + + /* Ensure changes are synchronized. */ + if (buffer_.resourceOptions & MTLResourceStorageModeManaged) { + [buffer_ didModifyRange:NSMakeRange(0, size_)]; + } +} + +int64_t MTLPixelBuffer::get_native_handle() +{ + if (buffer_ == nil) { + return 0; + } + + return reinterpret_cast(buffer_); +} + +uint MTLPixelBuffer::get_size() +{ + return size_; +} + +id MTLPixelBuffer::get_metal_buffer() +{ + return buffer_; +} + +/** \} */ + } // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 39a4dd80279..eb9466e0e26 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -76,6 +76,11 @@ class GLBackend : public GPUBackend { return new GLDrawList(list_length); }; + Fence *fence_alloc() override + { + return new GLFence(); + }; + FrameBuffer *framebuffer_alloc(const char *name) override { return new GLFrameBuffer(name); @@ -86,6 +91,11 @@ class GLBackend : public GPUBackend { return new GLIndexBuf(); }; + PixelBuffer *pixelbuf_alloc(uint size) override + { + return new GLPixelBuffer(size); + }; + QueryPool *querypool_alloc() override { return new GLQueryPool(); diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc index 46422124112..fdeee725a2b 100644 --- a/source/blender/gpu/opengl/gl_state.cc +++ b/source/blender/gpu/opengl/gl_state.cc @@ -641,6 +641,34 @@ void GLStateManager::issue_barrier(eGPUBarrier barrier_bits) glMemoryBarrier(to_gl(barrier_bits)); } +GLFence::~GLFence() +{ + if (gl_sync_ != 0) { + glDeleteSync(gl_sync_); + gl_sync_ = 0; + } +} + +void GLFence::signal() +{ + /* If fence is already signalled, create a newly signalled fence primitive. */ + if (gl_sync_) { + glDeleteSync(gl_sync_); + } + + gl_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + signalled_ = true; +} + +void GLFence::wait() +{ + /* Do not wait if fence does not yet exist. */ + if (gl_sync_ == 0) { + return; + } + glWaitSync(gl_sync_, 0, GL_TIMEOUT_IGNORED); + signalled_ = false; +} /** \} */ } // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh index e44ad60db85..b1c0c1bce03 100644 --- a/source/blender/gpu/opengl/gl_state.hh +++ b/source/blender/gpu/opengl/gl_state.hh @@ -103,6 +103,21 @@ class GLStateManager : public StateManager { MEM_CXX_CLASS_ALLOC_FUNCS("GLStateManager") }; +/* Fence synchronization primitive. */ +class GLFence : public Fence { + private: + GLsync gl_sync_ = 0; + + public: + GLFence() : Fence(){}; + ~GLFence(); + + void signal() override; + void wait() override; + + MEM_CXX_CLASS_ALLOC_FUNCS("GLFence") +}; + static inline GLbitfield to_gl(eGPUBarrier barrier_bits) { GLbitfield barrier = 0; diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index 02fc7dfdb5f..dae5aa45cc9 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -303,6 +303,42 @@ void GLTexture::update_sub( has_pixels_ = true; } +void GLTexture::update_sub(int offset[3], + int extent[3], + eGPUDataFormat format, + GPUPixelBuffer *pixbuf) +{ + /* Update texture from pixel buffer. */ + BLI_assert(validate_data_format(format_, format)); + BLI_assert(pixbuf != nullptr); + + const int dimensions = this->dimensions_count(); + GLenum gl_format = to_gl_data_format(format_); + GLenum gl_type = to_gl(format); + + /* Temporarily Bind texture. */ + GLContext::state_manager_active_get()->texture_bind_temp(this); + + /* Bind pixel buffer for source data. */ + int pix_buf_handle = (int)GPU_pixel_buffer_get_native_handle(pixbuf); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pix_buf_handle); + + switch (dimensions) { + default: + case 1: + glTexSubImage1D(target_, 0, offset[0], extent[0], gl_format, gl_type, 0); + break; + case 2: + glTexSubImage2D(target_, 0, UNPACK2(offset), UNPACK2(extent), gl_format, gl_type, 0); + break; + case 3: + glTexSubImage3D(target_, 0, UNPACK3(offset), UNPACK3(extent), gl_format, gl_type, 0); + break; + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + /** * This will create the mipmap images and populate them with filtered data from base level. * @@ -739,4 +775,63 @@ uint GLTexture::gl_bindcode_get() const return tex_id_; } +/* -------------------------------------------------------------------- */ +/** \name Pixel Buffer + * \{ */ + +GLPixelBuffer::GLPixelBuffer(uint size) : PixelBuffer(size) +{ + glGenBuffers(1, &gl_id_); + BLI_assert(gl_id_); + + if (!gl_id_) { + return; + } + + /* Ensure size is non-zero for pixel buffer backing storage creation. */ + size = max_ii(size, 32); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_id_); + glBufferData(GL_PIXEL_UNPACK_BUFFER, size, 0, GL_DYNAMIC_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +GLPixelBuffer::~GLPixelBuffer() +{ + if (!gl_id_) { + return; + } + glDeleteBuffers(1, &gl_id_); +} + +void *GLPixelBuffer::map() +{ + if (!gl_id_) { + BLI_assert(false); + return nullptr; + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_id_); + void *ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + BLI_assert(ptr); + return ptr; +} + +void GLPixelBuffer::unmap() +{ + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +int64_t GLPixelBuffer::get_native_handle() +{ + return (int64_t)gl_id_; +} + +uint GLPixelBuffer::get_size() +{ + return size_; +} + +/** \} */ } // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index b7d72455c25..efc3c743dd5 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -46,6 +46,10 @@ class GLTexture : public Texture { void update_sub( int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data) override; + void update_sub(int offset[3], + int extent[3], + eGPUDataFormat format, + GPUPixelBuffer *pixbuf) override; /** * This will create the mipmap images and populate them with filtered data from base level. @@ -87,6 +91,22 @@ class GLTexture : public Texture { MEM_CXX_CLASS_ALLOC_FUNCS("GLTexture") }; +class GLPixelBuffer : public PixelBuffer { + private: + GLuint gl_id_ = 0; + + public: + GLPixelBuffer(uint size); + ~GLPixelBuffer(); + + void *map() override; + void unmap() override; + int64_t get_native_handle() override; + uint get_size() override; + + MEM_CXX_CLASS_ALLOC_FUNCS("GLPixelBuffer") +}; + inline GLenum to_gl_internal_format(eGPUTextureFormat format) { /* You can add any of the available type to this list @@ -282,6 +302,8 @@ inline GLenum to_gl(eGPUDataFormat format) return GL_UNSIGNED_INT_2_10_10_10_REV; case GPU_DATA_10_11_11_REV: return GL_UNSIGNED_INT_10F_11F_11F_REV; + case GPU_DATA_HALF_FLOAT: + return GL_HALF_FLOAT; default: BLI_assert_msg(0, "Unhandled data format"); return GL_FLOAT; diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h index d5ad70e5a03..617db7b147c 100644 --- a/source/blender/render/RE_engine.h +++ b/source/blender/render/RE_engine.h @@ -18,6 +18,7 @@ struct BakeTargets; struct BakePixel; struct Depsgraph; +struct GPUContext; struct Main; struct Object; struct Render; @@ -158,9 +159,12 @@ typedef struct RenderEngine { void *update_render_passes_data; /* GPU context. */ - void *gpu_context; + void *wm_gpu_context; /* WindowManager GPU context -> GHOSTContext. */ ThreadMutex gpu_context_mutex; bool use_drw_render_context; + struct GPUContext *gpu_context; + /* Whether to restore DRWState after RenderEngine display pass. */ + bool gpu_restore_context; } RenderEngine; RenderEngine *RE_engine_create(RenderEngineType *type); diff --git a/source/blender/render/intern/engine.cc b/source/blender/render/intern/engine.cc index acca657f7dc..b9672246c5f 100644 --- a/source/blender/render/intern/engine.cc +++ b/source/blender/render/intern/engine.cc @@ -34,6 +34,8 @@ #include "DEG_depsgraph_debug.h" #include "DEG_depsgraph_query.h" +#include "GPU_context.h" + #include "RNA_access.h" #ifdef WITH_PYTHON @@ -1285,45 +1287,69 @@ bool RE_engine_gpu_context_create(RenderEngine *engine) BLI_assert(BLI_thread_is_main()); const bool drw_state = DRW_opengl_context_release(); - engine->gpu_context = WM_opengl_context_create(); + engine->wm_gpu_context = WM_opengl_context_create(); - /* On Windows an old context is restored after creation, and subsequent release of context - * generates a Win32 error. Harmless for users, but annoying to have possible misleading - * error prints in the console. */ -#ifndef _WIN32 - if (engine->gpu_context) { - WM_opengl_context_release(engine->gpu_context); + if (engine->wm_gpu_context) { + /* Activate new OpenGL Context for GPUContext creation. */ + WM_opengl_context_activate(engine->wm_gpu_context); + /* Requires GPUContext for usage of GPU Module for displaying results. */ + engine->gpu_context = GPU_context_create(nullptr, engine->wm_gpu_context); + GPU_context_active_set(nullptr); + /* Deactivate newly created OpenGL Context, as it is not needed until + * `RE_engine_gpu_context_enable` is called. */ + WM_opengl_context_release(engine->wm_gpu_context); + } + else { + engine->gpu_context = nullptr; } -#endif DRW_opengl_context_activate(drw_state); - return engine->gpu_context != nullptr; + return engine->wm_gpu_context != nullptr; } void RE_engine_gpu_context_destroy(RenderEngine *engine) { - if (!engine->gpu_context) { + if (!engine->wm_gpu_context) { return; } const bool drw_state = DRW_opengl_context_release(); - WM_opengl_context_activate(engine->gpu_context); - WM_opengl_context_dispose(engine->gpu_context); + WM_opengl_context_activate(engine->wm_gpu_context); + if (engine->gpu_context) { + GPUContext *restore_context = GPU_context_active_get(); + GPU_context_active_set(engine->gpu_context); + GPU_context_discard(engine->gpu_context); + if (restore_context != engine->gpu_context) { + GPU_context_active_set(restore_context); + } + engine->gpu_context = nullptr; + } + WM_opengl_context_dispose(engine->wm_gpu_context); DRW_opengl_context_activate(drw_state); } bool RE_engine_gpu_context_enable(RenderEngine *engine) { + engine->gpu_restore_context = false; if (engine->use_drw_render_context) { DRW_render_context_enable(engine->re); return true; } - if (engine->gpu_context) { + if (engine->wm_gpu_context) { BLI_mutex_lock(&engine->gpu_context_mutex); - WM_opengl_context_activate(engine->gpu_context); + /* If a previous OpenGL/GPUContext was active (DST.gpu_context), we should later restore this + * when disabling the RenderEngine context. */ + engine->gpu_restore_context = DRW_opengl_context_release(); + + /* Activate RenderEngine OpenGL and GPU Context. */ + WM_opengl_context_activate(engine->wm_gpu_context); + if (engine->gpu_context) { + GPU_context_active_set(engine->gpu_context); + GPU_render_begin(); + } return true; } return false; @@ -1335,8 +1361,14 @@ void RE_engine_gpu_context_disable(RenderEngine *engine) DRW_render_context_disable(engine->re); } else { - if (engine->gpu_context) { - WM_opengl_context_release(engine->gpu_context); + if (engine->wm_gpu_context) { + if (engine->gpu_context) { + GPU_render_end(); + GPU_context_active_set(nullptr); + } + WM_opengl_context_release(engine->wm_gpu_context); + /* Restore DRW state context if previously active. */ + DRW_opengl_context_activate(engine->gpu_restore_context); BLI_mutex_unlock(&engine->gpu_context_mutex); } } @@ -1348,7 +1380,7 @@ void RE_engine_gpu_context_lock(RenderEngine *engine) /* Locking already handled by the draw manager. */ } else { - if (engine->gpu_context) { + if (engine->wm_gpu_context) { BLI_mutex_lock(&engine->gpu_context_mutex); } } @@ -1360,7 +1392,7 @@ void RE_engine_gpu_context_unlock(RenderEngine *engine) /* Locking already handled by the draw manager. */ } else { - if (engine->gpu_context) { + if (engine->wm_gpu_context) { BLI_mutex_unlock(&engine->gpu_context_mutex); } }