From 204ae33d755561e68ad3a6193d9a90d39d47d4da Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 7 Dec 2021 20:49:34 +0100 Subject: Revert "Fix T93350: Cycles renders shows black during rendering huge resolutions" This reverts commit 5e37f70307bdacedd0f7da65f8b385bc1426f21d. It is leading to freezing of the entire desktop for a few seconds when stopping 3D viewport rendering on my Linux / NVIDIA system. --- intern/cycles/blender/display_driver.cpp | 720 ++++++++++--------------------- 1 file changed, 222 insertions(+), 498 deletions(-) (limited to 'intern/cycles/blender/display_driver.cpp') diff --git a/intern/cycles/blender/display_driver.cpp b/intern/cycles/blender/display_driver.cpp index d902edc4695..abf421983b3 100644 --- a/intern/cycles/blender/display_driver.cpp +++ b/intern/cycles/blender/display_driver.cpp @@ -272,300 +272,12 @@ uint BlenderDisplaySpaceShader::get_shader_program() return shader_program_; } -/* -------------------------------------------------------------------- - * DrawTile. - */ - -/* Higher level representation of a texture from the graphics library. */ -class GLTexture { - public: - /* Global counter for all allocated OpenGL textures used by instances of this class. */ - static inline std::atomic num_used = 0; - - GLTexture() = default; - - ~GLTexture() - { - assert(gl_id == 0); - } - - GLTexture(const GLTexture &other) = delete; - GLTexture &operator=(GLTexture &other) = delete; - - GLTexture(GLTexture &&other) noexcept - : gl_id(other.gl_id), width(other.width), height(other.height) - { - other.reset(); - } - - GLTexture &operator=(GLTexture &&other) - { - if (this == &other) { - return *this; - } - - gl_id = other.gl_id; - width = other.width; - height = other.height; - - other.reset(); - - return *this; - } - - bool gl_resources_ensure() - { - if (gl_id) { - return true; - } - - /* Create texture. */ - glGenTextures(1, &gl_id); - if (!gl_id) { - 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); - - ++num_used; - - return true; - } - - void gl_resources_destroy() - { - if (!gl_id) { - return; - } - - glDeleteTextures(1, &gl_id); - - reset(); - - --num_used; - } - - /* OpenGL resource IDs of the texture. - * - * NOTE: Allocated on the render engine's context. */ - uint gl_id = 0; - - /* Dimensions of the texture in pixels. */ - int width = 0; - int height = 0; - - protected: - void reset() - { - gl_id = 0; - width = 0; - height = 0; - } -}; - -/* Higher level representation of a Pixel Buffer Object (PBO) from the graphics library. */ -class GLPixelBufferObject { - public: - /* Global counter for all allocated OpenGL PBOs used by instances of this class. */ - static inline std::atomic num_used = 0; - - GLPixelBufferObject() = default; - - ~GLPixelBufferObject() - { - assert(gl_id == 0); - } - - GLPixelBufferObject(const GLPixelBufferObject &other) = delete; - GLPixelBufferObject &operator=(GLPixelBufferObject &other) = delete; - - GLPixelBufferObject(GLPixelBufferObject &&other) noexcept - : gl_id(other.gl_id), width(other.width), height(other.height) - { - other.reset(); - } - - GLPixelBufferObject &operator=(GLPixelBufferObject &&other) - { - if (this == &other) { - return *this; - } - - gl_id = other.gl_id; - width = other.width; - height = other.height; - - other.reset(); - - return *this; - } - - bool gl_resources_ensure() - { - if (gl_id) { - return true; - } - - glGenBuffers(1, &gl_id); - if (!gl_id) { - LOG(ERROR) << "Error creating texture pixel buffer object."; - return false; - } - - ++num_used; - - return true; - } - - void gl_resources_destroy() - { - if (!gl_id) { - return; - } - - glDeleteBuffers(1, &gl_id); - - reset(); - - --num_used; - } - - /* OpenGL resource IDs of the PBO. - * - * NOTE: Allocated on the render engine's context. */ - uint gl_id = 0; - - /* Dimensions of the PBO. */ - int width = 0; - int height = 0; - - protected: - void reset() - { - gl_id = 0; - width = 0; - height = 0; - } -}; - -class DrawTile { - public: - DrawTile() = default; - ~DrawTile() = default; - - DrawTile(const DrawTile &other) = delete; - DrawTile &operator=(const DrawTile &other) = delete; - - DrawTile(DrawTile &&other) noexcept = default; - - DrawTile &operator=(DrawTile &&other) = default; - - bool gl_resources_ensure() - { - if (!texture.gl_resources_ensure()) { - gl_resources_destroy(); - return false; - } - - if (!gl_vertex_buffer) { - glGenBuffers(1, &gl_vertex_buffer); - if (!gl_vertex_buffer) { - LOG(ERROR) << "Error allocating tile VBO."; - gl_resources_destroy(); - return false; - } - } - - return true; - } - - void gl_resources_destroy() - { - texture.gl_resources_destroy(); - - if (gl_vertex_buffer) { - glDeleteBuffers(1, &gl_vertex_buffer); - gl_vertex_buffer = 0; - } - } - - inline bool ready_to_draw() const - { - return texture.gl_id != 0; - } - - /* Texture which contains pixels of the tile. */ - GLTexture texture; - - /* Display parameters the texture of this tile has been updated for. */ - BlenderDisplayDriver::Params params; - - /* OpenGL resources needed for drawing. */ - uint gl_vertex_buffer = 0; -}; - -class DrawTileAndPBO { - public: - bool gl_resources_ensure() - { - if (!tile.gl_resources_ensure() || !buffer_object.gl_resources_ensure()) { - gl_resources_destroy(); - return false; - } - - return true; - } - - void gl_resources_destroy() - { - tile.gl_resources_destroy(); - buffer_object.gl_resources_destroy(); - } - - DrawTile tile; - GLPixelBufferObject buffer_object; -}; - /* -------------------------------------------------------------------- * BlenderDisplayDriver. */ -struct BlenderDisplayDriver::Tiles { - /* Resources of a tile which is being currently rendered. */ - DrawTileAndPBO current_tile; - - /* All tiles which rendering is finished and which content will not be changed. */ - struct { - vector tiles; - - void gl_resources_destroy_and_clear() - { - for (DrawTile &tile : tiles) { - tile.gl_resources_destroy(); - } - - tiles.clear(); - } - } finished_tiles; -}; - BlenderDisplayDriver::BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene) - : b_engine_(b_engine), - display_shader_(BlenderDisplayShader::create(b_engine, b_scene)), - tiles_(make_unique()) + : b_engine_(b_engine), display_shader_(BlenderDisplayShader::create(b_engine, b_scene)) { /* Create context while on the main thread. */ gl_context_create(); @@ -580,21 +292,6 @@ BlenderDisplayDriver::~BlenderDisplayDriver() * Update procedure. */ -void BlenderDisplayDriver::next_tile_begin() -{ - if (!tiles_->current_tile.tile.ready_to_draw()) { - LOG(ERROR) - << "Unexpectedly moving to the next tile without any data provided for current tile."; - return; - } - - /* Moving to the next tile without giving render data for the current tile is not an expected - * situation. */ - DCHECK(!need_clear_); - - tiles_->finished_tiles.tiles.emplace_back(std::move(tiles_->current_tile.tile)); -} - bool BlenderDisplayDriver::update_begin(const Params ¶ms, int texture_width, int texture_height) @@ -615,33 +312,24 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms, glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED); } - DrawTile ¤t_tile = tiles_->current_tile.tile; - GLPixelBufferObject ¤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. - * It also makes the logic reliable from the whether drawing did happen or not point of view. */ - if (need_clear_) { - tiles_->finished_tiles.gl_resources_destroy_and_clear(); - need_clear_ = false; - } - - if (!tiles_->current_tile.gl_resources_ensure()) { - tiles_->current_tile.gl_resources_destroy(); + if (!gl_texture_resources_ensure()) { gl_context_disable(); return false; } /* Update texture dimensions if needed. */ - if (current_tile.texture.width != texture_width || - current_tile.texture.height != texture_height) { + if (texture_.width != texture_width || texture_.height != texture_height) { glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, current_tile.texture.gl_id); + glBindTexture(GL_TEXTURE_2D, 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; + texture_.width = texture_width; + texture_.height = texture_height; glBindTexture(GL_TEXTURE_2D, 0); + + /* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to + * avoid undefined content. */ + texture_.need_clear = true; } /* Update PBO dimensions if needed. @@ -653,58 +341,29 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms, * sending too much data to GPU when resolution divider is not 1. */ /* TODO(sergey): Investigate whether keeping the PBO exact size of the texture makes non-interop * 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 int buffer_width = params.full_size.x; + const int buffer_height = params.full_size.y; + if (texture_.buffer_width != buffer_width || texture_.buffer_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); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_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; + texture_.buffer_width = buffer_width; + texture_.buffer_height = 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 - * the easiest and is not expensive. */ - tiles_->current_tile.tile.params = params; - - return true; -} - -static void update_tile_texture_pixels(const DrawTileAndPBO &tile) -{ - const GLTexture &texture = tile.tile.texture; - - DCHECK_NE(tile.buffer_object.gl_id, 0); + /* New content will be provided to the texture in one way or another, so mark this in a + * centralized place. */ + texture_.need_update = true; - 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); + texture_.params = params; - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); + return true; } void BlenderDisplayDriver::update_end() { - /* Unpack the PBO into the texture as soon as the new content is provided. - * - * This allows to ensure that the unpacking happens while resources like graphics interop (which - * lifetime is outside of control of the display driver) are still valid, as well as allows to - * move the tile from being current to finished immediately after this call. - * - * One concern with this approach is that if the update happens more often than drawing then - * doing the unpack here occupies GPU transfer for no good reason. However, the render scheduler - * takes care of ensuring updates don't happen that often. In regular applications redraw will - * happen much more often than this update. */ - update_tile_texture_pixels(tiles_->current_tile); - gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); @@ -717,11 +376,7 @@ 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); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); half4 *mapped_rgba_pixels = reinterpret_cast( glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); @@ -729,6 +384,15 @@ half4 *BlenderDisplayDriver::map_texture_buffer() LOG(ERROR) << "Error mapping BlenderDisplayDriver pixel buffer object."; } + if (texture_.need_clear) { + const int64_t texture_width = texture_.width; + const int64_t texture_height = texture_.height; + memset(reinterpret_cast(mapped_rgba_pixels), + 0, + texture_width * texture_height * sizeof(half4)); + texture_.need_clear = false; + } + return mapped_rgba_pixels; } @@ -747,9 +411,12 @@ BlenderDisplayDriver::GraphicsInterop BlenderDisplayDriver::graphics_interop_get { GraphicsInterop interop_dst; - 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.buffer_width = texture_.buffer_width; + interop_dst.buffer_height = texture_.buffer_height; + interop_dst.opengl_pbo_id = texture_.gl_pbo_id; + + interop_dst.need_clear = texture_.need_clear; + texture_.need_clear = false; return interop_dst; } @@ -770,7 +437,7 @@ void BlenderDisplayDriver::graphics_interop_deactivate() void BlenderDisplayDriver::clear() { - need_clear_ = true; + texture_.need_clear = true; } void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y) @@ -778,78 +445,42 @@ void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y) zoom_ = make_float2(zoom_x, zoom_y); } -/* Update vertex buffer with new coordinates of vertex positions and texture coordinates. - * 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) +void BlenderDisplayDriver::draw(const Params ¶ms) { - const int x = params.full_offset.x; - const int y = params.full_offset.y; - - 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); + /* See do_update_begin() for why no locking is required here. */ + const bool transparent = true; // TODO(sergey): Derive this from Film. - float *vpointer = reinterpret_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); - if (!vpointer) { + if (!gl_draw_resources_ensure()) { return; } - vpointer[0] = 0.0f; - vpointer[1] = 0.0f; - vpointer[2] = x; - vpointer[3] = y; - - vpointer[4] = 1.0f; - vpointer[5] = 0.0f; - vpointer[6] = x + width; - vpointer[7] = y; - - vpointer[8] = 1.0f; - vpointer[9] = 1.0f; - vpointer[10] = x + width; - vpointer[11] = y + height; - - vpointer[12] = 0.0f; - vpointer[13] = 1.0f; - vpointer[14] = x; - vpointer[15] = y + height; - - glUnmapBuffer(GL_ARRAY_BUFFER); -} + if (use_gl_context_) { + gl_context_mutex_.lock(); + } -static void draw_tile(const float2 &zoom, - const int texcoord_attribute, - const int position_attribute, - const DrawTile &draw_tile) -{ - if (!draw_tile.ready_to_draw()) { + if (texture_.need_clear) { + /* Texture is requested to be cleared and was not yet cleared. + * + * Do early return which should be equivalent of drawing all-zero texture. + * Watch out for the lock though so that the clear happening during update is properly + * synchronized here. */ + gl_context_mutex_.unlock(); return; } - const GLTexture &texture = draw_tile.texture; - - DCHECK_NE(texture.gl_id, 0); - DCHECK_NE(draw_tile.gl_vertex_buffer, 0); + if (gl_upload_sync_) { + glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); + } - glBindBuffer(GL_ARRAY_BUFFER, draw_tile.gl_vertex_buffer); + if (transparent) { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } - /* 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); + display_shader_->bind(params.full_size.x, params.full_size.y); - glBindTexture(GL_TEXTURE_2D, texture.gl_id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); /* Trick to keep sharp rendering without jagged edges on all GPUs. * @@ -859,93 +490,53 @@ static void draw_tile(const float2 &zoom, * * Use explicit MIN assignment to make sure the driver does not have an undefined behavior at * the zoom level 1. The MAG filter is always NEAREST. */ - const float zoomed_width = draw_tile.params.size.x * zoom.x; - 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) { + const float zoomed_width = params.size.x * zoom_.x; + const float zoomed_height = params.size.y * zoom_.y; + if (texture_.width != params.size.x || texture_.height != params.size.y) { /* Resolution divider is different from 1, force nearest interpolation. */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } - else if (zoomed_width - draw_tile.params.size.x > 0.5f || - zoomed_height - draw_tile.params.size.y > 0.5f) { + else if (zoomed_width - params.size.x > 0.5f || zoomed_height - params.size.y > 0.5f) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } - 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); -} + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); -void BlenderDisplayDriver::draw(const Params ¶ms) -{ - /* See do_update_begin() for why no locking is required here. */ - const bool transparent = true; // TODO(sergey): Derive this from Film. - - if (use_gl_context_) { - gl_context_mutex_.lock(); - } + texture_update_if_needed(); + vertex_buffer_update(params); - if (need_clear_) { - /* Texture is requested to be cleared and was not yet cleared. - * - * Do early return which should be equivalent of drawing all-zero texture. - * Watch out for the lock though so that the clear happening during update is properly - * synchronized here. */ - if (use_gl_context_) { - gl_context_mutex_.unlock(); - } - return; - } - - if (gl_upload_sync_) { - glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); - } - - if (transparent) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } - - glActiveTexture(GL_TEXTURE0); - - /* NOTE: THe VAO is to be allocated on the drawing context as it is not shared across contects. - * Simplest is to allocate it on every redraw so that it is possible to destroy it from a - * correct context. */ + /* TODO(sergey): Does it make sense/possible to cache/reuse the VAO? */ GLuint vertex_array_object; glGenVertexArrays(1, &vertex_array_object); glBindVertexArray(vertex_array_object); - 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); - 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); - } + 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)); - display_shader_->unbind(); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glBindTexture(GL_TEXTURE_2D, 0); - glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); glDeleteVertexArrays(1, &vertex_array_object); + display_shader_->unbind(); + if (transparent) { glDisable(GL_BLEND); } @@ -953,11 +544,6 @@ void BlenderDisplayDriver::draw(const Params ¶ms) gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glFlush(); - if (VLOG_IS_ON(5)) { - VLOG(5) << "Number of textures: " << GLTexture::num_used; - VLOG(5) << "Number of PBOs: " << GLPixelBufferObject::num_used; - } - if (use_gl_context_) { gl_context_mutex_.unlock(); } @@ -1032,16 +618,154 @@ void BlenderDisplayDriver::gl_context_dispose() } } +bool BlenderDisplayDriver::gl_draw_resources_ensure() +{ + if (!texture_.gl_id) { + /* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can + * can not continue. Note that this is not an unrecoverable error, so once the texture is known + * we will come back here and create all the GPU resources needed for draw. */ + return false; + } + + if (gl_draw_resource_creation_attempted_) { + return gl_draw_resources_created_; + } + gl_draw_resource_creation_attempted_ = true; + + if (!vertex_buffer_) { + glGenBuffers(1, &vertex_buffer_); + if (!vertex_buffer_) { + LOG(ERROR) << "Error creating vertex buffer."; + return false; + } + } + + gl_draw_resources_created_ = true; + + return true; +} + void BlenderDisplayDriver::gl_resources_destroy() { gl_context_enable(); - tiles_->current_tile.gl_resources_destroy(); - tiles_->finished_tiles.gl_resources_destroy_and_clear(); + if (vertex_buffer_ != 0) { + glDeleteBuffers(1, &vertex_buffer_); + } + + if (texture_.gl_pbo_id) { + glDeleteBuffers(1, &texture_.gl_pbo_id); + texture_.gl_pbo_id = 0; + } + + if (texture_.gl_id) { + glDeleteTextures(1, &texture_.gl_id); + texture_.gl_id = 0; + } gl_context_disable(); gl_context_dispose(); } +bool BlenderDisplayDriver::gl_texture_resources_ensure() +{ + if (texture_.creation_attempted) { + return texture_.is_created; + } + texture_.creation_attempted = true; + + DCHECK(!texture_.gl_id); + DCHECK(!texture_.gl_pbo_id); + + /* Create texture. */ + glGenTextures(1, &texture_.gl_id); + if (!texture_.gl_id) { + LOG(ERROR) << "Error creating texture."; + return false; + } + + /* Configure the texture. */ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + /* Create PBO for the texture. */ + glGenBuffers(1, &texture_.gl_pbo_id); + if (!texture_.gl_pbo_id) { + LOG(ERROR) << "Error creating texture pixel buffer object."; + return false; + } + + /* Creation finished with a success. */ + texture_.is_created = true; + + return true; +} + +void BlenderDisplayDriver::texture_update_if_needed() +{ + if (!texture_.need_update) { + return; + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + glTexSubImage2D( + GL_TEXTURE_2D, 0, 0, 0, texture_.width, texture_.height, GL_RGBA, GL_HALF_FLOAT, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + texture_.need_update = false; +} + +void BlenderDisplayDriver::vertex_buffer_update(const Params & /*params*/) +{ + /* 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. */ + const int x = texture_.params.full_offset.x; + const int y = texture_.params.full_offset.y; + + const int width = texture_.params.size.x; + const int height = texture_.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); + + float *vpointer = reinterpret_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); + if (!vpointer) { + return; + } + + vpointer[0] = 0.0f; + vpointer[1] = 0.0f; + vpointer[2] = x; + vpointer[3] = y; + + vpointer[4] = 1.0f; + vpointer[5] = 0.0f; + vpointer[6] = x + width; + vpointer[7] = y; + + vpointer[8] = 1.0f; + vpointer[9] = 1.0f; + vpointer[10] = x + width; + vpointer[11] = y + height; + + vpointer[12] = 0.0f; + vpointer[13] = 1.0f; + vpointer[14] = x; + vpointer[15] = y + height; + + glUnmapBuffer(GL_ARRAY_BUFFER); +} + CCL_NAMESPACE_END -- cgit v1.2.3