diff options
Diffstat (limited to 'intern/cycles/blender/display_driver.cpp')
-rw-r--r-- | intern/cycles/blender/display_driver.cpp | 744 |
1 files changed, 522 insertions, 222 deletions
diff --git a/intern/cycles/blender/display_driver.cpp b/intern/cycles/blender/display_driver.cpp index abf421983b3..7524a3adf37 100644 --- a/intern/cycles/blender/display_driver.cpp +++ b/intern/cycles/blender/display_driver.cpp @@ -273,11 +273,299 @@ uint BlenderDisplaySpaceShader::get_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<int> 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<int> 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<DrawTile> 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)) + : b_engine_(b_engine), + display_shader_(BlenderDisplayShader::create(b_engine, b_scene)), + tiles_(make_unique<Tiles>()) { /* Create context while on the main thread. */ gl_context_create(); @@ -292,6 +580,21 @@ 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) @@ -312,24 +615,33 @@ bool BlenderDisplayDriver::update_begin(const Params ¶ms, glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED); } - if (!gl_texture_resources_ensure()) { + 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(); gl_context_disable(); return false; } /* Update texture dimensions if needed. */ - if (texture_.width != texture_width || texture_.height != texture_height) { + if (current_tile.texture.width != texture_width || + current_tile.texture.height != texture_height) { glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + 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); - texture_.width = texture_width; - texture_.height = texture_height; + current_tile.texture.width = texture_width; + current_tile.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. @@ -341,29 +653,58 @@ 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.full_size.x; - const int buffer_height = params.full_size.y; - if (texture_.buffer_width != buffer_width || texture_.buffer_height != buffer_height) { + 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, texture_.gl_pbo_id); + 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); - texture_.buffer_width = buffer_width; - texture_.buffer_height = buffer_height; + current_tile_buffer_object.width = buffer_width; + current_tile_buffer_object.height = buffer_height; } - /* New content will be provided to the texture in one way or another, so mark this in a - * centralized place. */ - texture_.need_update = true; - - texture_.params = params; + /* 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); + + 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); +} + 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(); @@ -376,7 +717,11 @@ void BlenderDisplayDriver::update_end() half4 *BlenderDisplayDriver::map_texture_buffer() { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + 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<half4 *>( glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); @@ -384,15 +729,6 @@ 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<void *>(mapped_rgba_pixels), - 0, - texture_width * texture_height * sizeof(half4)); - texture_.need_clear = false; - } - return mapped_rgba_pixels; } @@ -411,12 +747,9 @@ BlenderDisplayDriver::GraphicsInterop BlenderDisplayDriver::graphics_interop_get { GraphicsInterop interop_dst; - 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; + 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; return interop_dst; } @@ -437,7 +770,7 @@ void BlenderDisplayDriver::graphics_interop_deactivate() void BlenderDisplayDriver::clear() { - texture_.need_clear = true; + need_clear_ = true; } void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y) @@ -445,42 +778,78 @@ void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y) zoom_ = make_float2(zoom_x, zoom_y); } -void BlenderDisplayDriver::draw(const Params ¶ms) +/* 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) { - /* See do_update_begin() for why no locking is required here. */ - const bool transparent = true; // TODO(sergey): Derive this from Film. + 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); - if (!gl_draw_resources_ensure()) { + float *vpointer = reinterpret_cast<float *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); + if (!vpointer) { return; } - if (use_gl_context_) { - gl_context_mutex_.lock(); - } + vpointer[0] = 0.0f; + vpointer[1] = 0.0f; + vpointer[2] = x; + vpointer[3] = y; - 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(); + 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); +} + +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()) { return; } - if (gl_upload_sync_) { - glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); - } + const GLTexture &texture = draw_tile.texture; - if (transparent) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } + DCHECK_NE(texture.gl_id, 0); + DCHECK_NE(draw_tile.gl_vertex_buffer, 0); - display_shader_->bind(params.full_size.x, params.full_size.y); + glBindBuffer(GL_ARRAY_BUFFER, draw_tile.gl_vertex_buffer); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + /* 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); /* Trick to keep sharp rendering without jagged edges on all GPUs. * @@ -490,53 +859,117 @@ void BlenderDisplayDriver::draw(const Params ¶ms) * * 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 = 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) { + 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) { /* Resolution divider is different from 1, force nearest interpolation. */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } - else if (zoomed_width - params.size.x > 0.5f || zoomed_height - params.size.y > 0.5f) { + 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); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); + 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); +} + +void BlenderDisplayDriver::flush() +{ + /* This is called from the render thread that also calls update_begin/end, right before ending + * the render loop. We wait for any queued PBO and render commands to be done, before destroying + * the render thread and activating the context in the main thread to destroy resources. + * + * If we don't do this, the NVIDIA driver hangs for a few seconds for when ending 3D viewport + * rendering, for unknown reasons. This was found with NVIDIA driver version 470.73 and a Quadro + * RTX 6000 on Linux. */ + if (!gl_context_enable()) { + 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); + } + + gl_context_disable(); +} + +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(); + } + + 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); + } - texture_update_if_needed(); - vertex_buffer_update(params); + glActiveTexture(GL_TEXTURE0); - /* TODO(sergey): Does it make sense/possible to cache/reuse the VAO? */ + /* 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. */ 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); - 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)); + draw_tile(zoom_, texcoord_attribute, position_attribute, tiles_->current_tile.tile); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + for (const DrawTile &tile : tiles_->finished_tiles.tiles) { + draw_tile(zoom_, texcoord_attribute, position_attribute, tile); + } + + display_shader_->unbind(); - glBindBuffer(GL_ARRAY_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, 0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); glDeleteVertexArrays(1, &vertex_array_object); - display_shader_->unbind(); - if (transparent) { glDisable(GL_BLEND); } @@ -544,6 +977,11 @@ 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(); } @@ -618,154 +1056,16 @@ 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(); - 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; - } + tiles_->current_tile.gl_resources_destroy(); + tiles_->finished_tiles.gl_resources_destroy_and_clear(); 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<float *>(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 |