diff options
Diffstat (limited to 'intern/cycles/integrator/path_trace_display.cpp')
-rw-r--r-- | intern/cycles/integrator/path_trace_display.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/intern/cycles/integrator/path_trace_display.cpp b/intern/cycles/integrator/path_trace_display.cpp new file mode 100644 index 00000000000..28f0a7f7745 --- /dev/null +++ b/intern/cycles/integrator/path_trace_display.cpp @@ -0,0 +1,268 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace_display.h" + +#include "render/buffers.h" + +#include "util/util_logging.h" + +CCL_NAMESPACE_BEGIN + +PathTraceDisplay::PathTraceDisplay(unique_ptr<DisplayDriver> driver) : driver_(move(driver)) +{ +} + +void PathTraceDisplay::reset(const BufferParams &buffer_params) +{ + thread_scoped_lock lock(mutex_); + + const DisplayDriver::Params old_params = params_; + + 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.width, buffer_params.height); + + /* If the parameters did change tag texture as unusable. This avoids drawing old texture content + * in an updated configuration of the viewport. For example, avoids drawing old frame when render + * border did change. + * If the parameters did not change, allow drawing the current state of the texture, which will + * not count as an up-to-date redraw. This will avoid flickering when doping camera navigation by + * showing a previously rendered frame for until the new one is ready. */ + if (old_params.modified(params_)) { + texture_state_.is_usable = false; + } + + texture_state_.is_outdated = true; +} + +void PathTraceDisplay::mark_texture_updated() +{ + texture_state_.is_outdated = false; + texture_state_.is_usable = true; +} + +/* -------------------------------------------------------------------- + * Update procedure. + */ + +bool PathTraceDisplay::update_begin(int texture_width, int texture_height) +{ + DCHECK(!update_state_.is_active); + + if (update_state_.is_active) { + LOG(ERROR) << "Attempt to re-activate update process."; + return false; + } + + /* Get parameters within a mutex lock, to avoid reset() modifying them at the same time. + * The update itself is non-blocking however, for better performance and to avoid + * potential deadlocks due to locks held by the subclass. */ + DisplayDriver::Params params; + { + thread_scoped_lock lock(mutex_); + params = params_; + texture_state_.size = make_int2(texture_width, texture_height); + } + + if (!driver_->update_begin(params, texture_width, texture_height)) { + LOG(ERROR) << "PathTraceDisplay implementation could not begin update."; + return false; + } + + update_state_.is_active = true; + + return true; +} + +void PathTraceDisplay::update_end() +{ + DCHECK(update_state_.is_active); + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to deactivate inactive update process."; + return; + } + + driver_->update_end(); + + update_state_.is_active = false; +} + +int2 PathTraceDisplay::get_texture_size() const +{ + return texture_state_.size; +} + +/* -------------------------------------------------------------------- + * Texture update from CPU buffer. + */ + +void PathTraceDisplay::copy_pixels_to_texture( + const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height) +{ + DCHECK(update_state_.is_active); + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update."; + return; + } + + mark_texture_updated(); + + /* This call copies pixels to a mapped texture buffer which is typically much cheaper from CPU + * time point of view than to copy data directly to a texture. + * + * The possible downside of this approach is that it might require a higher peak memory when + * doing partial updates of the texture (although, in practice even partial updates might peak + * with a full-frame buffer stored on the CPU if the GPU is currently occupied). */ + half4 *mapped_rgba_pixels = map_texture_buffer(); + if (!mapped_rgba_pixels) { + return; + } + + const int texture_width = texture_state_.size.x; + const int texture_height = texture_state_.size.y; + + if (texture_x == 0 && texture_y == 0 && pixels_width == texture_width && + pixels_height == texture_height) { + const size_t size_in_bytes = sizeof(half4) * texture_width * texture_height; + memcpy(mapped_rgba_pixels, rgba_pixels, size_in_bytes); + } + else { + const half4 *rgba_row = rgba_pixels; + half4 *mapped_rgba_row = mapped_rgba_pixels + texture_y * texture_width + texture_x; + for (int y = 0; y < pixels_height; + ++y, rgba_row += pixels_width, mapped_rgba_row += texture_width) { + memcpy(mapped_rgba_row, rgba_row, sizeof(half4) * pixels_width); + } + } + + unmap_texture_buffer(); +} + +/* -------------------------------------------------------------------- + * Texture buffer mapping. + */ + +half4 *PathTraceDisplay::map_texture_buffer() +{ + DCHECK(!texture_buffer_state_.is_mapped); + DCHECK(update_state_.is_active); + + if (texture_buffer_state_.is_mapped) { + LOG(ERROR) << "Attempt to re-map an already mapped texture buffer."; + return nullptr; + } + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update."; + return nullptr; + } + + half4 *mapped_rgba_pixels = driver_->map_texture_buffer(); + + if (mapped_rgba_pixels) { + texture_buffer_state_.is_mapped = true; + } + + return mapped_rgba_pixels; +} + +void PathTraceDisplay::unmap_texture_buffer() +{ + DCHECK(texture_buffer_state_.is_mapped); + + if (!texture_buffer_state_.is_mapped) { + LOG(ERROR) << "Attempt to unmap non-mapped texture buffer."; + return; + } + + texture_buffer_state_.is_mapped = false; + + mark_texture_updated(); + driver_->unmap_texture_buffer(); +} + +/* -------------------------------------------------------------------- + * Graphics interoperability. + */ + +DisplayDriver::GraphicsInterop PathTraceDisplay::graphics_interop_get() +{ + DCHECK(!texture_buffer_state_.is_mapped); + DCHECK(update_state_.is_active); + + if (texture_buffer_state_.is_mapped) { + LOG(ERROR) + << "Attempt to use graphics interoperability mode while the texture buffer is mapped."; + return DisplayDriver::GraphicsInterop(); + } + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to use graphics interoperability outside of PathTraceDisplay update."; + return DisplayDriver::GraphicsInterop(); + } + + /* Assume that interop will write new values to the texture. */ + mark_texture_updated(); + + return driver_->graphics_interop_get(); +} + +void PathTraceDisplay::graphics_interop_activate() +{ + driver_->graphics_interop_activate(); +} + +void PathTraceDisplay::graphics_interop_deactivate() +{ + driver_->graphics_interop_deactivate(); +} + +/* -------------------------------------------------------------------- + * Drawing. + */ + +void PathTraceDisplay::clear() +{ + driver_->clear(); +} + +bool PathTraceDisplay::draw() +{ + /* Get parameters within a mutex lock, to avoid reset() modifying them at the same time. + * The drawing itself is non-blocking however, for better performance and to avoid + * potential deadlocks due to locks held by the subclass. */ + DisplayDriver::Params params; + bool is_usable; + bool is_outdated; + + { + thread_scoped_lock lock(mutex_); + params = params_; + is_usable = texture_state_.is_usable; + is_outdated = texture_state_.is_outdated; + } + + if (is_usable) { + driver_->draw(params); + } + + return !is_outdated; +} + +CCL_NAMESPACE_END |