diff options
author | Brecht Van Lommel <brecht@blender.org> | 2021-12-07 22:49:34 +0300 |
---|---|---|
committer | Brecht Van Lommel <brecht@blender.org> | 2021-12-07 22:49:34 +0300 |
commit | 204ae33d755561e68ad3a6193d9a90d39d47d4da (patch) | |
tree | b83e69f4b4deb024e848b2c45a346409779df26b | |
parent | b81508841639aff7c0bc04accc2be265f554fdce (diff) |
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.
-rw-r--r-- | intern/cycles/blender/addon/properties.py | 2 | ||||
-rw-r--r-- | intern/cycles/blender/display_driver.cpp | 720 | ||||
-rw-r--r-- | intern/cycles/blender/display_driver.h | 67 | ||||
-rw-r--r-- | intern/cycles/device/cuda/graphics_interop.cpp | 6 | ||||
-rw-r--r-- | intern/cycles/integrator/path_trace.cpp | 11 | ||||
-rw-r--r-- | intern/cycles/integrator/path_trace.h | 4 | ||||
-rw-r--r-- | intern/cycles/integrator/path_trace_display.cpp | 11 | ||||
-rw-r--r-- | intern/cycles/integrator/path_trace_display.h | 9 | ||||
-rw-r--r-- | intern/cycles/integrator/path_trace_work.cpp | 8 | ||||
-rw-r--r-- | intern/cycles/integrator/path_trace_work_gpu.cpp | 6 | ||||
-rw-r--r-- | intern/cycles/session/display_driver.h | 13 | ||||
-rw-r--r-- | intern/cycles/session/session.cpp | 5 | ||||
-rw-r--r-- | intern/cycles/session/tile.cpp | 6 | ||||
-rw-r--r-- | intern/cycles/session/tile.h | 6 |
14 files changed, 306 insertions, 568 deletions
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 42f1e8f6f1a..0b1a86dfdc1 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -802,7 +802,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): name="Tile Size", default=2048, description="", - min=8, max=8192, + min=8, max=16384, ) # Various fine-tuning debug flags 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 @@ -273,299 +273,11 @@ 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)), - tiles_(make_unique<Tiles>()) + : 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<half4 *>( 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<void *>(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<float *>(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<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 diff --git a/intern/cycles/blender/display_driver.h b/intern/cycles/blender/display_driver.h index b9773f5eadf..66cfc8cffcc 100644 --- a/intern/cycles/blender/display_driver.h +++ b/intern/cycles/blender/display_driver.h @@ -26,7 +26,6 @@ #include "util/thread.h" #include "util/unique_ptr.h" -#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -113,8 +112,6 @@ class BlenderDisplayDriver : public DisplayDriver { void set_zoom(float zoom_x, float zoom_y); protected: - virtual void next_tile_begin() override; - virtual bool update_begin(const Params ¶ms, int texture_width, int texture_height) override; virtual void update_end() override; @@ -131,9 +128,27 @@ class BlenderDisplayDriver : public DisplayDriver { void gl_context_disable(); void gl_context_dispose(); + /* Make sure texture is allocated and its initial configuration is performed. */ + bool gl_texture_resources_ensure(); + + /* Ensure all runtime GPU resources needed for drawing are allocated. + * Returns true if all resources needed for drawing are available. */ + bool gl_draw_resources_ensure(); + /* Destroy all GPU resources which are being used by this object. */ void gl_resources_destroy(); + /* Update GPU texture dimensions and content if needed (new pixel data was provided). + * + * NOTE: The texture needs to be bound. */ + void texture_update_if_needed(); + + /* 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. */ + void vertex_buffer_update(const Params ¶ms); + BL::RenderEngine b_engine_; /* OpenGL context which is used the render engine doesn't have its own. */ @@ -144,14 +159,50 @@ class BlenderDisplayDriver : public DisplayDriver { /* Mutex used to guard the `gl_context_`. */ thread_mutex gl_context_mutex_; - /* Content of the display is to be filled with zeroes. */ - std::atomic<bool> need_clear_ = true; + /* Texture which contains pixels of the render result. */ + struct { + /* Indicates whether texture creation was attempted and succeeded. + * Used to avoid multiple attempts of texture creation on GPU issues or GPU context + * misconfiguration. */ + bool creation_attempted = false; + bool is_created = false; + + /* OpenGL resource IDs of the texture itself and Pixel Buffer Object (PBO) used to write + * pixels to it. + * + * NOTE: Allocated on the engine's context. */ + uint gl_id = 0; + uint gl_pbo_id = 0; + + /* Is true when new data was written to the PBO, meaning, the texture might need to be resized + * and new data is to be uploaded to the GPU. */ + bool need_update = false; + + /* Content of the texture is to be filled with zeroes. */ + std::atomic<bool> need_clear = true; + + /* Dimensions of the texture in pixels. */ + int width = 0; + int height = 0; + + /* Dimensions of the underlying PBO. */ + int buffer_width = 0; + int buffer_height = 0; + + /* Display parameters the texture has been updated for. */ + Params params; + } texture_; unique_ptr<BlenderDisplayShader> display_shader_; - /* Opaque storage for an internal state and data for tiles. */ - struct Tiles; - unique_ptr<Tiles> tiles_; + /* Special track of whether GPU resources were attempted to be created, to avoid attempts of + * their re-creation on failure on every redraw. */ + bool gl_draw_resource_creation_attempted_ = false; + bool gl_draw_resources_created_ = false; + + /* Vertex buffer which hold vertices of a triangle fan which is textures with the texture + * holding the render result. */ + uint vertex_buffer_ = 0; void *gl_render_sync_ = nullptr; void *gl_upload_sync_ = nullptr; diff --git a/intern/cycles/device/cuda/graphics_interop.cpp b/intern/cycles/device/cuda/graphics_interop.cpp index c75d7957460..30efefd9b6b 100644 --- a/intern/cycles/device/cuda/graphics_interop.cpp +++ b/intern/cycles/device/cuda/graphics_interop.cpp @@ -45,10 +45,8 @@ void CUDADeviceGraphicsInterop::set_display_interop( need_clear_ = display_interop.need_clear; - if (!display_interop.need_recreate) { - if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) { - return; - } + if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) { + return; } CUDAContextScope scope(device_); diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 28a012c2506..bdc18b1c0a1 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -115,9 +115,7 @@ bool PathTrace::ready_to_reset() return false; } -void PathTrace::reset(const BufferParams &full_params, - const BufferParams &big_tile_params, - const bool reset_rendering) +void PathTrace::reset(const BufferParams &full_params, const BufferParams &big_tile_params) { if (big_tile_params_.modified(big_tile_params)) { big_tile_params_ = big_tile_params; @@ -130,7 +128,7 @@ void PathTrace::reset(const BufferParams &full_params, * It is requires to inform about reset whenever it happens, so that the redraw state tracking is * properly updated. */ if (display_) { - display_->reset(big_tile_params, reset_rendering); + display_->reset(full_params); } render_state_.has_denoised_result = false; @@ -624,8 +622,9 @@ void PathTrace::update_display(const RenderWork &render_work) if (display_) { VLOG(3) << "Perform copy to GPUDisplay work."; - const int texture_width = render_state_.effective_big_tile_params.window_width; - const int texture_height = render_state_.effective_big_tile_params.window_height; + const int resolution_divider = render_work.resolution_divider; + const int texture_width = max(1, full_params_.width / resolution_divider); + const int texture_height = max(1, full_params_.height / resolution_divider); if (!display_->update_begin(texture_width, texture_height)) { LOG(ERROR) << "Error beginning GPUDisplay update."; return; diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index 1ae48f82810..9b079352a63 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -72,9 +72,7 @@ class PathTrace { * render result. */ bool ready_to_reset(); - void reset(const BufferParams &full_params, - const BufferParams &big_tile_params, - bool reset_rendering); + void reset(const BufferParams &full_params, const BufferParams &big_tile_params); void device_free(); diff --git a/intern/cycles/integrator/path_trace_display.cpp b/intern/cycles/integrator/path_trace_display.cpp index d6279f3298c..c1cade923b1 100644 --- a/intern/cycles/integrator/path_trace_display.cpp +++ b/intern/cycles/integrator/path_trace_display.cpp @@ -26,20 +26,15 @@ PathTraceDisplay::PathTraceDisplay(unique_ptr<DisplayDriver> driver) : driver_(m { } -void PathTraceDisplay::reset(const BufferParams &buffer_params, const bool reset_rendering) +void PathTraceDisplay::reset(const BufferParams &buffer_params) { thread_scoped_lock lock(mutex_); - params_.full_offset = make_int2(buffer_params.full_x + buffer_params.window_x, - buffer_params.full_y + buffer_params.window_y); + params_.full_offset = make_int2(buffer_params.full_x, buffer_params.full_y); params_.full_size = make_int2(buffer_params.full_width, buffer_params.full_height); - params_.size = make_int2(buffer_params.window_width, buffer_params.window_height); + params_.size = make_int2(buffer_params.width, buffer_params.height); texture_state_.is_outdated = true; - - if (!reset_rendering) { - driver_->next_tile_begin(); - } } void PathTraceDisplay::mark_texture_updated() diff --git a/intern/cycles/integrator/path_trace_display.h b/intern/cycles/integrator/path_trace_display.h index be301e55359..47014f43afa 100644 --- a/intern/cycles/integrator/path_trace_display.h +++ b/intern/cycles/integrator/path_trace_display.h @@ -38,17 +38,14 @@ class BufferParams; class PathTraceDisplay { public: - explicit PathTraceDisplay(unique_ptr<DisplayDriver> driver); + PathTraceDisplay(unique_ptr<DisplayDriver> driver); virtual ~PathTraceDisplay() = default; /* Reset the display for the new state of render session. Is called whenever session is reset, * which happens on changes like viewport navigation or viewport dimension change. * - * This call will configure parameters for a changed buffer and reset the texture state. - * - * When the `reset_rendering` a complete displat reset happens. When it is false reset happens - * for a new state of the buffer parameters which is assumed to correspond to the next tile. */ - void reset(const BufferParams &buffer_params, bool reset_rendering); + * This call will configure parameters for a changed buffer and reset the texture state. */ + void reset(const BufferParams &buffer_params); /* -------------------------------------------------------------------- * Update procedure. diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp index 4ecc7d775ee..b0c40cfe15c 100644 --- a/intern/cycles/integrator/path_trace_work.cpp +++ b/intern/cycles/integrator/path_trace_work.cpp @@ -194,10 +194,10 @@ PassAccessor::Destination PathTraceWork::get_display_destination_template( PassAccessor::Destination destination(film_->get_display_pass()); const int2 display_texture_size = display->get_texture_size(); - const int texture_x = effective_buffer_params_.full_x - effective_big_tile_params_.full_x + - effective_buffer_params_.window_x - effective_big_tile_params_.window_x; - const int texture_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y + - effective_buffer_params_.window_y - effective_big_tile_params_.window_y; + const int texture_x = effective_buffer_params_.full_x - effective_full_params_.full_x + + effective_buffer_params_.window_x; + const int texture_y = effective_buffer_params_.full_y - effective_full_params_.full_y + + effective_buffer_params_.window_y; destination.offset = texture_y * display_texture_size.x + texture_x; destination.stride = display_texture_size.x; diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp index 7a13447f2cf..e5062c6c47e 100644 --- a/intern/cycles/integrator/path_trace_work_gpu.cpp +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -875,10 +875,8 @@ void PathTraceWorkGPU::copy_to_display_naive(PathTraceDisplay *display, const int final_width = buffers_->params.window_width; const int final_height = buffers_->params.window_height; - const int texture_x = full_x - effective_big_tile_params_.full_x + - effective_buffer_params_.window_x - effective_big_tile_params_.window_x; - const int texture_y = full_y - effective_big_tile_params_.full_y + - effective_buffer_params_.window_y - effective_big_tile_params_.window_y; + const int texture_x = full_x - effective_full_params_.full_x + effective_buffer_params_.window_x; + const int texture_y = full_y - effective_full_params_.full_y + effective_buffer_params_.window_y; /* Re-allocate display memory if needed, and make sure the device pointer is allocated. * diff --git a/intern/cycles/session/display_driver.h b/intern/cycles/session/display_driver.h index 5544ceee83d..77f89326fd0 100644 --- a/intern/cycles/session/display_driver.h +++ b/intern/cycles/session/display_driver.h @@ -54,8 +54,6 @@ class DisplayDriver { } }; - virtual void next_tile_begin() = 0; - /* Update the render from the rendering thread. * * Cycles periodically updates the render to be displayed. For multithreaded updates with @@ -99,17 +97,6 @@ class DisplayDriver { /* Clear the entire buffer before doing partial write to it. */ bool need_clear = false; - - /* Enforce re-creation of the graphics interop object. - * - * When this field is true then the graphics interop will be re-created no matter what the - * rest of the configuration is. - * When this field is false the graphics interop will be re-created if the PBO or buffer size - * did change. - * - * This allows to ensure graphics interop is re-created when there is a possibility that an - * underlying PBO was re-allocated but did not change its ID. */ - bool need_recreate = false; }; virtual GraphicsInterop graphics_interop_get() diff --git a/intern/cycles/session/session.cpp b/intern/cycles/session/session.cpp index 4d8e3dfbfad..af5c6b3f1fd 100644 --- a/intern/cycles/session/session.cpp +++ b/intern/cycles/session/session.cpp @@ -303,7 +303,7 @@ RenderWork Session::run_update_for_next_iteration() tile_params.update_offset_stride(); - path_trace_->reset(buffer_params_, tile_params, did_reset); + path_trace_->reset(buffer_params_, tile_params); } const int resolution = render_work.resolution_divider; @@ -384,8 +384,7 @@ int2 Session::get_effective_tile_size() const const int tile_size = tile_manager_.compute_render_tile_size(params.tile_size); const int64_t actual_tile_area = static_cast<int64_t>(tile_size) * tile_size; - if (actual_tile_area >= image_area && image_width <= TileManager::MAX_TILE_SIZE && - image_height <= TileManager::MAX_TILE_SIZE) { + if (actual_tile_area >= image_area) { return make_int2(image_width, image_height); } diff --git a/intern/cycles/session/tile.cpp b/intern/cycles/session/tile.cpp index afd1f334120..3b8482fa16f 100644 --- a/intern/cycles/session/tile.cpp +++ b/intern/cycles/session/tile.cpp @@ -341,10 +341,8 @@ int TileManager::compute_render_tile_size(const int suggested_tile_size) const /* Must be a multiple of IMAGE_TILE_SIZE so that we can write render tiles into the image file * aligned on image tile boundaries. We can't set IMAGE_TILE_SIZE equal to the render tile size * because too big tile size leads to integer overflow inside OpenEXR. */ - const int computed_tile_size = (suggested_tile_size <= IMAGE_TILE_SIZE) ? - suggested_tile_size : - align_up(suggested_tile_size, IMAGE_TILE_SIZE); - return min(computed_tile_size, MAX_TILE_SIZE); + return (suggested_tile_size <= IMAGE_TILE_SIZE) ? suggested_tile_size : + align_up(suggested_tile_size, IMAGE_TILE_SIZE); } void TileManager::reset_scheduling(const BufferParams ¶ms, int2 tile_size) diff --git a/intern/cycles/session/tile.h b/intern/cycles/session/tile.h index 7c8f7570d3e..eace148eb0a 100644 --- a/intern/cycles/session/tile.h +++ b/intern/cycles/session/tile.h @@ -122,12 +122,6 @@ class TileManager { /* Tile size in the image file. */ static const int IMAGE_TILE_SIZE = 128; - /* Maximum supported tile size. - * Needs to be safe from allocation on a GPU point of view: the display driver needs to be able - * to allocate texture with the side size of this value. - * Use conservative value which is safe for most of OpenGL drivers and GPUs. */ - static const int MAX_TILE_SIZE = 8192; - protected: /* Get tile configuration for its index. * The tile index must be within [0, state_.tile_state_). */ |