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:
-rw-r--r--intern/cycles/blender/addon/properties.py2
-rw-r--r--intern/cycles/blender/display_driver.cpp744
-rw-r--r--intern/cycles/blender/display_driver.h69
-rw-r--r--intern/cycles/device/cuda/graphics_interop.cpp6
-rw-r--r--intern/cycles/integrator/path_trace.cpp20
-rw-r--r--intern/cycles/integrator/path_trace.h7
-rw-r--r--intern/cycles/integrator/path_trace_display.cpp16
-rw-r--r--intern/cycles/integrator/path_trace_display.h12
-rw-r--r--intern/cycles/integrator/path_trace_work.cpp8
-rw-r--r--intern/cycles/integrator/path_trace_work_gpu.cpp6
-rw-r--r--intern/cycles/session/display_driver.h16
-rw-r--r--intern/cycles/session/session.cpp7
-rw-r--r--intern/cycles/session/tile.cpp6
-rw-r--r--intern/cycles/session/tile.h6
14 files changed, 619 insertions, 306 deletions
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index a7deae2c05d..f669adb9f37 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=16384,
+ min=8, max=8192,
)
# Various fine-tuning debug flags
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
diff --git a/intern/cycles/blender/display_driver.h b/intern/cycles/blender/display_driver.h
index 66cfc8cffcc..2cf6be0d287 100644
--- a/intern/cycles/blender/display_driver.h
+++ b/intern/cycles/blender/display_driver.h
@@ -26,6 +26,7 @@
#include "util/thread.h"
#include "util/unique_ptr.h"
+#include "util/vector.h"
CCL_NAMESPACE_BEGIN
@@ -112,6 +113,8 @@ 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 &params, int texture_width, int texture_height) override;
virtual void update_end() override;
@@ -122,33 +125,17 @@ class BlenderDisplayDriver : public DisplayDriver {
virtual void draw(const Params &params) override;
+ virtual void flush() override;
+
/* Helper function which allocates new GPU context. */
void gl_context_create();
bool gl_context_enable();
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 &params);
-
BL::RenderEngine b_engine_;
/* OpenGL context which is used the render engine doesn't have its own. */
@@ -159,50 +146,14 @@ class BlenderDisplayDriver : public DisplayDriver {
/* Mutex used to guard the `gl_context_`. */
thread_mutex gl_context_mutex_;
- /* 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_;
+ /* Content of the display is to be filled with zeroes. */
+ std::atomic<bool> need_clear_ = true;
unique_ptr<BlenderDisplayShader> display_shader_;
- /* 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;
+ /* Opaque storage for an internal state and data for tiles. */
+ struct Tiles;
+ unique_ptr<Tiles> tiles_;
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 30efefd9b6b..c75d7957460 100644
--- a/intern/cycles/device/cuda/graphics_interop.cpp
+++ b/intern/cycles/device/cuda/graphics_interop.cpp
@@ -45,8 +45,10 @@ void CUDADeviceGraphicsInterop::set_display_interop(
need_clear_ = display_interop.need_clear;
- if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) {
- return;
+ if (!display_interop.need_recreate) {
+ 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 bdc18b1c0a1..0b55d1078a8 100644
--- a/intern/cycles/integrator/path_trace.cpp
+++ b/intern/cycles/integrator/path_trace.cpp
@@ -115,7 +115,9 @@ bool PathTrace::ready_to_reset()
return false;
}
-void PathTrace::reset(const BufferParams &full_params, const BufferParams &big_tile_params)
+void PathTrace::reset(const BufferParams &full_params,
+ const BufferParams &big_tile_params,
+ const bool reset_rendering)
{
if (big_tile_params_.modified(big_tile_params)) {
big_tile_params_ = big_tile_params;
@@ -128,7 +130,7 @@ void PathTrace::reset(const BufferParams &full_params, const BufferParams &big_t
* It is requires to inform about reset whenever it happens, so that the redraw state tracking is
* properly updated. */
if (display_) {
- display_->reset(full_params);
+ display_->reset(big_tile_params, reset_rendering);
}
render_state_.has_denoised_result = false;
@@ -594,6 +596,15 @@ void PathTrace::draw()
did_draw_after_reset_ |= display_->draw();
}
+void PathTrace::flush_display()
+{
+ if (!display_) {
+ return;
+ }
+
+ display_->flush();
+}
+
void PathTrace::update_display(const RenderWork &render_work)
{
if (!render_work.display.update) {
@@ -622,9 +633,8 @@ void PathTrace::update_display(const RenderWork &render_work)
if (display_) {
VLOG(3) << "Perform copy to GPUDisplay work.";
- 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);
+ const int texture_width = render_state_.effective_big_tile_params.window_width;
+ const int texture_height = render_state_.effective_big_tile_params.window_height;
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 9b079352a63..bb41c8c3210 100644
--- a/intern/cycles/integrator/path_trace.h
+++ b/intern/cycles/integrator/path_trace.h
@@ -72,7 +72,9 @@ class PathTrace {
* render result. */
bool ready_to_reset();
- void reset(const BufferParams &full_params, const BufferParams &big_tile_params);
+ void reset(const BufferParams &full_params,
+ const BufferParams &big_tile_params,
+ bool reset_rendering);
void device_free();
@@ -112,6 +114,9 @@ class PathTrace {
/* Perform drawing of the current state of the DisplayDriver. */
void draw();
+ /* Flush outstanding display commands before ending the render loop. */
+ void flush_display();
+
/* Cancel rendering process as soon as possible, without waiting for full tile to be sampled.
* Used in cases like reset of render session.
*
diff --git a/intern/cycles/integrator/path_trace_display.cpp b/intern/cycles/integrator/path_trace_display.cpp
index c1cade923b1..4af622065c2 100644
--- a/intern/cycles/integrator/path_trace_display.cpp
+++ b/intern/cycles/integrator/path_trace_display.cpp
@@ -26,15 +26,20 @@ PathTraceDisplay::PathTraceDisplay(unique_ptr<DisplayDriver> driver) : driver_(m
{
}
-void PathTraceDisplay::reset(const BufferParams &buffer_params)
+void PathTraceDisplay::reset(const BufferParams &buffer_params, const bool reset_rendering)
{
thread_scoped_lock lock(mutex_);
- params_.full_offset = make_int2(buffer_params.full_x, buffer_params.full_y);
+ params_.full_offset = make_int2(buffer_params.full_x + buffer_params.window_x,
+ buffer_params.full_y + buffer_params.window_y);
params_.full_size = make_int2(buffer_params.full_width, buffer_params.full_height);
- params_.size = make_int2(buffer_params.width, buffer_params.height);
+ params_.size = make_int2(buffer_params.window_width, buffer_params.window_height);
texture_state_.is_outdated = true;
+
+ if (!reset_rendering) {
+ driver_->next_tile_begin();
+ }
}
void PathTraceDisplay::mark_texture_updated()
@@ -248,4 +253,9 @@ bool PathTraceDisplay::draw()
return !is_outdated;
}
+void PathTraceDisplay::flush()
+{
+ driver_->flush();
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/integrator/path_trace_display.h b/intern/cycles/integrator/path_trace_display.h
index 47014f43afa..b9aff5f6000 100644
--- a/intern/cycles/integrator/path_trace_display.h
+++ b/intern/cycles/integrator/path_trace_display.h
@@ -38,14 +38,17 @@ class BufferParams;
class PathTraceDisplay {
public:
- PathTraceDisplay(unique_ptr<DisplayDriver> driver);
+ explicit 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. */
- void reset(const BufferParams &buffer_params);
+ * 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);
/* --------------------------------------------------------------------
* Update procedure.
@@ -151,6 +154,9 @@ class PathTraceDisplay {
* Returns true if this call did draw an updated state of the texture. */
bool draw();
+ /* Flush outstanding display commands before ending the render loop. */
+ void flush();
+
private:
/* Display driver implemented by the host application. */
unique_ptr<DisplayDriver> driver_;
diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp
index b0c40cfe15c..4ecc7d775ee 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_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;
+ 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;
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 e5062c6c47e..7a13447f2cf 100644
--- a/intern/cycles/integrator/path_trace_work_gpu.cpp
+++ b/intern/cycles/integrator/path_trace_work_gpu.cpp
@@ -875,8 +875,10 @@ 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_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;
+ 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;
/* 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 77f89326fd0..c56a82436d9 100644
--- a/intern/cycles/session/display_driver.h
+++ b/intern/cycles/session/display_driver.h
@@ -54,6 +54,8 @@ 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
@@ -80,6 +82,9 @@ class DisplayDriver {
virtual bool update_begin(const Params &params, int width, int height) = 0;
virtual void update_end() = 0;
+ /* Optionally flush outstanding display commands before ending the render loop. */
+ virtual void flush(){};
+
virtual half4 *map_texture_buffer() = 0;
virtual void unmap_texture_buffer() = 0;
@@ -97,6 +102,17 @@ 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 af5c6b3f1fd..a839303debc 100644
--- a/intern/cycles/session/session.cpp
+++ b/intern/cycles/session/session.cpp
@@ -192,6 +192,8 @@ void Session::run_main_render_loop()
break;
}
}
+
+ path_trace_->flush_display();
}
void Session::run()
@@ -303,7 +305,7 @@ RenderWork Session::run_update_for_next_iteration()
tile_params.update_offset_stride();
- path_trace_->reset(buffer_params_, tile_params);
+ path_trace_->reset(buffer_params_, tile_params, did_reset);
}
const int resolution = render_work.resolution_divider;
@@ -384,7 +386,8 @@ 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) {
+ if (actual_tile_area >= image_area && image_width <= TileManager::MAX_TILE_SIZE &&
+ image_height <= TileManager::MAX_TILE_SIZE) {
return make_int2(image_width, image_height);
}
diff --git a/intern/cycles/session/tile.cpp b/intern/cycles/session/tile.cpp
index 3b8482fa16f..afd1f334120 100644
--- a/intern/cycles/session/tile.cpp
+++ b/intern/cycles/session/tile.cpp
@@ -341,8 +341,10 @@ 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. */
- return (suggested_tile_size <= IMAGE_TILE_SIZE) ? suggested_tile_size :
- align_up(suggested_tile_size, IMAGE_TILE_SIZE);
+ 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);
}
void TileManager::reset_scheduling(const BufferParams &params, int2 tile_size)
diff --git a/intern/cycles/session/tile.h b/intern/cycles/session/tile.h
index eace148eb0a..7c8f7570d3e 100644
--- a/intern/cycles/session/tile.h
+++ b/intern/cycles/session/tile.h
@@ -122,6 +122,12 @@ 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_). */