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>2021-09-14 16:37:47 +0300
committerBrecht Van Lommel <brecht@blender.org>2021-09-30 21:48:08 +0300
commita754e35198d852ea34e2b82cd2b126538e6f5a3b (patch)
tree9118b3fa19ab70aa1b50440ce62e5d028d940cfd /intern/cycles/integrator
parentac582056e2e70f3b0d91ff69d0307dd357e2e2ed (diff)
Cycles: refactor API for GPU display
* Split GPUDisplay into two classes. PathTraceDisplay to implement the Cycles side, and DisplayDriver to implement the host application side. The DisplayDriver is now a fully abstract base class, embedded in the PathTraceDisplay. * Move copy_pixels_to_texture implementation out of the host side into the Cycles side, since it can be implemented in terms of the texture buffer mapping. * Move definition of DeviceGraphicsInteropDestination into display driver header, so that we do not need to expose private device headers in the public API. * Add more detailed comments about how the DisplayDriver should be implemented. The "driver" terminology might not be obvious, but is also used in other renderers. Differential Revision: https://developer.blender.org/D12626
Diffstat (limited to 'intern/cycles/integrator')
-rw-r--r--intern/cycles/integrator/CMakeLists.txt2
-rw-r--r--intern/cycles/integrator/path_trace.cpp47
-rw-r--r--intern/cycles/integrator/path_trace.h15
-rw-r--r--intern/cycles/integrator/path_trace_display.cpp268
-rw-r--r--intern/cycles/integrator/path_trace_display.h201
-rw-r--r--intern/cycles/integrator/path_trace_work.cpp8
-rw-r--r--intern/cycles/integrator/path_trace_work.h12
-rw-r--r--intern/cycles/integrator/path_trace_work_cpu.cpp20
-rw-r--r--intern/cycles/integrator/path_trace_work_cpu.h8
-rw-r--r--intern/cycles/integrator/path_trace_work_gpu.cpp56
-rw-r--r--intern/cycles/integrator/path_trace_work_gpu.h24
-rw-r--r--intern/cycles/integrator/render_scheduler.h2
12 files changed, 568 insertions, 95 deletions
diff --git a/intern/cycles/integrator/CMakeLists.txt b/intern/cycles/integrator/CMakeLists.txt
index bfabd35d7c3..8acd72f0508 100644
--- a/intern/cycles/integrator/CMakeLists.txt
+++ b/intern/cycles/integrator/CMakeLists.txt
@@ -27,6 +27,7 @@ set(SRC
pass_accessor.cpp
pass_accessor_cpu.cpp
pass_accessor_gpu.cpp
+ path_trace_display.cpp
path_trace_work.cpp
path_trace_work_cpu.cpp
path_trace_work_gpu.cpp
@@ -47,6 +48,7 @@ set(SRC_HEADERS
pass_accessor.h
pass_accessor_cpu.h
pass_accessor_gpu.h
+ path_trace_display.h
path_trace_work.h
path_trace_work_cpu.h
path_trace_work_gpu.h
diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp
index 9633d3b87d3..36cd7314b4c 100644
--- a/intern/cycles/integrator/path_trace.cpp
+++ b/intern/cycles/integrator/path_trace.cpp
@@ -19,8 +19,8 @@
#include "device/cpu/device.h"
#include "device/device.h"
#include "integrator/pass_accessor.h"
+#include "integrator/path_trace_display.h"
#include "integrator/render_scheduler.h"
-#include "render/gpu_display.h"
#include "render/pass.h"
#include "render/scene.h"
#include "render/tile.h"
@@ -67,11 +67,11 @@ PathTrace::PathTrace(Device *device,
PathTrace::~PathTrace()
{
/* Destroy any GPU resource which was used for graphics interop.
- * Need to have access to the GPUDisplay as it is the only source of drawing context which is
- * used for interop. */
- if (gpu_display_) {
+ * Need to have access to the PathTraceDisplay as it is the only source of drawing context which
+ * is used for interop. */
+ if (display_) {
for (auto &&path_trace_work : path_trace_works_) {
- path_trace_work->destroy_gpu_resources(gpu_display_.get());
+ path_trace_work->destroy_gpu_resources(display_.get());
}
}
}
@@ -94,7 +94,7 @@ bool PathTrace::ready_to_reset()
{
/* The logic here is optimized for the best feedback in the viewport, which implies having a GPU
* display. Of there is no such display, the logic here will break. */
- DCHECK(gpu_display_);
+ DCHECK(display_);
/* The logic here tries to provide behavior which feels the most interactive feel to artists.
* General idea is to be able to reset as quickly as possible, while still providing interactive
@@ -126,8 +126,8 @@ void PathTrace::reset(const BufferParams &full_params, const BufferParams &big_t
/* NOTE: GPU display checks for buffer modification and avoids unnecessary re-allocation.
* It is requires to inform about reset whenever it happens, so that the redraw state tracking is
* properly updated. */
- if (gpu_display_) {
- gpu_display_->reset(full_params);
+ if (display_) {
+ display_->reset(full_params);
}
render_state_.has_denoised_result = false;
@@ -535,25 +535,30 @@ void PathTrace::denoise(const RenderWork &render_work)
render_scheduler_.report_denoise_time(render_work, time_dt() - start_time);
}
-void PathTrace::set_gpu_display(unique_ptr<GPUDisplay> gpu_display)
+void PathTrace::set_display_driver(unique_ptr<DisplayDriver> driver)
{
- gpu_display_ = move(gpu_display);
+ if (driver) {
+ display_ = make_unique<PathTraceDisplay>(move(driver));
+ }
+ else {
+ display_ = nullptr;
+ }
}
-void PathTrace::clear_gpu_display()
+void PathTrace::clear_display()
{
- if (gpu_display_) {
- gpu_display_->clear();
+ if (display_) {
+ display_->clear();
}
}
void PathTrace::draw()
{
- if (!gpu_display_) {
+ if (!display_) {
return;
}
- did_draw_after_reset_ |= gpu_display_->draw();
+ did_draw_after_reset_ |= display_->draw();
}
void PathTrace::update_display(const RenderWork &render_work)
@@ -562,13 +567,13 @@ void PathTrace::update_display(const RenderWork &render_work)
return;
}
- if (!gpu_display_ && !tile_buffer_update_cb) {
+ if (!display_ && !tile_buffer_update_cb) {
VLOG(3) << "Ignore display update.";
return;
}
if (full_params_.width == 0 || full_params_.height == 0) {
- VLOG(3) << "Skipping GPUDisplay update due to 0 size of the render buffer.";
+ VLOG(3) << "Skipping PathTraceDisplay update due to 0 size of the render buffer.";
return;
}
@@ -580,13 +585,13 @@ void PathTrace::update_display(const RenderWork &render_work)
tile_buffer_update_cb();
}
- if (gpu_display_) {
+ 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);
- if (!gpu_display_->update_begin(texture_width, texture_height)) {
+ if (!display_->update_begin(texture_width, texture_height)) {
LOG(ERROR) << "Error beginning GPUDisplay update.";
return;
}
@@ -600,10 +605,10 @@ void PathTrace::update_display(const RenderWork &render_work)
* all works in parallel. */
const int num_samples = get_num_samples_in_buffer();
for (auto &&path_trace_work : path_trace_works_) {
- path_trace_work->copy_to_gpu_display(gpu_display_.get(), pass_mode, num_samples);
+ path_trace_work->copy_to_display(display_.get(), pass_mode, num_samples);
}
- gpu_display_->update_end();
+ display_->update_end();
}
render_scheduler_.report_display_update_time(render_work, time_dt() - start_time);
diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h
index f507c2d7e0a..46eb0435c91 100644
--- a/intern/cycles/integrator/path_trace.h
+++ b/intern/cycles/integrator/path_trace.h
@@ -31,12 +31,13 @@ CCL_NAMESPACE_BEGIN
class AdaptiveSampling;
class Device;
class DeviceScene;
+class DisplayDriver;
class Film;
class RenderBuffers;
class RenderScheduler;
class RenderWork;
+class PathTraceDisplay;
class Progress;
-class GPUDisplay;
class TileManager;
/* PathTrace class takes care of kernel graph and scheduling on a (multi)device. It takes care of
@@ -98,13 +99,13 @@ class PathTrace {
* Use this to configure the adaptive sampler before rendering any samples. */
void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling);
- /* Set GPU display which takes care of drawing the render result. */
- void set_gpu_display(unique_ptr<GPUDisplay> gpu_display);
+ /* Set display driver which takes care of drawing the render result. */
+ void set_display_driver(unique_ptr<DisplayDriver> driver);
- /* Clear the GPU display by filling it in with all zeroes. */
- void clear_gpu_display();
+ /* Clear the display buffer by filling it in with all zeroes. */
+ void clear_display();
- /* Perform drawing of the current state of the GPUDisplay. */
+ /* Perform drawing of the current state of the DisplayDriver. */
void draw();
/* Cancel rendering process as soon as possible, without waiting for full tile to be sampled.
@@ -252,7 +253,7 @@ class PathTrace {
RenderScheduler &render_scheduler_;
TileManager &tile_manager_;
- unique_ptr<GPUDisplay> gpu_display_;
+ unique_ptr<PathTraceDisplay> display_;
/* Per-compute device descriptors of work which is responsible for path tracing on its configured
* device. */
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
diff --git a/intern/cycles/integrator/path_trace_display.h b/intern/cycles/integrator/path_trace_display.h
new file mode 100644
index 00000000000..24aaa0df6b1
--- /dev/null
+++ b/intern/cycles/integrator/path_trace_display.h
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "render/display_driver.h"
+
+#include "util/util_half.h"
+#include "util/util_thread.h"
+#include "util/util_types.h"
+#include "util/util_unique_ptr.h"
+
+CCL_NAMESPACE_BEGIN
+
+class BufferParams;
+
+/* PathTraceDisplay is used for efficient render buffer display.
+ *
+ * The host applications implements a DisplayDriver, storing a render pass in a GPU-side
+ * textures. This texture is continuously updated by the path tracer and drawn by the host
+ * application.
+ *
+ * PathTraceDisplay is a wrapper around the DisplayDriver, adding thread safety, state tracking
+ * and error checking. */
+
+class PathTraceDisplay {
+ public:
+ 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);
+
+ /* --------------------------------------------------------------------
+ * Update procedure.
+ *
+ * These calls indicates a desire of the caller to update content of the displayed texture. */
+
+ /* Returns true when update is ready. Update should be finished with update_end().
+ *
+ * If false is returned then no update is possible, and no update_end() call is needed.
+ *
+ * The texture width and height denotes an actual resolution of the underlying render result. */
+ bool update_begin(int texture_width, int texture_height);
+
+ void update_end();
+
+ /* Get currently configured texture size of the display (as configured by `update_begin()`. */
+ int2 get_texture_size() const;
+
+ /* --------------------------------------------------------------------
+ * Texture update from CPU buffer.
+ *
+ * NOTE: The PathTraceDisplay should be marked for an update being in process with
+ * `update_begin()`.
+ *
+ * Most portable implementation, which must be supported by all platforms. Might not be the most
+ * efficient one.
+ */
+
+ /* Copy buffer of rendered pixels of a given size into a given position of the texture.
+ *
+ * This function does not acquire a lock. The reason for this is is to allow use of this function
+ * for partial updates from different devices. In this case the caller will acquire the lock
+ * once, update all the slices and release
+ * the lock once. This will ensure that draw() will never use partially updated texture. */
+ void copy_pixels_to_texture(
+ const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height);
+
+ /* --------------------------------------------------------------------
+ * Texture buffer mapping.
+ *
+ * This functionality is used to update GPU-side texture content without need to maintain CPU
+ * side buffer on the caller.
+ *
+ * NOTE: The PathTraceDisplay should be marked for an update being in process with
+ * `update_begin()`.
+ *
+ * NOTE: Texture buffer can not be mapped while graphics interoperability is active. This means
+ * that `map_texture_buffer()` is not allowed between `graphics_interop_begin()` and
+ * `graphics_interop_end()` calls.
+ */
+
+ /* Map pixels memory form texture to a buffer available for write from CPU. Width and height will
+ * define a requested size of the texture to write to.
+ * Upon success a non-null pointer is returned and the texture buffer is to be unmapped.
+ * If an error happens during mapping, or if mapping is not supported by this GPU display a
+ * null pointer is returned and the buffer is NOT to be unmapped.
+ *
+ * NOTE: Usually the implementation will rely on a GPU context of some sort, and the GPU context
+ * is often can not be bound to two threads simultaneously, and can not be released from a
+ * different thread. This means that the mapping API should be used from the single thread only,
+ */
+ half4 *map_texture_buffer();
+ void unmap_texture_buffer();
+
+ /* --------------------------------------------------------------------
+ * Graphics interoperability.
+ *
+ * A special code path which allows to update texture content directly from the GPU compute
+ * device. Complementary part of DeviceGraphicsInterop.
+ *
+ * NOTE: Graphics interoperability can not be used while the texture buffer is mapped. This means
+ * that `graphics_interop_get()` is not allowed between `map_texture_buffer()` and
+ * `unmap_texture_buffer()` calls. */
+
+ /* Get PathTraceDisplay graphics interoperability information which acts as a destination for the
+ * device API. */
+ DisplayDriver::GraphicsInterop graphics_interop_get();
+
+ /* (De)activate GPU display for graphics interoperability outside of regular display update
+ * routines. */
+ void graphics_interop_activate();
+ void graphics_interop_deactivate();
+
+ /* --------------------------------------------------------------------
+ * Drawing.
+ */
+
+ /* Clear the texture by filling it with all zeroes.
+ *
+ * This call might happen in parallel with draw, but can never happen in parallel with the
+ * update.
+ *
+ * The actual zeroing can be deferred to a later moment. What is important is that after clear
+ * and before pixels update the drawing texture will be fully empty, and that partial update
+ * after clear will write new pixel values for an updating area, leaving everything else zeroed.
+ *
+ * If the GPU display supports graphics interoperability then the zeroing the display is to be
+ * delegated to the device via the `DisplayDriver::GraphicsInterop`. */
+ void clear();
+
+ /* Draw the current state of the texture.
+ *
+ * Returns true if this call did draw an updated state of the texture. */
+ bool draw();
+
+ private:
+ /* Display driver implemented by the host application. */
+ unique_ptr<DisplayDriver> driver_;
+
+ /* Current display parameters */
+ thread_mutex mutex_;
+ DisplayDriver::Params params_;
+
+ /* Mark texture as its content has been updated.
+ * Used from places which knows that the texture content has been brought up-to-date, so that the
+ * drawing knows whether it can be performed, and whether drawing happened with an up-to-date
+ * texture state. */
+ void mark_texture_updated();
+
+ /* State of the update process. */
+ struct {
+ /* True when update is in process, indicated by `update_begin()` / `update_end()`. */
+ bool is_active = false;
+ } update_state_;
+
+ /* State of the texture, which is needed for an integration with render session and interactive
+ * updates and navigation. */
+ struct {
+ /* Denotes whether possibly existing state of GPU side texture is still usable.
+ * It will not be usable in cases like render border did change (in this case we don't want
+ * previous texture to be rendered at all).
+ *
+ * However, if only navigation or object in scene did change, then the outdated state of the
+ * texture is still usable for draw, preventing display viewport flickering on navigation and
+ * object modifications. */
+ bool is_usable = false;
+
+ /* Texture is considered outdated after `reset()` until the next call of
+ * `copy_pixels_to_texture()`. */
+ bool is_outdated = true;
+
+ /* Texture size in pixels. */
+ int2 size = make_int2(0, 0);
+ } texture_state_;
+
+ /* State of the texture buffer. Is tracked to perform sanity checks. */
+ struct {
+ /* True when the texture buffer is mapped with `map_texture_buffer()`. */
+ bool is_mapped = false;
+ } texture_buffer_state_;
+};
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp
index d9634acac10..c29177907c9 100644
--- a/intern/cycles/integrator/path_trace_work.cpp
+++ b/intern/cycles/integrator/path_trace_work.cpp
@@ -16,12 +16,12 @@
#include "device/device.h"
+#include "integrator/path_trace_display.h"
#include "integrator/path_trace_work.h"
#include "integrator/path_trace_work_cpu.h"
#include "integrator/path_trace_work_gpu.h"
#include "render/buffers.h"
#include "render/film.h"
-#include "render/gpu_display.h"
#include "render/scene.h"
#include "kernel/kernel_types.h"
@@ -185,12 +185,12 @@ PassAccessor::PassAccessInfo PathTraceWork::get_display_pass_access_info(PassMod
return pass_access_info;
}
-PassAccessor::Destination PathTraceWork::get_gpu_display_destination_template(
- const GPUDisplay *gpu_display) const
+PassAccessor::Destination PathTraceWork::get_display_destination_template(
+ const PathTraceDisplay *display) const
{
PassAccessor::Destination destination(film_->get_display_pass());
- const int2 display_texture_size = gpu_display->get_texture_size();
+ const int2 display_texture_size = display->get_texture_size();
const int texture_x = effective_buffer_params_.full_x - effective_full_params_.full_x;
const int texture_y = effective_buffer_params_.full_y - effective_full_params_.full_y;
diff --git a/intern/cycles/integrator/path_trace_work.h b/intern/cycles/integrator/path_trace_work.h
index e1be1655edd..404165b7c55 100644
--- a/intern/cycles/integrator/path_trace_work.h
+++ b/intern/cycles/integrator/path_trace_work.h
@@ -28,7 +28,7 @@ class BufferParams;
class Device;
class DeviceScene;
class Film;
-class GPUDisplay;
+class PathTraceDisplay;
class RenderBuffers;
class PathTraceWork {
@@ -83,11 +83,9 @@ class PathTraceWork {
* noisy pass mode will be passed here when it is known that the buffer does not have denoised
* passes yet (because denoiser did not run). If the denoised pass is requested and denoiser is
* not used then this function will fall-back to the noisy pass instead. */
- virtual void copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples) = 0;
+ virtual void copy_to_display(PathTraceDisplay *display, PassMode pass_mode, int num_samples) = 0;
- virtual void destroy_gpu_resources(GPUDisplay *gpu_display) = 0;
+ virtual void destroy_gpu_resources(PathTraceDisplay *display) = 0;
/* Copy data from/to given render buffers.
* Will copy pixels from a corresponding place (from multi-device point of view) of the render
@@ -162,8 +160,8 @@ class PathTraceWork {
/* Get destination which offset and stride are configured so that writing to it will write to a
* proper location of GPU display texture, taking current tile and device slice into account. */
- PassAccessor::Destination get_gpu_display_destination_template(
- const GPUDisplay *gpu_display) const;
+ PassAccessor::Destination get_display_destination_template(
+ const PathTraceDisplay *display) const;
/* Device which will be used for path tracing.
* Note that it is an actual render device (and never is a multi-device). */
diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp
index 14658d4d1ce..18a5365453d 100644
--- a/intern/cycles/integrator/path_trace_work_cpu.cpp
+++ b/intern/cycles/integrator/path_trace_work_cpu.cpp
@@ -22,9 +22,9 @@
#include "kernel/kernel_path_state.h"
#include "integrator/pass_accessor_cpu.h"
+#include "integrator/path_trace_display.h"
#include "render/buffers.h"
-#include "render/gpu_display.h"
#include "render/scene.h"
#include "util/util_atomic.h"
@@ -161,14 +161,14 @@ void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobals *kernel_global
}
}
-void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples)
+void PathTraceWorkCPU::copy_to_display(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples)
{
- half4 *rgba_half = gpu_display->map_texture_buffer();
+ half4 *rgba_half = display->map_texture_buffer();
if (!rgba_half) {
- /* TODO(sergey): Look into using copy_to_gpu_display() if mapping failed. Might be needed for
- * some implementations of GPUDisplay which can not map memory? */
+ /* TODO(sergey): Look into using copy_to_display() if mapping failed. Might be needed for
+ * some implementations of PathTraceDisplay which can not map memory? */
return;
}
@@ -178,7 +178,7 @@ void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display,
const PassAccessorCPU pass_accessor(pass_access_info, kfilm.exposure, num_samples);
- PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display);
+ PassAccessor::Destination destination = get_display_destination_template(display);
destination.pixels_half_rgba = rgba_half;
tbb::task_arena local_arena = local_tbb_arena_create(device_);
@@ -186,10 +186,10 @@ void PathTraceWorkCPU::copy_to_gpu_display(GPUDisplay *gpu_display,
pass_accessor.get_render_tile_pixels(buffers_.get(), effective_buffer_params_, destination);
});
- gpu_display->unmap_texture_buffer();
+ display->unmap_texture_buffer();
}
-void PathTraceWorkCPU::destroy_gpu_resources(GPUDisplay * /*gpu_display*/)
+void PathTraceWorkCPU::destroy_gpu_resources(PathTraceDisplay * /*display*/)
{
}
diff --git a/intern/cycles/integrator/path_trace_work_cpu.h b/intern/cycles/integrator/path_trace_work_cpu.h
index ab729bbf879..d011e8d05bd 100644
--- a/intern/cycles/integrator/path_trace_work_cpu.h
+++ b/intern/cycles/integrator/path_trace_work_cpu.h
@@ -50,10 +50,10 @@ class PathTraceWorkCPU : public PathTraceWork {
int start_sample,
int samples_num) override;
- virtual void copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples) override;
- virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override;
+ virtual void copy_to_display(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples) override;
+ virtual void destroy_gpu_resources(PathTraceDisplay *display) override;
virtual bool copy_render_buffers_from_device() override;
virtual bool copy_render_buffers_to_device() override;
diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp
index e41d8d1d252..17c49f244d2 100644
--- a/intern/cycles/integrator/path_trace_work_gpu.cpp
+++ b/intern/cycles/integrator/path_trace_work_gpu.cpp
@@ -15,12 +15,12 @@
*/
#include "integrator/path_trace_work_gpu.h"
+#include "integrator/path_trace_display.h"
#include "device/device.h"
#include "integrator/pass_accessor_gpu.h"
#include "render/buffers.h"
-#include "render/gpu_display.h"
#include "render/scene.h"
#include "util/util_logging.h"
#include "util/util_tbb.h"
@@ -46,7 +46,7 @@ PathTraceWorkGPU::PathTraceWorkGPU(Device *device,
queued_paths_(device, "queued_paths", MEM_READ_WRITE),
num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE),
work_tiles_(device, "work_tiles", MEM_READ_WRITE),
- gpu_display_rgba_half_(device, "display buffer half", MEM_READ_WRITE),
+ display_rgba_half_(device, "display buffer half", MEM_READ_WRITE),
max_num_paths_(queue_->num_concurrent_states(sizeof(IntegratorStateCPU))),
min_num_active_paths_(queue_->num_concurrent_busy_states()),
max_active_path_index_(0)
@@ -652,7 +652,7 @@ int PathTraceWorkGPU::get_num_active_paths()
bool PathTraceWorkGPU::should_use_graphics_interop()
{
/* There are few aspects with the graphics interop when using multiple devices caused by the fact
- * that the GPUDisplay has a single texture:
+ * that the PathTraceDisplay has a single texture:
*
* CUDA will return `CUDA_ERROR_NOT_SUPPORTED` from `cuGraphicsGLRegisterBuffer()` when
* attempting to register OpenGL PBO which has been mapped. Which makes sense, because
@@ -678,9 +678,9 @@ bool PathTraceWorkGPU::should_use_graphics_interop()
return interop_use_;
}
-void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples)
+void PathTraceWorkGPU::copy_to_display(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples)
{
if (device_->have_error()) {
/* Don't attempt to update GPU display if the device has errors: the error state will make
@@ -694,7 +694,7 @@ void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display,
}
if (should_use_graphics_interop()) {
- if (copy_to_gpu_display_interop(gpu_display, pass_mode, num_samples)) {
+ if (copy_to_display_interop(display, pass_mode, num_samples)) {
return;
}
@@ -703,12 +703,12 @@ void PathTraceWorkGPU::copy_to_gpu_display(GPUDisplay *gpu_display,
interop_use_ = false;
}
- copy_to_gpu_display_naive(gpu_display, pass_mode, num_samples);
+ copy_to_display_naive(display, pass_mode, num_samples);
}
-void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples)
+void PathTraceWorkGPU::copy_to_display_naive(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples)
{
const int full_x = effective_buffer_params_.full_x;
const int full_y = effective_buffer_params_.full_y;
@@ -725,44 +725,42 @@ void PathTraceWorkGPU::copy_to_gpu_display_naive(GPUDisplay *gpu_display,
* NOTE: allocation happens to the final resolution so that no re-allocation happens on every
* change of the resolution divider. However, if the display becomes smaller, shrink the
* allocated memory as well. */
- if (gpu_display_rgba_half_.data_width != final_width ||
- gpu_display_rgba_half_.data_height != final_height) {
- gpu_display_rgba_half_.alloc(final_width, final_height);
+ if (display_rgba_half_.data_width != final_width ||
+ display_rgba_half_.data_height != final_height) {
+ display_rgba_half_.alloc(final_width, final_height);
/* TODO(sergey): There should be a way to make sure device-side memory is allocated without
* transferring zeroes to the device. */
- queue_->zero_to_device(gpu_display_rgba_half_);
+ queue_->zero_to_device(display_rgba_half_);
}
PassAccessor::Destination destination(film_->get_display_pass());
- destination.d_pixels_half_rgba = gpu_display_rgba_half_.device_pointer;
+ destination.d_pixels_half_rgba = display_rgba_half_.device_pointer;
get_render_tile_film_pixels(destination, pass_mode, num_samples);
- queue_->copy_from_device(gpu_display_rgba_half_);
+ queue_->copy_from_device(display_rgba_half_);
queue_->synchronize();
- gpu_display->copy_pixels_to_texture(
- gpu_display_rgba_half_.data(), texture_x, texture_y, width, height);
+ display->copy_pixels_to_texture(display_rgba_half_.data(), texture_x, texture_y, width, height);
}
-bool PathTraceWorkGPU::copy_to_gpu_display_interop(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples)
+bool PathTraceWorkGPU::copy_to_display_interop(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples)
{
if (!device_graphics_interop_) {
device_graphics_interop_ = queue_->graphics_interop_create();
}
- const DeviceGraphicsInteropDestination graphics_interop_dst =
- gpu_display->graphics_interop_get();
- device_graphics_interop_->set_destination(graphics_interop_dst);
+ const DisplayDriver::GraphicsInterop graphics_interop_dst = display->graphics_interop_get();
+ device_graphics_interop_->set_display_interop(graphics_interop_dst);
const device_ptr d_rgba_half = device_graphics_interop_->map();
if (!d_rgba_half) {
return false;
}
- PassAccessor::Destination destination = get_gpu_display_destination_template(gpu_display);
+ PassAccessor::Destination destination = get_display_destination_template(display);
destination.d_pixels_half_rgba = d_rgba_half;
get_render_tile_film_pixels(destination, pass_mode, num_samples);
@@ -772,14 +770,14 @@ bool PathTraceWorkGPU::copy_to_gpu_display_interop(GPUDisplay *gpu_display,
return true;
}
-void PathTraceWorkGPU::destroy_gpu_resources(GPUDisplay *gpu_display)
+void PathTraceWorkGPU::destroy_gpu_resources(PathTraceDisplay *display)
{
if (!device_graphics_interop_) {
return;
}
- gpu_display->graphics_interop_activate();
+ display->graphics_interop_activate();
device_graphics_interop_ = nullptr;
- gpu_display->graphics_interop_deactivate();
+ display->graphics_interop_deactivate();
}
void PathTraceWorkGPU::get_render_tile_film_pixels(const PassAccessor::Destination &destination,
diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h
index 38788122b0d..9212537d2fd 100644
--- a/intern/cycles/integrator/path_trace_work_gpu.h
+++ b/intern/cycles/integrator/path_trace_work_gpu.h
@@ -48,10 +48,10 @@ class PathTraceWorkGPU : public PathTraceWork {
int start_sample,
int samples_num) override;
- virtual void copy_to_gpu_display(GPUDisplay *gpu_display,
- PassMode pass_mode,
- int num_samples) override;
- virtual void destroy_gpu_resources(GPUDisplay *gpu_display) override;
+ virtual void copy_to_display(PathTraceDisplay *display,
+ PassMode pass_mode,
+ int num_samples) override;
+ virtual void destroy_gpu_resources(PathTraceDisplay *display) override;
virtual bool copy_render_buffers_from_device() override;
virtual bool copy_render_buffers_to_device() override;
@@ -88,16 +88,16 @@ class PathTraceWorkGPU : public PathTraceWork {
int get_num_active_paths();
- /* Check whether graphics interop can be used for the GPUDisplay update. */
+ /* Check whether graphics interop can be used for the PathTraceDisplay update. */
bool should_use_graphics_interop();
- /* Naive implementation of the `copy_to_gpu_display()` which performs film conversion on the
- * device, then copies pixels to the host and pushes them to the `gpu_display`. */
- void copy_to_gpu_display_naive(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples);
+ /* Naive implementation of the `copy_to_display()` which performs film conversion on the
+ * device, then copies pixels to the host and pushes them to the `display`. */
+ void copy_to_display_naive(PathTraceDisplay *display, PassMode pass_mode, int num_samples);
- /* Implementation of `copy_to_gpu_display()` which uses driver's OpenGL/GPU interoperability
+ /* Implementation of `copy_to_display()` which uses driver's OpenGL/GPU interoperability
* functionality, avoiding copy of pixels to the host. */
- bool copy_to_gpu_display_interop(GPUDisplay *gpu_display, PassMode pass_mode, int num_samples);
+ bool copy_to_display_interop(PathTraceDisplay *display, PassMode pass_mode, int num_samples);
/* Synchronously run film conversion kernel and store display result in the given destination. */
void get_render_tile_film_pixels(const PassAccessor::Destination &destination,
@@ -139,9 +139,9 @@ class PathTraceWorkGPU : public PathTraceWork {
/* Temporary buffer for passing work tiles to kernel. */
device_vector<KernelWorkTile> work_tiles_;
- /* Temporary buffer used by the copy_to_gpu_display() whenever graphics interoperability is not
+ /* Temporary buffer used by the copy_to_display() whenever graphics interoperability is not
* available. Is allocated on-demand. */
- device_vector<half4> gpu_display_rgba_half_;
+ device_vector<half4> display_rgba_half_;
unique_ptr<DeviceGraphicsInterop> device_graphics_interop_;
diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h
index 6ed368a2dc8..c4ab15e54ba 100644
--- a/intern/cycles/integrator/render_scheduler.h
+++ b/intern/cycles/integrator/render_scheduler.h
@@ -344,7 +344,7 @@ class RenderScheduler {
/* Number of rendered samples on top of the start sample. */
int num_rendered_samples = 0;
- /* Point in time the latest GPUDisplay work has been scheduled. */
+ /* Point in time the latest PathTraceDisplay work has been scheduled. */
double last_display_update_time = 0.0;
/* Value of -1 means display was never updated. */
int last_display_update_sample = -1;