Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brecht@blender.org>2022-01-06 18:41:44 +0300
committerBrecht Van Lommel <brecht@blender.org>2022-01-07 19:20:04 +0300
commitae28d90578be516bf81f3532846c29f9985f1085 (patch)
treec34a11d1d63829beb4178fcfcd59ab895729d18b /intern/cycles/blender/display_driver.cpp
parentefe3d60a2c8306aefd41bc304548da35b67c252c (diff)
Fix T93350: Cycles renders shows black during rendering huge resolutions
The root of the issue is caused by Cycles ignoring OpenGL limitation on the maximum resolution of textures: Cycles was allocating texture of the final render resolution. It was exceeding limitation on certain GPUs and driver. The idea is simple: use multiple textures for the display, each of which will fit into OpenGL limitations. There is some code which allows the display driver to know when to start the new tile. Also added some code to allow force graphics interop to be re-created. The latter one ended up not used in the final version of the patch, but it might be helpful for other drivers implementation. The tile size is limited to 8K now as it is the safest size for textures on many GPUs and OpenGL drivers. This is an updated fix with a workaround for freezing with the NVIDIA driver on Linux. Differential Revision: https://developer.blender.org/D13385
Diffstat (limited to 'intern/cycles/blender/display_driver.cpp')
-rw-r--r--intern/cycles/blender/display_driver.cpp744
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 &params,
int texture_width,
int texture_height)
@@ -312,24 +615,33 @@ bool BlenderDisplayDriver::update_begin(const Params &params,
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
}
- if (!gl_texture_resources_ensure()) {
+ DrawTile &current_tile = tiles_->current_tile.tile;
+ GLPixelBufferObject &current_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 &params,
* 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 &params)
+/* 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 &params)
{
- /* 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 &params)
*
* 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 &params)
+{
+ /* 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 &params)
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