diff options
author | Brecht Van Lommel <brecht> | 2021-09-30 17:51:03 +0300 |
---|---|---|
committer | Brecht Van Lommel <brecht@blender.org> | 2021-09-30 21:53:27 +0300 |
commit | 1a134c4c30a643ada1b9a7a037040b5f5c173a28 (patch) | |
tree | b4216998527ff24fb3fc9e9351ced05cd7b7eb08 /intern/cycles | |
parent | a754e35198d852ea34e2b82cd2b126538e6f5a3b (diff) |
Cycles: refactor API for render output
* Add OutputDriver, replacing function callbacks in Session.
* Add PathTraceTile, replacing tile access methods in Session.
* Add more detailed comments about how this driver should be implemented.
* Add OIIOOutputDriver for Cycles standalone to output an image.
Differential Revision: https://developer.blender.org/D12627
Diffstat (limited to 'intern/cycles')
21 files changed, 594 insertions, 360 deletions
diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index f9dc5f00802..3ed3f54ef9f 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -64,6 +64,8 @@ if(WITH_CYCLES_STANDALONE) cycles_standalone.cpp cycles_xml.cpp cycles_xml.h + oiio_output_driver.cpp + oiio_output_driver.h ) add_executable(cycles ${SRC} ${INC} ${INC_SYS}) unset(SRC) @@ -73,7 +75,7 @@ if(WITH_CYCLES_STANDALONE) if(APPLE) if(WITH_OPENCOLORIO) - set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit") + set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit -framework Carbon") endif() if(WITH_OPENIMAGEDENOISE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") # OpenImageDenoise uses BNNS from the Accelerate framework. diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index 258e67b3459..00dc140648a 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -36,6 +36,9 @@ #include "util/util_unique_ptr.h" #include "util/util_version.h" +#include "app/cycles_xml.h" +#include "app/oiio_output_driver.h" + #ifdef WITH_CYCLES_STANDALONE_GUI # include "util/util_view.h" #endif @@ -54,6 +57,7 @@ struct Options { bool quiet; bool show_help, interactive, pause; string output_filepath; + string output_pass; } options; static void session_print(const string &str) @@ -89,30 +93,6 @@ static void session_print_status() session_print(status); } -static bool write_render(const uchar *pixels, int w, int h, int channels) -{ - string msg = string_printf("Writing image %s", options.output_path.c_str()); - session_print(msg); - - unique_ptr<ImageOutput> out = unique_ptr<ImageOutput>(ImageOutput::create(options.output_path)); - if (!out) { - return false; - } - - ImageSpec spec(w, h, channels, TypeDesc::UINT8); - if (!out->open(options.output_path, spec)) { - return false; - } - - /* conversion for different top/bottom convention */ - out->write_image( - TypeDesc::UINT8, pixels + (h - 1) * w * channels, AutoStride, -w * channels, AutoStride); - - out->close(); - - return true; -} - static BufferParams &session_buffer_params() { static BufferParams buffer_params; @@ -147,9 +127,14 @@ static void scene_init() static void session_init() { - options.session_params.write_render_cb = write_render; + options.output_pass = "combined"; options.session = new Session(options.session_params, options.scene_params); + if (!options.output_filepath.empty()) { + options.session->set_output_driver(make_unique<OIIOOutputDriver>( + options.output_filepath, options.output_pass, session_print)); + } + if (options.session_params.background && !options.quiet) options.session->progress.set_update_callback(function_bind(&session_print_status)); #ifdef WITH_CYCLES_STANDALONE_GUI @@ -160,6 +145,11 @@ static void session_init() /* load scene */ scene_init(); + /* add pass for output. */ + Pass *pass = options.scene->create_node<Pass>(); + pass->set_name(ustring(options.output_pass.c_str())); + pass->set_type(PASS_COMBINED); + options.session->reset(options.session_params, session_buffer_params()); options.session->start(); } diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 54f97fddbd9..0b83c60f32d 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -333,6 +333,7 @@ static void xml_read_shader_graph(XMLReadState &state, Shader *shader, xml_node } snode = (ShaderNode *)node_type->create(node_type); + snode->set_owner(graph); } xml_read_node(graph_reader, snode, node); diff --git a/intern/cycles/app/oiio_output_driver.cpp b/intern/cycles/app/oiio_output_driver.cpp new file mode 100644 index 00000000000..d791c89772f --- /dev/null +++ b/intern/cycles/app/oiio_output_driver.cpp @@ -0,0 +1,71 @@ +/* + * 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 "app/oiio_output_driver.h" + +CCL_NAMESPACE_BEGIN + +OIIOOutputDriver::OIIOOutputDriver(const string_view filepath, + const string_view pass, + LogFunction log) + : filepath_(filepath), pass_(pass), log_(log) +{ +} + +OIIOOutputDriver::~OIIOOutputDriver() +{ +} + +void OIIOOutputDriver::write_render_tile(const Tile &tile) +{ + /* Only write the full buffer, no intermediate tiles. */ + if (!(tile.size == tile.full_size)) { + return; + } + + log_(string_printf("Writing image %s", filepath_.c_str())); + + unique_ptr<ImageOutput> image_output(ImageOutput::create(filepath_)); + if (image_output == nullptr) { + log_("Failed to create image file"); + return; + } + + const int width = tile.size.x; + const int height = tile.size.y; + + ImageSpec spec(width, height, 4, TypeDesc::FLOAT); + if (!image_output->open(filepath_, spec)) { + log_("Failed to create image file"); + return; + } + + vector<float> pixels(width * height * 4); + if (!tile.get_pass_pixels(pass_, 4, pixels.data())) { + log_("Failed to read render pass pixels"); + return; + } + + /* Manipulate offset and stride to convert from bottom-up to top-down convention. */ + image_output->write_image(TypeDesc::FLOAT, + pixels.data() + (height - 1) * width * 4, + AutoStride, + -width * 4 * sizeof(float), + AutoStride); + image_output->close(); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/app/oiio_output_driver.h b/intern/cycles/app/oiio_output_driver.h new file mode 100644 index 00000000000..cdc4085d962 --- /dev/null +++ b/intern/cycles/app/oiio_output_driver.h @@ -0,0 +1,42 @@ +/* + * 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 "render/output_driver.h" + +#include "util/util_function.h" +#include "util/util_image.h" +#include "util/util_string.h" +#include "util/util_unique_ptr.h" +#include "util/util_vector.h" + +CCL_NAMESPACE_BEGIN + +class OIIOOutputDriver : public OutputDriver { + public: + typedef function<void(const string &)> LogFunction; + + OIIOOutputDriver(const string_view filepath, const string_view pass, LogFunction log); + virtual ~OIIOOutputDriver(); + + void write_render_tile(const Tile &tile) override; + + protected: + string filepath_; + string pass_; + LogFunction log_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index 2660eee017b..a0442b3394b 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -38,6 +38,7 @@ set(SRC blender_mesh.cpp blender_object.cpp blender_object_cull.cpp + blender_output_driver.cpp blender_particles.cpp blender_curves.cpp blender_logging.cpp @@ -55,6 +56,7 @@ set(SRC blender_id_map.h blender_image.h blender_object_cull.h + blender_output_driver.h blender_sync.h blender_session.h blender_texture.h diff --git a/intern/cycles/blender/blender_output_driver.cpp b/intern/cycles/blender/blender_output_driver.cpp new file mode 100644 index 00000000000..f380b7b3bb1 --- /dev/null +++ b/intern/cycles/blender/blender_output_driver.cpp @@ -0,0 +1,127 @@ +/* + * 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 "blender/blender_output_driver.h" + +CCL_NAMESPACE_BEGIN + +BlenderOutputDriver::BlenderOutputDriver(BL::RenderEngine &b_engine) : b_engine_(b_engine) +{ +} + +BlenderOutputDriver::~BlenderOutputDriver() +{ +} + +bool BlenderOutputDriver::read_render_tile(const Tile &tile) +{ + /* Get render result. */ + BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x, + tile.offset.y, + tile.size.x, + tile.size.y, + tile.layer.c_str(), + tile.view.c_str()); + + /* Can happen if the intersected rectangle gives 0 width or height. */ + if (b_rr.ptr.data == NULL) { + return false; + } + + BL::RenderResult::layers_iterator b_single_rlay; + b_rr.layers.begin(b_single_rlay); + + /* layer will be missing if it was disabled in the UI */ + if (b_single_rlay == b_rr.layers.end()) { + return false; + } + + BL::RenderLayer b_rlay = *b_single_rlay; + + vector<float> pixels(tile.size.x * tile.size.y * 4); + + /* Copy each pass. + * TODO:copy only the required ones for better performance? */ + for (BL::RenderPass &b_pass : b_rlay.passes) { + tile.set_pass_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect()); + } + + b_engine_.end_result(b_rr, false, false, false); + + return true; +} + +bool BlenderOutputDriver::update_render_tile(const Tile &tile) +{ + /* Use final write for preview renders, otherwise render result wouldn't be be updated + * quickly on Blender side. For all other cases we use the display driver. */ + if (b_engine_.is_preview()) { + write_render_tile(tile); + return true; + } + else { + /* Don't highlight full-frame tile. */ + if (!(tile.size == tile.full_size)) { + b_engine_.tile_highlight_clear_all(); + b_engine_.tile_highlight_set(tile.offset.x, tile.offset.y, tile.size.x, tile.size.y, true); + } + + return false; + } +} + +void BlenderOutputDriver::write_render_tile(const Tile &tile) +{ + b_engine_.tile_highlight_clear_all(); + + /* Get render result. */ + BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x, + tile.offset.y, + tile.size.x, + tile.size.y, + tile.layer.c_str(), + tile.view.c_str()); + + /* Can happen if the intersected rectangle gives 0 width or height. */ + if (b_rr.ptr.data == NULL) { + return; + } + + BL::RenderResult::layers_iterator b_single_rlay; + b_rr.layers.begin(b_single_rlay); + + /* Layer will be missing if it was disabled in the UI. */ + if (b_single_rlay == b_rr.layers.end()) { + return; + } + + BL::RenderLayer b_rlay = *b_single_rlay; + + vector<float> pixels(tile.size.x * tile.size.y * 4); + + /* Copy each pass. */ + for (BL::RenderPass &b_pass : b_rlay.passes) { + if (!tile.get_pass_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) { + memset(&pixels[0], 0, pixels.size() * sizeof(float)); + } + + b_pass.rect(&pixels[0]); + } + + b_engine_.end_result(b_rr, true, false, true); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_output_driver.h b/intern/cycles/blender/blender_output_driver.h new file mode 100644 index 00000000000..8a1cf92d7c7 --- /dev/null +++ b/intern/cycles/blender/blender_output_driver.h @@ -0,0 +1,40 @@ +/* + * 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 "MEM_guardedalloc.h" + +#include "RNA_blender_cpp.h" + +#include "render/output_driver.h" + +CCL_NAMESPACE_BEGIN + +class BlenderOutputDriver : public OutputDriver { + public: + BlenderOutputDriver(BL::RenderEngine &b_engine); + ~BlenderOutputDriver(); + + virtual void write_render_tile(const Tile &tile) override; + virtual bool update_render_tile(const Tile &tile) override; + virtual bool read_render_tile(const Tile &tile) override; + + protected: + BL::RenderEngine b_engine_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 1a42456eda0..3be7ff32bd8 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -43,6 +43,7 @@ #include "util/util_time.h" #include "blender/blender_display_driver.h" +#include "blender/blender_output_driver.h" #include "blender/blender_session.h" #include "blender/blender_sync.h" #include "blender/blender_util.h" @@ -157,7 +158,8 @@ void BlenderSession::create_session() b_v3d, b_rv3d, scene->camera, width, height); session->reset(session_params, buffer_params); - /* Create GPU display. */ + /* Create GPU display. + * TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */ if (!b_engine.is_preview() && !headless) { unique_ptr<BlenderDisplayDriver> display_driver = make_unique<BlenderDisplayDriver>(b_engine, b_scene); @@ -279,96 +281,6 @@ void BlenderSession::free_session() session = nullptr; } -void BlenderSession::read_render_tile() -{ - const int2 tile_offset = session->get_render_tile_offset(); - const int2 tile_size = session->get_render_tile_size(); - - /* get render result */ - BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x, - tile_offset.y, - tile_size.x, - tile_size.y, - b_rlay_name.c_str(), - b_rview_name.c_str()); - - /* can happen if the intersected rectangle gives 0 width or height */ - if (b_rr.ptr.data == NULL) { - return; - } - - BL::RenderResult::layers_iterator b_single_rlay; - b_rr.layers.begin(b_single_rlay); - - /* layer will be missing if it was disabled in the UI */ - if (b_single_rlay == b_rr.layers.end()) - return; - - BL::RenderLayer b_rlay = *b_single_rlay; - - vector<float> pixels(tile_size.x * tile_size.y * 4); - - /* Copy each pass. - * TODO:copy only the required ones for better performance? */ - for (BL::RenderPass &b_pass : b_rlay.passes) { - session->set_render_tile_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect()); - } - - b_engine.end_result(b_rr, false, false, false); -} - -void BlenderSession::write_render_tile() -{ - const int2 tile_offset = session->get_render_tile_offset(); - const int2 tile_size = session->get_render_tile_size(); - - const string_view render_layer_name = session->get_render_tile_layer(); - const string_view render_view_name = session->get_render_tile_view(); - - b_engine.tile_highlight_clear_all(); - - /* get render result */ - BL::RenderResult b_rr = b_engine.begin_result(tile_offset.x, - tile_offset.y, - tile_size.x, - tile_size.y, - render_layer_name.c_str(), - render_view_name.c_str()); - - /* can happen if the intersected rectangle gives 0 width or height */ - if (b_rr.ptr.data == NULL) { - return; - } - - BL::RenderResult::layers_iterator b_single_rlay; - b_rr.layers.begin(b_single_rlay); - - /* layer will be missing if it was disabled in the UI */ - if (b_single_rlay == b_rr.layers.end()) { - return; - } - - BL::RenderLayer b_rlay = *b_single_rlay; - - write_render_result(b_rlay); - - b_engine.end_result(b_rr, true, false, true); -} - -void BlenderSession::update_render_tile() -{ - if (!session->has_multiple_render_tiles()) { - /* Don't highlight full-frame tile. */ - return; - } - - const int2 tile_offset = session->get_render_tile_offset(); - const int2 tile_size = session->get_render_tile_size(); - - b_engine.tile_highlight_clear_all(); - b_engine.tile_highlight_set(tile_offset.x, tile_offset.y, tile_size.x, tile_size.y, true); -} - void BlenderSession::full_buffer_written(string_view filename) { full_buffer_files_.emplace_back(filename); @@ -442,18 +354,8 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) return; } - /* set callback to write out render results */ - session->write_render_tile_cb = [&]() { write_render_tile(); }; - - /* Use final write for preview renders, otherwise render result wouldn't be be updated on Blender - * side. */ - /* TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */ - if (b_engine.is_preview()) { - session->update_render_tile_cb = [&]() { write_render_tile(); }; - } - else { - session->update_render_tile_cb = [&]() { update_render_tile(); }; - } + /* Create driver to write out render results. */ + session->set_output_driver(make_unique<BlenderOutputDriver>(b_engine)); session->full_buffer_written_cb = [&](string_view filename) { full_buffer_written(filename); }; @@ -599,9 +501,8 @@ void BlenderSession::render_frame_finish() path_remove(filename); } - /* clear callback */ - session->write_render_tile_cb = function_null; - session->update_render_tile_cb = function_null; + /* Clear driver. */ + session->set_output_driver(nullptr); session->full_buffer_written_cb = function_null; } @@ -707,9 +608,8 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, pass->set_type(bake_type_to_pass(bake_type, bake_filter)); pass->set_include_albedo((bake_filter & BL::BakeSettings::pass_filter_COLOR)); - session->read_render_tile_cb = [&]() { read_render_tile(); }; - session->write_render_tile_cb = [&]() { write_render_tile(); }; session->set_display_driver(nullptr); + session->set_output_driver(make_unique<BlenderOutputDriver>(b_engine)); if (!session->progress.get_cancel()) { /* Sync scene. */ @@ -752,43 +652,7 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, session->wait(); } - session->read_render_tile_cb = function_null; - session->write_render_tile_cb = function_null; -} - -void BlenderSession::write_render_result(BL::RenderLayer &b_rlay) -{ - if (!session->copy_render_tile_from_device()) { - return; - } - - const int2 tile_size = session->get_render_tile_size(); - vector<float> pixels(tile_size.x * tile_size.y * 4); - - /* Copy each pass. */ - for (BL::RenderPass &b_pass : b_rlay.passes) { - if (!session->get_render_tile_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) { - memset(&pixels[0], 0, pixels.size() * sizeof(float)); - } - - b_pass.rect(&pixels[0]); - } -} - -void BlenderSession::update_render_result(BL::RenderLayer &b_rlay) -{ - if (!session->copy_render_tile_from_device()) { - return; - } - - const int2 tile_size = session->get_render_tile_size(); - vector<float> pixels(tile_size.x * tile_size.y * 4); - - /* Copy combined pass. */ - BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str())); - if (session->get_render_tile_pixels("Combined", b_combined_pass.channels(), &pixels[0])) { - b_combined_pass.rect(&pixels[0]); - } + session->set_output_driver(nullptr); } void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index 1ca8fdf87d0..fef6ad1adfc 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -70,20 +70,7 @@ class BlenderSession { const int bake_width, const int bake_height); - void write_render_result(BL::RenderLayer &b_rlay); - void write_render_tile(); - - void update_render_tile(); - void full_buffer_written(string_view filename); - - /* update functions are used to update display buffer only after sample was rendered - * only needed for better visual feedback */ - void update_render_result(BL::RenderLayer &b_rlay); - - /* read functions for baking input */ - void read_render_tile(); - /* interactive updates */ void synchronize(BL::Depsgraph &b_depsgraph); diff --git a/intern/cycles/integrator/CMakeLists.txt b/intern/cycles/integrator/CMakeLists.txt index 8acd72f0508..949254606b8 100644 --- a/intern/cycles/integrator/CMakeLists.txt +++ b/intern/cycles/integrator/CMakeLists.txt @@ -28,6 +28,7 @@ set(SRC pass_accessor_cpu.cpp pass_accessor_gpu.cpp path_trace_display.cpp + path_trace_tile.cpp path_trace_work.cpp path_trace_work_cpu.cpp path_trace_work_gpu.cpp @@ -49,6 +50,7 @@ set(SRC_HEADERS pass_accessor_cpu.h pass_accessor_gpu.h path_trace_display.h + path_trace_tile.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 36cd7314b4c..7624b244175 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -20,6 +20,7 @@ #include "device/device.h" #include "integrator/pass_accessor.h" #include "integrator/path_trace_display.h" +#include "integrator/path_trace_tile.h" #include "integrator/render_scheduler.h" #include "render/pass.h" #include "render/scene.h" @@ -535,6 +536,11 @@ void PathTrace::denoise(const RenderWork &render_work) render_scheduler_.report_denoise_time(render_work, time_dt() - start_time); } +void PathTrace::set_output_driver(unique_ptr<OutputDriver> driver) +{ + output_driver_ = move(driver); +} + void PathTrace::set_display_driver(unique_ptr<DisplayDriver> driver) { if (driver) { @@ -567,7 +573,7 @@ void PathTrace::update_display(const RenderWork &render_work) return; } - if (!display_ && !tile_buffer_update_cb) { + if (!display_ && !output_driver_) { VLOG(3) << "Ignore display update."; return; } @@ -579,10 +585,11 @@ void PathTrace::update_display(const RenderWork &render_work) const double start_time = time_dt(); - if (tile_buffer_update_cb) { + if (output_driver_) { VLOG(3) << "Invoke buffer update callback."; - tile_buffer_update_cb(); + PathTraceTile tile(*this); + output_driver_->update_render_tile(tile); } if (display_) { @@ -758,20 +765,26 @@ bool PathTrace::is_cancel_requested() void PathTrace::tile_buffer_write() { - if (!tile_buffer_write_cb) { + if (!output_driver_) { return; } - tile_buffer_write_cb(); + PathTraceTile tile(*this); + output_driver_->write_render_tile(tile); } void PathTrace::tile_buffer_read() { - if (!tile_buffer_read_cb) { + if (!device_scene_->data.bake.use) { return; } - if (tile_buffer_read_cb()) { + if (!output_driver_) { + return; + } + + PathTraceTile tile(*this); + if (output_driver_->read_render_tile(tile)) { tbb::parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) { path_trace_work->copy_render_buffers_to_device(); }); @@ -1010,6 +1023,11 @@ int2 PathTrace::get_render_tile_offset() const return make_int2(tile.x, tile.y); } +int2 PathTrace::get_render_size() const +{ + return tile_manager_.get_size(); +} + const BufferParams &PathTrace::get_render_tile_params() const { if (full_frame_state_.render_buffers) { diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index 46eb0435c91..dbb22c204d9 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -37,6 +37,7 @@ class RenderBuffers; class RenderScheduler; class RenderWork; class PathTraceDisplay; +class OutputDriver; class Progress; class TileManager; @@ -99,7 +100,10 @@ class PathTrace { * Use this to configure the adaptive sampler before rendering any samples. */ void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling); - /* Set display driver which takes care of drawing the render result. */ + /* Sets output driver for render buffer output. */ + void set_output_driver(unique_ptr<OutputDriver> driver); + + /* Set display driver for interactive render buffer display. */ void set_display_driver(unique_ptr<DisplayDriver> driver); /* Clear the display buffer by filling it in with all zeroes. */ @@ -158,6 +162,7 @@ class PathTrace { * instead. */ int2 get_render_tile_size() const; int2 get_render_tile_offset() const; + int2 get_render_size() const; /* Get buffer parameters of the current tile. * @@ -169,18 +174,6 @@ class PathTrace { * times, and so on. */ string full_report() const; - /* Callback which communicates an updates state of the render buffer of the current big tile. - * Is called during path tracing to communicate work-in-progress state of the final buffer. */ - function<void(void)> tile_buffer_update_cb; - - /* Callback which communicates final rendered buffer. Is called after path-tracing is done. */ - function<void(void)> tile_buffer_write_cb; - - /* Callback which initializes rendered buffer. Is called before path-tracing starts. - * - * This is used for baking. */ - function<bool(void)> tile_buffer_read_cb; - /* Callback which is called to report current rendering progress. * * It is supposed to be cheaper than buffer update/write, hence can be called more often. @@ -253,8 +246,12 @@ class PathTrace { RenderScheduler &render_scheduler_; TileManager &tile_manager_; + /* Display driver for interactive render buffer display. */ unique_ptr<PathTraceDisplay> display_; + /* Output driver to write render buffer to. */ + unique_ptr<OutputDriver> output_driver_; + /* Per-compute device descriptors of work which is responsible for path tracing on its configured * device. */ vector<unique_ptr<PathTraceWork>> path_trace_works_; diff --git a/intern/cycles/integrator/path_trace_tile.cpp b/intern/cycles/integrator/path_trace_tile.cpp new file mode 100644 index 00000000000..540f4aa5f68 --- /dev/null +++ b/intern/cycles/integrator/path_trace_tile.cpp @@ -0,0 +1,107 @@ +/* + * 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_tile.h" +#include "integrator/pass_accessor_cpu.h" +#include "integrator/path_trace.h" + +#include "render/buffers.h" +#include "render/film.h" +#include "render/pass.h" +#include "render/scene.h" + +CCL_NAMESPACE_BEGIN + +PathTraceTile::PathTraceTile(PathTrace &path_trace) + : OutputDriver::Tile(path_trace.get_render_tile_offset(), + path_trace.get_render_tile_size(), + path_trace.get_render_size(), + path_trace.get_render_tile_params().layer, + path_trace.get_render_tile_params().view), + path_trace_(path_trace), + copied_from_device_(false) +{ +} + +bool PathTraceTile::get_pass_pixels(const string_view pass_name, + const int num_channels, + float *pixels) const +{ + /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification + * is happening while this function runs. */ + + if (!copied_from_device_) { + /* Copy from device on demand. */ + path_trace_.copy_render_tile_from_device(); + const_cast<PathTraceTile *>(this)->copied_from_device_ = true; + } + + const BufferParams &buffer_params = path_trace_.get_render_tile_params(); + + const BufferPass *pass = buffer_params.find_pass(pass_name); + if (pass == nullptr) { + return false; + } + + const bool has_denoised_result = path_trace_.has_denoised_result(); + if (pass->mode == PassMode::DENOISED && !has_denoised_result) { + pass = buffer_params.find_pass(pass->type); + if (pass == nullptr) { + /* Happens when denoised result pass is requested but is never written by the kernel. */ + return false; + } + } + + pass = buffer_params.get_actual_display_pass(pass); + + const float exposure = buffer_params.exposure; + const int num_samples = path_trace_.get_num_render_tile_samples(); + + PassAccessor::PassAccessInfo pass_access_info(*pass); + pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher; + pass_access_info.use_approximate_shadow_catcher_background = + pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background; + + const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); + const PassAccessor::Destination destination(pixels, num_channels); + + return path_trace_.get_render_tile_pixels(pass_accessor, destination); +} + +bool PathTraceTile::set_pass_pixels(const string_view pass_name, + const int num_channels, + const float *pixels) const +{ + /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification + * is happening while this function runs. */ + + const BufferParams &buffer_params = path_trace_.get_render_tile_params(); + const BufferPass *pass = buffer_params.find_pass(pass_name); + if (!pass) { + return false; + } + + const float exposure = buffer_params.exposure; + const int num_samples = 1; + + const PassAccessor::PassAccessInfo pass_access_info(*pass); + PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); + PassAccessor::Source source(pixels, num_channels); + + return path_trace_.set_render_tile_pixels(pass_accessor, source); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_tile.h b/intern/cycles/integrator/path_trace_tile.h new file mode 100644 index 00000000000..fd3e2969f6c --- /dev/null +++ b/intern/cycles/integrator/path_trace_tile.h @@ -0,0 +1,43 @@ +/* + * 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/output_driver.h" + +CCL_NAMESPACE_BEGIN + +/* PathTraceTile + * + * Implementation of OutputDriver::Tile interface for path tracer. */ + +class PathTrace; + +class PathTraceTile : public OutputDriver::Tile { + public: + PathTraceTile(PathTrace &path_trace); + + bool get_pass_pixels(const string_view pass_name, const int num_channels, float *pixels) const; + bool set_pass_pixels(const string_view pass_name, + const int num_channels, + const float *pixels) const; + + private: + PathTrace &path_trace_; + bool copied_from_device_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index ce1a9e5f430..323222b8c85 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -78,6 +78,7 @@ set(SRC_HEADERS constant_fold.h denoising.h display_driver.h + output_driver.h film.h geometry.h graph.h diff --git a/intern/cycles/render/output_driver.h b/intern/cycles/render/output_driver.h new file mode 100644 index 00000000000..b7e980d71d4 --- /dev/null +++ b/intern/cycles/render/output_driver.h @@ -0,0 +1,82 @@ +/* + * 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 "util/util_math.h" +#include "util/util_string.h" +#include "util/util_types.h" + +CCL_NAMESPACE_BEGIN + +/* Output driver for reading render buffers. + * + * Host applications implement this interface for outputting render buffers for offline rendering. + * Drivers can be used to copy the buffers into the host application or write them directly to + * disk. This interface may also be used for interactive display, however the DisplayDriver is more + * efficient for that purpose. + */ +class OutputDriver { + public: + OutputDriver() = default; + virtual ~OutputDriver() = default; + + class Tile { + public: + Tile(const int2 offset, + const int2 size, + const int2 full_size, + const string_view layer, + const string_view view) + : offset(offset), size(size), full_size(full_size), layer(layer), view(view) + { + } + virtual ~Tile() = default; + + const int2 offset; + const int2 size; + const int2 full_size; + const string layer; + const string view; + + virtual bool get_pass_pixels(const string_view pass_name, + const int num_channels, + float *pixels) const = 0; + virtual bool set_pass_pixels(const string_view pass_name, + const int num_channels, + const float *pixels) const = 0; + }; + + /* Write tile once it has finished rendering. */ + virtual void write_render_tile(const Tile &tile) = 0; + + /* Update tile while rendering is in progress. Return true if any update + * was performed. */ + virtual bool update_render_tile(const Tile & /* tile */) + { + return false; + } + + /* For baking, read render pass PASS_BAKE_PRIMITIVE and PASS_BAKE_DIFFERENTIAL + * to determine which shading points to use for baking at each pixel. Return + * true if any data was read. */ + virtual bool read_render_tile(const Tile & /* tile */) + { + return false; + } +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index c191b9a9b4a..550188b196a 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -31,6 +31,7 @@ #include "render/light.h" #include "render/mesh.h" #include "render/object.h" +#include "render/output_driver.h" #include "render/scene.h" #include "render/session.h" @@ -64,25 +65,6 @@ Session::Session(const SessionParams ¶ms_, const SceneParams &scene_params) path_trace_ = make_unique<PathTrace>( device, scene->film, &scene->dscene, render_scheduler_, tile_manager_); path_trace_->set_progress(&progress); - path_trace_->tile_buffer_update_cb = [&]() { - if (!update_render_tile_cb) { - return; - } - update_render_tile_cb(); - }; - path_trace_->tile_buffer_write_cb = [&]() { - if (!write_render_tile_cb) { - return; - } - write_render_tile_cb(); - }; - path_trace_->tile_buffer_read_cb = [&]() -> bool { - if (!read_render_tile_cb) { - return false; - } - read_render_tile_cb(); - return true; - }; path_trace_->progress_update_cb = [&]() { update_status_time(); }; tile_manager_.full_buffer_written_cb = [&](string_view filename) { @@ -97,24 +79,6 @@ Session::~Session() { cancel(); - /* TODO(sergey): Bring the passes in viewport back. - * It is unclear why there is such an exception needed though. */ -#if 0 - if (buffers && params.write_render_cb) { - /* Copy to display buffer and write out image if requested */ - delete display; - - display = new DisplayBuffer(device, false); - display->reset(buffers->params); - copy_to_display_buffer(params.samples); - - int w = display->draw_width; - int h = display->draw_height; - uchar4 *pixels = display->rgba_byte.copy_from_device(0, w, h); - params.write_render_cb((uchar *)pixels, w, h, 4); - } -#endif - /* Make sure path tracer is destroyed before the device. This is needed because destruction might * need to access device for device memory free. */ /* TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the @@ -514,6 +478,11 @@ void Session::set_pause(bool pause) } } +void Session::set_output_driver(unique_ptr<OutputDriver> driver) +{ + path_trace_->set_output_driver(move(driver)); +} + void Session::set_display_driver(unique_ptr<DisplayDriver> driver) { path_trace_->set_display_driver(move(driver)); @@ -637,101 +606,6 @@ void Session::collect_statistics(RenderStats *render_stats) } /* -------------------------------------------------------------------- - * Tile and tile pixels access. - */ - -bool Session::has_multiple_render_tiles() const -{ - return tile_manager_.has_multiple_tiles(); -} - -int2 Session::get_render_tile_size() const -{ - return path_trace_->get_render_tile_size(); -} - -int2 Session::get_render_tile_offset() const -{ - return path_trace_->get_render_tile_offset(); -} - -string_view Session::get_render_tile_layer() const -{ - const BufferParams &buffer_params = path_trace_->get_render_tile_params(); - return buffer_params.layer; -} - -string_view Session::get_render_tile_view() const -{ - const BufferParams &buffer_params = path_trace_->get_render_tile_params(); - return buffer_params.view; -} - -bool Session::copy_render_tile_from_device() -{ - return path_trace_->copy_render_tile_from_device(); -} - -bool Session::get_render_tile_pixels(const string &pass_name, int num_components, float *pixels) -{ - /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification - * is happening while this function runs. */ - - const BufferParams &buffer_params = path_trace_->get_render_tile_params(); - - const BufferPass *pass = buffer_params.find_pass(pass_name); - if (pass == nullptr) { - return false; - } - - const bool has_denoised_result = path_trace_->has_denoised_result(); - if (pass->mode == PassMode::DENOISED && !has_denoised_result) { - pass = buffer_params.find_pass(pass->type); - if (pass == nullptr) { - /* Happens when denoised result pass is requested but is never written by the kernel. */ - return false; - } - } - - pass = buffer_params.get_actual_display_pass(pass); - - const float exposure = buffer_params.exposure; - const int num_samples = path_trace_->get_num_render_tile_samples(); - - PassAccessor::PassAccessInfo pass_access_info(*pass); - pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher; - pass_access_info.use_approximate_shadow_catcher_background = - pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background; - - const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); - const PassAccessor::Destination destination(pixels, num_components); - - return path_trace_->get_render_tile_pixels(pass_accessor, destination); -} - -bool Session::set_render_tile_pixels(const string &pass_name, - int num_components, - const float *pixels) -{ - /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification - * is happening while this function runs. */ - - const BufferPass *pass = buffer_params_.find_pass(pass_name); - if (!pass) { - return false; - } - - const float exposure = scene->film->get_exposure(); - const int num_samples = render_scheduler_.get_num_rendered_samples(); - - const PassAccessor::PassAccessInfo pass_access_info(*pass); - PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); - PassAccessor::Source source(pixels, num_components); - - return path_trace_->set_render_tile_pixels(pass_accessor, source); -} - -/* -------------------------------------------------------------------- * Full-frame on-disk storage. */ diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index 607e40c47c1..46c964bc98c 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -36,6 +36,7 @@ class BufferParams; class Device; class DeviceScene; class DisplayDriver; +class OutputDriver; class PathTrace; class Progress; class RenderBuffers; @@ -67,8 +68,6 @@ class SessionParams { ShadingSystem shadingsystem; - function<bool(const uchar *pixels, int width, int height, int channels)> write_render_cb; - SessionParams() { headless = false; @@ -114,10 +113,6 @@ class Session { Stats stats; Profiler profiler; - function<void(void)> write_render_tile_cb; - function<void(void)> update_render_tile_cb; - function<void(void)> read_render_tile_cb; - /* Callback is invoked by tile manager whenever on-dist tiles storage file is closed after * writing. Allows an engine integration to keep track of those files without worry about * transferring the information when it needs to re-create session during rendering. */ @@ -143,6 +138,7 @@ class Session { void set_samples(int samples); void set_time_limit(double time_limit); + void set_output_driver(unique_ptr<OutputDriver> driver); void set_display_driver(unique_ptr<DisplayDriver> driver); double get_estimated_remaining_time() const; @@ -156,24 +152,6 @@ class Session { void collect_statistics(RenderStats *stats); /* -------------------------------------------------------------------- - * Tile and tile pixels access. - */ - - bool has_multiple_render_tiles() const; - - /* Get size and offset (relative to the buffer's full x/y) of the currently rendering tile. */ - int2 get_render_tile_size() const; - int2 get_render_tile_offset() const; - - string_view get_render_tile_layer() const; - string_view get_render_tile_view() const; - - bool copy_render_tile_from_device(); - - bool get_render_tile_pixels(const string &pass_name, int num_components, float *pixels); - bool set_render_tile_pixels(const string &pass_name, int num_components, const float *pixels); - - /* -------------------------------------------------------------------- * Full-frame on-disk storage. */ diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 4ab2e856c5d..7e53a9d0911 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -420,6 +420,11 @@ const Tile &TileManager::get_current_tile() const return tile_state_.current_tile; } +const int2 TileManager::get_size() const +{ + return make_int2(buffer_params_.width, buffer_params_.height); +} + bool TileManager::open_tile_output() { write_state_.filename = path_temp_get("cycles-tile-buffer-" + tile_file_unique_part_ + "-" + diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index 8392274ff79..08eaa4034f0 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -82,6 +82,7 @@ class TileManager { bool done(); const Tile &get_current_tile() const; + const int2 get_size() const; /* Write render buffer of a tile to a file on disk. * |