From b1450953d38b907cd0fe10c1e2d6d20e3c164968 Mon Sep 17 00:00:00 2001 From: Kevin Masson Date: Mon, 30 Jul 2018 09:55:29 +0200 Subject: [PATCH] Expose checkpoint option and save/restore renders - Expose a checkpoint option in cli - Create a checkpoint after each passes if "--checkpoint" is on - Restore from a checkpoint if "--checkpoint" is on - Store and restore checkpoints to/from the given output file - Store multi pass informations in diagnostic AOVs - Store only the last samples in depth, normal and uv AOVs - Handle denoiser when restoring a checkpoint (informations are not saved in the same checkpoint file) - Refactor diagnostic and pixel time AOVs - Fix passes option paths in cli and studio - Clean-up tile bbox code snippets - Minor changes --- src/appleseed.cli/commandlinehandler.cpp | 5 + src/appleseed.cli/commandlinehandler.h | 1 + src/appleseed.cli/main.cpp | 34 +- .../mainwindow/renderingsettingswindow.cpp | 2 +- src/appleseed/CMakeLists.txt | 10 +- .../foundation/image/exrimagefilewriter.cpp | 19 +- .../genericprogressiveimagefilereader.cpp | 120 +++- .../image/genericprogressiveimagefilereader.h | 11 + src/appleseed/foundation/image/image.h | 1 + src/appleseed/renderer/api/aov.h | 4 +- .../renderer/kernel/aov/aovaccumulator.cpp | 28 +- .../renderer/kernel/aov/aovaccumulator.h | 39 +- .../rendering/final/adaptivepixelrenderer.cpp | 15 +- .../rendering/final/adaptivetilerenderer.cpp | 21 +- .../generic/genericframerenderer.cpp | 601 +++++++++++++++++- .../rendering/generic/genericframerenderer.h | 34 +- .../rendering/generic/generictilerenderer.cpp | 21 +- ...rmanentshadingresultframebufferfactory.cpp | 34 +- ...permanentshadingresultframebufferfactory.h | 23 +- .../kernel/rendering/renderercomponents.cpp | 7 + src/appleseed/renderer/modeling/aov/aov.cpp | 96 +-- src/appleseed/renderer/modeling/aov/aov.h | 32 +- .../modeling/aov/aovfactoryregistrar.cpp | 4 +- .../renderer/modeling/aov/denoiseraov.cpp | 24 +- .../renderer/modeling/aov/denoiseraov.h | 7 + .../renderer/modeling/aov/depthaov.cpp | 45 +- .../renderer/modeling/aov/diagnosticaov.cpp | 500 --------------- .../modeling/aov/invalidsamplesaov.cpp | 258 ++++++++ .../{diagnosticaov.h => invalidsamplesaov.h} | 58 +- .../renderer/modeling/aov/normalaov.cpp | 69 +- .../renderer/modeling/aov/npraovs.cpp | 24 +- .../modeling/aov/pixelsamplecountaov.cpp | 167 +++++ .../modeling/aov/pixelsamplecountaov.h | 77 +++ .../renderer/modeling/aov/pixeltimeaov.cpp | 64 +- .../modeling/aov/pixelvariationaov.cpp | 166 +++++ .../renderer/modeling/aov/pixelvariationaov.h | 77 +++ .../renderer/modeling/aov/positionaov.cpp | 69 +- src/appleseed/renderer/modeling/aov/uvaov.cpp | 60 +- .../renderer/modeling/frame/frame.cpp | 37 +- src/appleseed/renderer/modeling/frame/frame.h | 6 +- .../renderer/modeling/input/texturesource.cpp | 1 - .../modeling/project/configuration.cpp | 2 - src/appleseed/renderer/utility/bbox.h | 53 ++ src/appleseed/renderer/utility/filesystem.cpp | 63 ++ src/appleseed/renderer/utility/filesystem.h | 46 ++ 45 files changed, 2036 insertions(+), 999 deletions(-) delete mode 100644 src/appleseed/renderer/modeling/aov/diagnosticaov.cpp create mode 100644 src/appleseed/renderer/modeling/aov/invalidsamplesaov.cpp rename src/appleseed/renderer/modeling/aov/{diagnosticaov.h => invalidsamplesaov.h} (63%) create mode 100644 src/appleseed/renderer/modeling/aov/pixelsamplecountaov.cpp create mode 100644 src/appleseed/renderer/modeling/aov/pixelsamplecountaov.h create mode 100644 src/appleseed/renderer/modeling/aov/pixelvariationaov.cpp create mode 100644 src/appleseed/renderer/modeling/aov/pixelvariationaov.h create mode 100644 src/appleseed/renderer/utility/filesystem.cpp create mode 100644 src/appleseed/renderer/utility/filesystem.h diff --git a/src/appleseed.cli/commandlinehandler.cpp b/src/appleseed.cli/commandlinehandler.cpp index 628f948095..7bfd10e6ee 100644 --- a/src/appleseed.cli/commandlinehandler.cpp +++ b/src/appleseed.cli/commandlinehandler.cpp @@ -143,6 +143,11 @@ CommandLineHandler::CommandLineHandler() .set_description("display the output image")); #endif + parser().add_option_handler( + &m_checkpoint + .add_name("--checkpoint") + .set_description("save a rendering checkpoint on each passes or resume rendering with a previous checkpoint")); + parser().add_option_handler( &m_send_to_mplay .add_name("--to-mplay") diff --git a/src/appleseed.cli/commandlinehandler.h b/src/appleseed.cli/commandlinehandler.h index 9dd296a314..3f98d7d3ce 100644 --- a/src/appleseed.cli/commandlinehandler.h +++ b/src/appleseed.cli/commandlinehandler.h @@ -75,6 +75,7 @@ class CommandLineHandler #if defined __APPLE__ || defined _WIN32 foundation::FlagOptionHandler m_display_output; #endif + foundation::FlagOptionHandler m_checkpoint; foundation::FlagOptionHandler m_send_to_stdout; foundation::FlagOptionHandler m_send_to_mplay; foundation::ValueOptionHandler m_send_to_hrmanpipe; diff --git a/src/appleseed.cli/main.cpp b/src/appleseed.cli/main.cpp index d781bdbdba..727b28dbcf 100644 --- a/src/appleseed.cli/main.cpp +++ b/src/appleseed.cli/main.cpp @@ -303,7 +303,7 @@ namespace if (g_cl.m_passes.is_set()) { params.insert_path( - "generic_frame_renderer.passes", + "passes", g_cl.m_passes.values()[0]); } } @@ -336,6 +336,26 @@ namespace apply_visibility_command_line_options(assembly, show_filter, hide_filter); } + bool apply_checkpoint_command_line_option(ParamArray& params) + { + if (g_cl.m_checkpoint.is_set()) + { + if (!g_cl.m_output.is_set()) + { + LOG_ERROR(g_logger, "ouput path must be specified when using checkpoints"); + return false; + } + + const char* file_path = g_cl.m_output.value().c_str(); + LOG_DEBUG(g_logger, "will save checkpoint to %s", file_path); + + params.insert_path("generic_frame_renderer.checkpoint", true); + params.insert_path("generic_frame_renderer.checkpoint_path", file_path); + } + + return true; + } + void apply_parameter_command_line_options(ParamArray& params) { for (size_t i = 0; i < g_cl.m_params.values().size(); ++i) @@ -353,7 +373,7 @@ namespace } } - void apply_command_line_options(Project& project, ParamArray& params) + bool apply_command_line_options(Project& project, ParamArray& params) { // Apply --disable-autosave option. if (g_cl.m_disable_autosave.is_set()) @@ -397,8 +417,14 @@ namespace g_cl.m_hide_object_instances.is_set() ? g_cl.m_hide_object_instances.value() : "(?!)"); // match nothing } + // Apply --checkpoint option. + if (!apply_checkpoint_command_line_option(params)) + return false; + // Apply --parameter options. apply_parameter_command_line_options(params); + + return true; } #if defined __APPLE__ || defined _WIN32 @@ -466,9 +492,7 @@ namespace params.merge(configuration->get_parameters()); // Apply the command line options. - apply_command_line_options(project, params); - - return true; + return apply_command_line_options(project, params); } bool is_progressive_render(const ParamArray& params) diff --git a/src/appleseed.studio/mainwindow/renderingsettingswindow.cpp b/src/appleseed.studio/mainwindow/renderingsettingswindow.cpp index 997527d234..d26eaddda8 100644 --- a/src/appleseed.studio/mainwindow/renderingsettingswindow.cpp +++ b/src/appleseed.studio/mainwindow/renderingsettingswindow.cpp @@ -536,7 +536,7 @@ namespace SLOT(slot_changed_image_plane_sampler(const int))); m_image_plane_sampler_passes = create_integer_input("general.passes", 1, 1000000, 1); - m_image_plane_sampler_passes->setToolTip(m_params_metadata.get_path("generic_frame_renderer.passes.help")); + m_image_plane_sampler_passes->setToolTip(m_params_metadata.get_path("passes.help")); sublayout->addRow("Passes:", m_image_plane_sampler_passes); } diff --git a/src/appleseed/CMakeLists.txt b/src/appleseed/CMakeLists.txt index f3f84973be..cba628e640 100644 --- a/src/appleseed/CMakeLists.txt +++ b/src/appleseed/CMakeLists.txt @@ -1411,8 +1411,6 @@ set (renderer_modeling_aov_sources renderer/modeling/aov/denoiseraov.h renderer/modeling/aov/depthaov.cpp renderer/modeling/aov/depthaov.h - renderer/modeling/aov/diagnosticaov.cpp - renderer/modeling/aov/diagnosticaov.h renderer/modeling/aov/diffuseaov.cpp renderer/modeling/aov/diffuseaov.h renderer/modeling/aov/emissionaov.cpp @@ -1420,12 +1418,18 @@ set (renderer_modeling_aov_sources renderer/modeling/aov/glossyaov.cpp renderer/modeling/aov/glossyaov.h renderer/modeling/aov/iaovfactory.h + renderer/modeling/aov/invalidsamplesaov.cpp + renderer/modeling/aov/invalidsamplesaov.h renderer/modeling/aov/normalaov.cpp renderer/modeling/aov/normalaov.h renderer/modeling/aov/npraovs.cpp renderer/modeling/aov/npraovs.h + renderer/modeling/aov/pixelsamplecountaov.cpp + renderer/modeling/aov/pixelsamplecountaov.h renderer/modeling/aov/pixeltimeaov.cpp renderer/modeling/aov/pixeltimeaov.h + renderer/modeling/aov/pixelvariationaov.cpp + renderer/modeling/aov/pixelvariationaov.h renderer/modeling/aov/positionaov.cpp renderer/modeling/aov/positionaov.h renderer/modeling/aov/uvaov.cpp @@ -2004,6 +2008,8 @@ set (renderer_utility_sources renderer/utility/autodeskmax.h renderer/utility/bbox.h renderer/utility/dynamicspectrum.h + renderer/utility/filesystem.h + renderer/utility/filesystem.cpp renderer/utility/iostreamop.h renderer/utility/messagecontext.cpp renderer/utility/messagecontext.h diff --git a/src/appleseed/foundation/image/exrimagefilewriter.cpp b/src/appleseed/foundation/image/exrimagefilewriter.cpp index 4bf16383f4..5bdc37020d 100644 --- a/src/appleseed/foundation/image/exrimagefilewriter.cpp +++ b/src/appleseed/foundation/image/exrimagefilewriter.cpp @@ -38,6 +38,7 @@ #include "foundation/image/icanvas.h" #include "foundation/image/pixel.h" #include "foundation/image/tile.h" +#include "foundation/utility/string.h" // OpenEXR headers. #include "foundation/platform/_beginexrheaders.h" @@ -58,6 +59,7 @@ // Standard headers. #include #include +#include #include using namespace Iex; @@ -68,6 +70,14 @@ using namespace std; namespace foundation { +namespace +{ + string generate_channel_bin_name(const size_t channel_number) + { + return string("Bin_") + pad_left(to_string(channel_number), '0', 4); + } +} + // // EXRImageFileWriter class implementation. // @@ -138,7 +148,11 @@ Header build_header( pixel_type = get_imf_pixel_type(props); ChannelList channels; for (size_t c = 0; c < channel_count; ++c) - channels.insert(channel_names[c], Channel(pixel_type)); + { + channels.insert( + channel_names == nullptr ? generate_channel_bin_name(c) : channel_names[c], + Channel(pixel_type)); + } header.channels() = channels; @@ -179,7 +193,7 @@ void write_tiles( { const char* base = tile_base + c * channel_size; framebuffer.insert( - channel_names[c], + channel_names == nullptr ? generate_channel_bin_name(c) : channel_names[c], Slice( pixel_type, const_cast(base), @@ -279,6 +293,7 @@ void EXRImageFileWriter::write_multipart_exr(const char *filename) } delete impl; + impl = nullptr; } } // namespace foundation diff --git a/src/appleseed/foundation/image/genericprogressiveimagefilereader.cpp b/src/appleseed/foundation/image/genericprogressiveimagefilereader.cpp index 1196cabcbd..ba0e2aee83 100644 --- a/src/appleseed/foundation/image/genericprogressiveimagefilereader.cpp +++ b/src/appleseed/foundation/image/genericprogressiveimagefilereader.cpp @@ -78,9 +78,11 @@ struct GenericProgressiveImageFileReader::Impl throw ExceptionIOError(OIIO::geterror().c_str()); m_supports_random_access = m_input->supports("random_access") != 0; + read_spec(m_input->spec()); + } - const OIIO::ImageSpec& spec = m_input->spec(); - + void read_spec(const OIIO::ImageSpec& spec) + { m_is_tiled = spec.tile_width > 0 && spec.tile_height > 0 && spec.tile_depth > 0; size_t tile_width, tile_height; @@ -139,6 +141,7 @@ struct GenericProgressiveImageFileReader::Impl static_cast(spec.nchannels), pixel_format); } + }; GenericProgressiveImageFileReader::GenericProgressiveImageFileReader(Logger* logger) @@ -326,4 +329,117 @@ Tile* GenericProgressiveImageFileReader::read_tile( } } +void GenericProgressiveImageFileReader::read_tile( + const size_t tile_x, + const size_t tile_y, + Tile* output_tile) +{ + assert(is_open()); + assert(output_tile); + assert(output_tile->get_channel_count() == impl->m_props.m_channel_count); + assert(output_tile->get_pixel_format() == impl->m_props.m_pixel_format); + + if (impl->m_is_tiled) + { + // + // Tiled image. + // + // In appleseed, for images whose width or height are not multiples + // of the tile's width or height, border tiles are actually smaller. + // In OpenImageIO, border tiles have the same size as other tiles, + // and the image's pixel data window defines which pixels of those + // tiles actually belong to the image. + // + // Since in appleseed we don't propagate pixel data windows through + // the whole image pipeline, we make sure here to return tiles of + // the correct dimensions, at some expenses. + // + + const size_t origin_x = tile_x * impl->m_props.m_tile_width; + const size_t origin_y = tile_y * impl->m_props.m_tile_height; + const size_t tile_width = min(impl->m_props.m_tile_width, impl->m_props.m_canvas_width - origin_x); + const size_t tile_height = min(impl->m_props.m_tile_height, impl->m_props.m_canvas_height - origin_y); + + // The tile fits perfectly into the canvas. + if (tile_width == impl->m_props.m_tile_width && tile_height == impl->m_props.m_tile_height) + { + assert(output_tile->get_width() == impl->m_props.m_tile_width); + assert(output_tile->get_height() == impl->m_props.m_tile_height); + + if (!impl->m_input->read_tile( + static_cast(origin_x), + static_cast(origin_y), + 0, // z + impl->m_input->spec().format, + output_tile->get_storage())) + throw ExceptionIOError(impl->m_input->geterror().c_str()); + } + else + { + assert(output_tile->get_width() == tile_width); + assert(output_tile->get_height() == tile_height); + + unique_ptr source_tile( + new Tile( + impl->m_props.m_tile_width, + impl->m_props.m_tile_height, + impl->m_props.m_channel_count, + impl->m_props.m_pixel_format)); + + if (!impl->m_input->read_tile( + static_cast(origin_x), + static_cast(origin_y), + 0, // z + impl->m_input->spec().format, + source_tile->get_storage())) + throw ExceptionIOError(impl->m_input->geterror().c_str()); + + for (size_t y = 0; y < tile_height; ++y) + { + memcpy( + output_tile->pixel(0, y), + source_tile->pixel(0, y), + tile_width * impl->m_props.m_pixel_size); + } + } + } + else + { + // + // Scanline image. + // + + assert(output_tile->get_width() == impl->m_props.m_canvas_width); + assert(output_tile->get_height() == impl->m_props.m_canvas_height); + + if (!impl->m_supports_random_access) + { + close(); + impl->open(); + } + + if (!impl->m_input->read_image( + impl->m_input->spec().format, + output_tile->get_storage())) + throw ExceptionIOError(impl->m_input->geterror().c_str()); + } +} + +bool GenericProgressiveImageFileReader::choose_subimage(const size_t subimage) const +{ + OIIO::ImageSpec spec; + const bool success = impl->m_input->seek_subimage(subimage, 0, spec); + + if (success) + impl->read_spec(spec); + + return success; +} + +const char* GenericProgressiveImageFileReader::get_filename() const +{ + assert(is_open()); + return impl->m_filename.c_str(); +} + } // namespace foundation diff --git a/src/appleseed/foundation/image/genericprogressiveimagefilereader.h b/src/appleseed/foundation/image/genericprogressiveimagefilereader.h index 74b257cf2a..3a4f0cec42 100644 --- a/src/appleseed/foundation/image/genericprogressiveimagefilereader.h +++ b/src/appleseed/foundation/image/genericprogressiveimagefilereader.h @@ -85,6 +85,17 @@ class APPLESEED_DLLSYMBOL GenericProgressiveImageFileReader const size_t tile_x, const size_t tile_y) override; + // Read an image tile. Outputs the content in the given tile. + void read_tile( + const size_t tile_x, + const size_t tile_y, + Tile* output_tile); + + bool choose_subimage(const size_t subimage) const; + + // Return the path to the opened file. + const char* get_filename() const; + private: struct Impl; Impl* impl; diff --git a/src/appleseed/foundation/image/image.h b/src/appleseed/foundation/image/image.h index 30d50db478..ded341ebb2 100644 --- a/src/appleseed/foundation/image/image.h +++ b/src/appleseed/foundation/image/image.h @@ -1,4 +1,5 @@ + // // This source file is part of appleseed. // Visit https://appleseedhq.net/ for additional information and resources. diff --git a/src/appleseed/renderer/api/aov.h b/src/appleseed/renderer/api/aov.h index 48880cf352..cc864ae5f3 100644 --- a/src/appleseed/renderer/api/aov.h +++ b/src/appleseed/renderer/api/aov.h @@ -37,13 +37,15 @@ #include "renderer/modeling/aov/aovfactoryregistrar.h" #include "renderer/modeling/aov/aovtraits.h" #include "renderer/modeling/aov/depthaov.h" -#include "renderer/modeling/aov/diagnosticaov.h" #include "renderer/modeling/aov/diffuseaov.h" #include "renderer/modeling/aov/emissionaov.h" #include "renderer/modeling/aov/glossyaov.h" #include "renderer/modeling/aov/iaovfactory.h" +#include "renderer/modeling/aov/invalidsamplesaov.h" #include "renderer/modeling/aov/normalaov.h" +#include "renderer/modeling/aov/pixelsamplecountaov.h" #include "renderer/modeling/aov/pixeltimeaov.h" +#include "renderer/modeling/aov/pixelvariationaov.h" #include "renderer/modeling/aov/positionaov.h" #include "renderer/modeling/aov/uvaov.h" diff --git a/src/appleseed/renderer/kernel/aov/aovaccumulator.cpp b/src/appleseed/renderer/kernel/aov/aovaccumulator.cpp index 470bd90f81..ee573ae1cd 100644 --- a/src/appleseed/renderer/kernel/aov/aovaccumulator.cpp +++ b/src/appleseed/renderer/kernel/aov/aovaccumulator.cpp @@ -36,6 +36,7 @@ #include "renderer/modeling/aov/aov.h" #include "renderer/modeling/color/colorspace.h" #include "renderer/modeling/frame/frame.h" +#include "renderer/utility/bbox.h" // appleseed.foundation headers. #include "foundation/image/canvasproperties.h" @@ -138,11 +139,9 @@ namespace // UnfilteredAOVAccumulator class implementation. // -UnfilteredAOVAccumulator::UnfilteredAOVAccumulator( - Image& image, - Image& filter_image) +UnfilteredAOVAccumulator::UnfilteredAOVAccumulator(Image& image) : m_image(image) - , m_filter_image(filter_image) + , m_tile(nullptr) { } @@ -153,15 +152,22 @@ void UnfilteredAOVAccumulator::on_tile_begin( const size_t max_spp) { // Fetch the destination tile. - const CanvasProperties& props = frame.image().properties(); m_tile = &m_image.tile(tile_x, tile_y); - m_filter_tile = &m_filter_image.tile(tile_x, tile_y); - // Fetch the tile bounds (inclusive). - m_tile_origin_x = static_cast(tile_x * props.m_tile_width); - m_tile_origin_y = static_cast(tile_y * props.m_tile_height); - m_tile_end_x = static_cast(m_tile_origin_x + m_tile->get_width() - 1); - m_tile_end_y = static_cast(m_tile_origin_y + m_tile->get_height() - 1); + m_tile_bbox = compute_tile_image_space_bbox( + *m_tile, + tile_x, + tile_y); + + assert(m_tile_bbox.is_valid()); +} + +void UnfilteredAOVAccumulator::on_tile_end( + const Frame& frame, + const size_t tile_x, + const size_t tile_y) +{ + m_tile = nullptr; } diff --git a/src/appleseed/renderer/kernel/aov/aovaccumulator.h b/src/appleseed/renderer/kernel/aov/aovaccumulator.h index 4192e86190..a5b5ffc0f1 100644 --- a/src/appleseed/renderer/kernel/aov/aovaccumulator.h +++ b/src/appleseed/renderer/kernel/aov/aovaccumulator.h @@ -37,6 +37,7 @@ // appleseed.foundation headers. #include "foundation/core/concepts/noncopyable.h" #include "foundation/image/color.h" +#include "foundation/math/aabb.h" #include "foundation/math/vector.h" #include "foundation/utility/autoreleaseptr.h" @@ -117,9 +118,7 @@ class UnfilteredAOVAccumulator : public AOVAccumulator { public: - UnfilteredAOVAccumulator( - foundation::Image& image, - foundation::Image& filter_image); + UnfilteredAOVAccumulator(foundation::Image& image); void on_tile_begin( const Frame& frame, @@ -127,24 +126,20 @@ class UnfilteredAOVAccumulator const size_t tile_y, const size_t max_spp) override; + void on_tile_end( + const Frame& frame, + const size_t tile_x, + const size_t tile_y) override; + protected: foundation::Image& m_image; foundation::Tile* m_tile; - foundation::Image& m_filter_image; - foundation::Tile* m_filter_tile; - - int m_tile_origin_x; - int m_tile_origin_y; - int m_tile_end_x; - int m_tile_end_y; + foundation::AABB2i m_tile_bbox; foundation::Tile& get_tile() const; - foundation::Tile& get_filter_tile() const; bool outside_tile(const foundation::Vector2i& pi) const; - - static float square_distance_to_pixel_center(const foundation::Vector2d& ps); }; @@ -222,26 +217,10 @@ inline foundation::Tile& UnfilteredAOVAccumulator::get_tile() const return *m_tile; } -inline foundation::Tile& UnfilteredAOVAccumulator::get_filter_tile() const -{ - return *m_filter_tile; -} - inline bool UnfilteredAOVAccumulator::outside_tile( const foundation::Vector2i& pi) const { - return - pi.x < m_tile_origin_x || - pi.y < m_tile_origin_y || - pi.x > m_tile_end_x || - pi.y > m_tile_end_y; -} - -inline float UnfilteredAOVAccumulator::square_distance_to_pixel_center( - const foundation::Vector2d& ps) -{ - return static_cast( - foundation::square(ps.y - 0.5) + foundation::square(ps.y - 0.5)); + return !m_tile_bbox.is_valid() || !m_tile_bbox.contains(pi); } } // namespace renderer diff --git a/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.cpp b/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.cpp index fc75f642c8..bf6201db1e 100644 --- a/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.cpp +++ b/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.cpp @@ -282,23 +282,20 @@ namespace if ((m_sample_aov_tile || m_variation_aov_tile) && tile_bbox.contains(pt)) { Color3f value(0.0f, 0.0f, 0.0f); + m_sample_aov_tile->get_pixel(pt.x, pt.y, value); if (m_sample_aov_tile) { - value[0] = static_cast(trackers[0].get_size()); - + value[0] += static_cast(trackers[0].get_size()); m_sample_aov_tile->set_pixel(pt.x, pt.y, value); } if (m_variation_aov_tile) { - value[0] = - saturate( - max( - trackers[0].get_variation(), - trackers[1].get_variation(), - trackers[2].get_variation()) - / m_params.m_max_variation); + value[0] += max( + trackers[0].get_variation(), + trackers[1].get_variation(), + trackers[2].get_variation()); m_variation_aov_tile->set_pixel(pt.x, pt.y, value); } diff --git a/src/appleseed/renderer/kernel/rendering/final/adaptivetilerenderer.cpp b/src/appleseed/renderer/kernel/rendering/final/adaptivetilerenderer.cpp index 4ced66c1aa..e63fe931f8 100644 --- a/src/appleseed/renderer/kernel/rendering/final/adaptivetilerenderer.cpp +++ b/src/appleseed/renderer/kernel/rendering/final/adaptivetilerenderer.cpp @@ -44,6 +44,7 @@ #include "renderer/kernel/shading/shadingresult.h" #include "renderer/modeling/aov/aov.h" #include "renderer/modeling/frame/frame.h" +#include "renderer/utility/bbox.h" #include "renderer/utility/settingsparsing.h" // appleseed.foundation headers. @@ -227,22 +228,16 @@ namespace const int tile_origin_x = static_cast(frame_properties.m_tile_width * tile_x); const int tile_origin_y = static_cast(frame_properties.m_tile_height * tile_y); - // Compute the image space bounding box of the pixels to render. - AABB2i tile_bbox; - tile_bbox.min.x = tile_origin_x; - tile_bbox.min.y = tile_origin_y; - tile_bbox.max.x = tile_origin_x + static_cast(tile.get_width()) - 1; - tile_bbox.max.y = tile_origin_y + static_cast(tile.get_height()) - 1; - tile_bbox = AABB2i::intersect(tile_bbox, AABB2i(frame.get_crop_window())); + // Compute the tile space bounding box of the pixels to render. + AABB2i tile_bbox = compute_tile_space_bbox( + tile, + tile_origin_x, + tile_origin_y, + frame.get_crop_window()); + if (!tile_bbox.is_valid()) return; - // Transform the bounding box to local (tile) space. - tile_bbox.min.x -= tile_origin_x; - tile_bbox.min.y -= tile_origin_y; - tile_bbox.max.x -= tile_origin_x; - tile_bbox.max.y -= tile_origin_y; - // Pad the bounding box with tile margins. AABB2i padded_tile_bbox; padded_tile_bbox.min.x = tile_bbox.min.x - m_margin_width; diff --git a/src/appleseed/renderer/kernel/rendering/generic/genericframerenderer.cpp b/src/appleseed/renderer/kernel/rendering/generic/genericframerenderer.cpp index e426a65d72..8c0869a88d 100644 --- a/src/appleseed/renderer/kernel/rendering/generic/genericframerenderer.cpp +++ b/src/appleseed/renderer/kernel/rendering/generic/genericframerenderer.cpp @@ -33,20 +33,33 @@ // appleseed.renderer headers. #include "renderer/global/globallogger.h" #include "renderer/global/globaltypes.h" +#include "renderer/kernel/aov/imagestack.h" #include "renderer/kernel/rendering/generic/tilejob.h" #include "renderer/kernel/rendering/generic/tilejobfactory.h" #include "renderer/kernel/rendering/iframerenderer.h" #include "renderer/kernel/rendering/ipasscallback.h" +#include "renderer/kernel/rendering/ishadingresultframebufferfactory.h" #include "renderer/kernel/rendering/itilecallback.h" #include "renderer/kernel/rendering/itilerenderer.h" +#include "renderer/kernel/rendering/permanentshadingresultframebufferfactory.h" +#include "renderer/kernel/rendering/shadingresultframebuffer.h" +#include "renderer/modeling/aov/aov.h" +#include "renderer/modeling/aov/denoiseraov.h" #include "renderer/modeling/frame/frame.h" +#include "renderer/utility/bbox.h" +#include "renderer/utility/filesystem.h" #include "renderer/utility/settingsparsing.h" // appleseed.foundation headers. #include "foundation/core/concepts/noncopyable.h" +#include "foundation/math/aabb.h" #include "foundation/math/hash.h" #include "foundation/image/canvasproperties.h" +#include "foundation/image/exrimagefilewriter.h" +#include "foundation/image/genericprogressiveimagefilereader.h" #include "foundation/image/image.h" +#include "foundation/image/imageattributes.h" +#include "foundation/image/pixel.h" #include "foundation/platform/thread.h" #include "foundation/platform/types.h" #include "foundation/utility/containers/dictionary.h" @@ -55,22 +68,120 @@ #include "foundation/utility/statistics.h" #include "foundation/utility/string.h" +// BCD headers. +#include "bcd/DeepImage.h" +#include "bcd/ImageIO.h" + +// Boost headers. +#include "boost/filesystem.hpp" + // Standard headers. #include #include #include #include +#include #include using namespace boost; using namespace foundation; using namespace std; +using namespace bcd; +namespace bf = boost::filesystem; namespace renderer { namespace { + typedef vector> CheckpointProperties; + + // + // Interface used to save the rendering buffer in checkpoints. + // + + class ShadingBufferCanvas + : public ICanvas + { + public: + ShadingBufferCanvas( + const Frame& frame, + PermanentShadingResultFrameBufferFactory* buffer_factory) + : m_frame(frame) + , m_buffer_factory(buffer_factory) + , m_props( + m_frame.image().properties().m_canvas_width, + m_frame.image().properties().m_canvas_height, + m_frame.image().properties().m_tile_width, + m_frame.image().properties().m_tile_height, + (1 + m_frame.aov_images().size()) * 4 + 1, + PixelFormatFloat) + { + assert(buffer_factory); + } + + ~ShadingBufferCanvas() {} + + const CanvasProperties& properties() const override + { + return m_props; + } + + Tile& tile( + const size_t tile_x, + const size_t tile_y) override + { + ShadingResultFrameBuffer* tile_buffer = m_buffer_factory->create( + m_frame, + tile_x, + tile_y, + get_tile_bbox(tile_x, tile_y)); + + assert(tile_buffer); + return *tile_buffer; + } + + const Tile& tile( + const size_t tile_x, + const size_t tile_y) const override + { + const ShadingResultFrameBuffer* tile_buffer = m_buffer_factory->create( + m_frame, + tile_x, + tile_y, + get_tile_bbox(tile_x, tile_y)); + + assert(tile_buffer); + return *tile_buffer; + } + + AABB2i get_tile_bbox( + const size_t tile_x, + const size_t tile_y) const + { + // Compute the image space bounding box of the tile. + const int tile_origin_x = static_cast( + m_frame.image().properties().m_tile_width * tile_x); + const int tile_origin_y = static_cast( + m_frame.image().properties().m_tile_height * tile_y); + + const Tile& frame_tile = m_frame.image().tile(tile_x, tile_y); + + // Compute the tile space bounding box of the pixels to render. + return compute_tile_space_bbox( + frame_tile, + tile_origin_x, + tile_origin_y, + m_frame.get_crop_window()); + } + + private: + const Frame& m_frame; + PermanentShadingResultFrameBufferFactory* m_buffer_factory; + const CanvasProperties m_props; + }; + + // // Generic frame renderer. // @@ -80,12 +191,14 @@ namespace { public: GenericFrameRenderer( - const Frame& frame, - ITileRendererFactory* tile_renderer_factory, - ITileCallbackFactory* tile_callback_factory, - IPassCallback* pass_callback, - const ParamArray& params) + const Frame& frame, + ITileRendererFactory* tile_renderer_factory, + IShadingResultFrameBufferFactory* framebuffer_factory, + ITileCallbackFactory* tile_callback_factory, + IPassCallback* pass_callback, + const ParamArray& params) : m_frame(frame) + , m_framebuffer_factory(framebuffer_factory) , m_params(params) , m_pass_callback(pass_callback) , m_is_rendering(false) @@ -146,14 +259,22 @@ namespace " sampling mode %s\n" " rendering threads %s\n" " tile ordering %s\n" - " passes %s", + " passes %s\n" + " checkpoint %s", get_spectrum_mode_name(m_params.m_spectrum_mode).c_str(), get_sampling_context_mode_name(m_params.m_sampling_mode).c_str(), pretty_uint(m_params.m_thread_count).c_str(), m_params.m_tile_ordering == TileJobFactory::TileOrdering::LinearOrdering ? "linear" : m_params.m_tile_ordering == TileJobFactory::TileOrdering::SpiralOrdering ? "spiral" : m_params.m_tile_ordering == TileJobFactory::TileOrdering::HilbertOrdering ? "hilbert" : "random", - pretty_uint(m_params.m_pass_count).c_str()); + pretty_uint(m_params.m_pass_count).c_str(), + m_params.m_checkpoint ? "on" : "off"); + + if (m_params.m_checkpoint) + { + RENDERER_LOG_INFO( + " checkpoint path %s", m_params.m_checkpoint_path.c_str()); + } m_tile_renderers.front()->print_settings(); } @@ -188,11 +309,14 @@ namespace m_params.m_pass_count, m_params.m_spectrum_mode, m_tile_renderers, + m_framebuffer_factory, m_tile_callbacks, m_pass_callback, m_job_queue, m_params.m_thread_count, m_abort_switch, + m_params.m_checkpoint, + m_params.m_checkpoint_path, m_is_rendering)); ThreadFunctionWrapper wrapper(m_pass_manager_func.get()); m_pass_manager_thread.reset(new boost::thread(wrapper)); @@ -238,6 +362,8 @@ namespace const size_t m_thread_count; // number of rendering threads const TileJobFactory::TileOrdering m_tile_ordering; // tile rendering order const size_t m_pass_count; // number of rendering passes + const bool m_checkpoint; + const string m_checkpoint_path; explicit Parameters(const ParamArray& params) : m_spectrum_mode(get_spectrum_mode(params)) @@ -245,6 +371,8 @@ namespace , m_thread_count(get_rendering_thread_count(params)) , m_tile_ordering(get_tile_ordering(params)) , m_pass_count(params.get_optional("passes", 1)) + , m_checkpoint(params.get_optional("checkpoint", false) && m_pass_count > 1) + , m_checkpoint_path(params.get_optional("checkpoint_path", "")) { } @@ -292,15 +420,19 @@ namespace const size_t pass_count, const Spectrum::Mode spectrum_mode, vector& tile_renderers, + IShadingResultFrameBufferFactory* framebuffer_factory, vector& tile_callbacks, IPassCallback* pass_callback, JobQueue& job_queue, const size_t thread_count, IAbortSwitch& abort_switch, + const bool checkpoint, + const string checkpoint_path, bool& is_rendering) : m_frame(frame) , m_tile_ordering(tile_ordering) , m_tile_renderers(tile_renderers) + , m_framebuffer_factory(framebuffer_factory) , m_tile_callbacks(tile_callbacks) , m_pass_callback(pass_callback) , m_pass_count(pass_count) @@ -308,6 +440,8 @@ namespace , m_job_queue(job_queue) , m_thread_count(thread_count) , m_abort_switch(abort_switch) + , m_checkpoint(checkpoint) + , m_checkpoint_path(checkpoint_path) , m_is_rendering(is_rendering) { } @@ -316,11 +450,15 @@ namespace { set_current_thread_name("pass_manager"); + const size_t start_pass = try_restore_checkpoint(); + + if (!m_is_rendering) return; + // // Rendering passes. // - for (size_t pass = 0; pass < m_pass_count; ++pass) + for (size_t pass = start_pass; pass < m_pass_count; ++pass) { // Check abort flag. if (m_abort_switch.is_aborted()) @@ -375,6 +513,12 @@ namespace m_pass_callback->on_pass_end(m_frame, m_job_queue, m_abort_switch); assert(!m_job_queue.has_scheduled_or_running_jobs()); } + + // Create a checkpoint. + if (m_checkpoint) + { + save_checkpoint(pass); + } } // Check abort flag. @@ -384,6 +528,11 @@ namespace return; } + // Move the checkpoint to avoid overwritting it with the final render. + // Could be usefull when increasing number of passes. + if (m_checkpoint) + backup_checkpoint(); + // Post-process AOVs. m_frame.post_process_aov_images(); @@ -409,10 +558,394 @@ namespace m_is_rendering = false; } + size_t try_restore_checkpoint() + { + if (!m_checkpoint) return 0; + + bf::path bf_path(m_checkpoint_path.c_str()); + + // Check for the extension. Only EXR is allowed. + string extension = lower_case(bf_path.extension().string()); + if (extension != ".exr") + { + RENDERER_LOG_ERROR("checkpoint file must be an \".exr\" file"); + m_is_rendering = false; + return 0; + } + + // Check if the file exists. + if (!bf::exists(bf_path)) + { + RENDERER_LOG_WARNING("no checkpoint found, starting a new render"); + return 0; + } + + // Open the file. + GenericProgressiveImageFileReader reader; + reader.open(m_checkpoint_path.c_str()); + + // First, read layers name and properties. + CheckpointProperties checkpoint_props; + + { + size_t layer_index = 0; + while (true) + { + if (!reader.choose_subimage(layer_index)) + break; + + CanvasProperties layer_canvas_props; + ImageAttributes layer_attributes; + + reader.read_canvas_properties(layer_canvas_props); + reader.read_image_attributes(layer_attributes); + + const string layer_name = layer_attributes.exist("name") ? + layer_attributes.get("name") : + "undefined"; + + checkpoint_props.push_back( + make_tuple( + layer_name, + layer_canvas_props, + layer_attributes)); + + ++layer_index; + } + } + + // Check checkpoint's compatibility. + if (!is_checkpoint_compatible(checkpoint_props)) + { + m_is_rendering = false; + reader.close(); + return 0; + } + + const size_t start_pass = get<2>(checkpoint_props[0]).get("appleseed:LastPass") + 1; + + // Interface the shading buffer in a canvas. + PermanentShadingResultFrameBufferFactory* buffer_factory = + static_cast(m_framebuffer_factory); + + ShadingBufferCanvas shading_canvas(m_frame, buffer_factory); + + // Load tiles from the checkpoint. + const CanvasProperties& beauty_props = get<1>(checkpoint_props[0]); + + for (size_t tile_y = 0; tile_y < beauty_props.m_tile_count_y; ++tile_y) + { + for (size_t tile_x = 0; tile_x < beauty_props.m_tile_count_x; ++tile_x) + { + // Read shading buffer. + reader.choose_subimage(1); + Tile& shading_tile = shading_canvas.tile(tile_x, tile_y); + reader.read_tile(tile_x, tile_y, &shading_tile); + + // No need to read beauty and filtered AOVs because they + // are in the shading buffer. + + // Read Unfiltered AOV layers. + vector> aov_tiles; + vector aov_ptrs; + for (size_t i = 0; i < m_frame.aovs().size(); ++i) + { + UnfilteredAOV* aov = dynamic_cast( + m_frame.aovs().get_by_index(i)); + + if (aov == nullptr) + continue; + + const string aov_name = aov->get_name(); + + // Search layer index in the file. + size_t subimage_index(~0); + for (size_t s = 0; s < checkpoint_props.size(); ++s) + { + if (get<0>(checkpoint_props[s]) == aov_name) + { + subimage_index = s; + break; + } + } + + assert(subimage_index != size_t(~0)); + + Image& aov_image = aov->get_image(); + Tile& aov_tile = aov_image.tile(tile_x, tile_y); + reader.choose_subimage(subimage_index); + reader.read_tile(tile_x, tile_y, &aov_tile); + } + } + } + + reader.close(); + + // Load internal AOVs (from external files). + for (size_t i = 0, e = m_frame.internal_aovs().size(); i < e; ++i) + { + AOV* aov = m_frame.internal_aovs().get_by_index(i); + + DenoiserAOV* denoiser_aov = dynamic_cast(aov); + + // Save denoiser checkpoint. + if (denoiser_aov != nullptr) + { + load_denoiser_checkpoint(denoiser_aov); + continue; + } + } + + RENDERER_LOG_INFO("opened checkpoint resuming at pass %s", + pretty_uint(start_pass).c_str()); + + return start_pass; + } + + bool is_checkpoint_compatible(const CheckpointProperties& checkpoint_props) + { + const Image& frame_image = m_frame.image(); + const CanvasProperties& frame_props = frame_image.properties(); + const CanvasProperties& beauty_props = get<1>(checkpoint_props[0]); + const ImageAttributes& exr_attributes = get<2>(checkpoint_props[0]); + const string initial_layer_name = get<0>(checkpoint_props[0]); + + // Check for atttributes. + if (!exr_attributes.exist("appleseed:LastPass")) + { + RENDERER_LOG_ERROR("incorrect checkpoint: some attributes are missing."); + return false; + } + + // Check for beauty layer. + if (initial_layer_name != "beauty") + { + RENDERER_LOG_ERROR("incorrect checkpoint: beauty layer is missing."); + return false; + } + + // Check for weight layer. + const string second_layer_name = get<0>(checkpoint_props[1]); + if (second_layer_name != "appleseed:RenderingBuffer") + { + RENDERER_LOG_ERROR("incorrect checkpoint: rendering buffer layer is missing."); + return false; + } + + // Check that weight layer has correct amount of channel + const size_t expect_channel_count = (1 + m_frame.aov_images().size()) * 4 + 1; + if (get<1>(checkpoint_props[1]).m_channel_count != expect_channel_count) + { + RENDERER_LOG_ERROR("incorrect checkpoint: the shading buffer doesn't contain the correct number of channel."); + return false; + } + + // Check canvas properties. + if (frame_props.m_canvas_width != beauty_props.m_canvas_width + || frame_props.m_canvas_height != beauty_props.m_canvas_height + || frame_props.m_tile_width != beauty_props.m_tile_width + || frame_props.m_tile_height != beauty_props.m_tile_height + || frame_props.m_channel_count != beauty_props.m_channel_count + || frame_props.m_pixel_format != beauty_props.m_pixel_format) + { + RENDERER_LOG_ERROR("incorrect checkpoint: the beauty layer properties doesn't match the renderer properties."); + return false; + } + + // Check if aovs are here. + if (checkpoint_props.size() < m_frame.aovs().size() + 2) + { + RENDERER_LOG_ERROR("incorrect checkpoint: some aovs are missing."); + return false; + } + + // Check if denoising is enabled and pass exists. + if (m_frame.get_denoising_mode() != Frame::DenoisingMode::Off) + { + string hist_file_path, cov_file_path, sum_file_path; + denoiser_checkpoint_paths(hist_file_path, cov_file_path, sum_file_path); + + if (!bf::exists(bf::path(hist_file_path.c_str())) + || !bf::exists(bf::path(cov_file_path.c_str())) + || !bf::exists(bf::path(sum_file_path.c_str()))) + { + RENDERER_LOG_WARNING("one or more file is missing to restore denoiser's checkpoint"); + return false; + } + } + + return true; + } + + void save_checkpoint( + const size_t pass) const + { + create_parent_directories(m_checkpoint_path.c_str()); + + EXRImageFileWriter writer; + + ImageAttributes image_attributes = ImageAttributes::create_default_attributes(); + image_attributes.insert("appleseed:LastPass", pass); + + writer.begin_multipart_exr(); + + // Create layers. + // Buffer containing pixels' weight. + PermanentShadingResultFrameBufferFactory* buffer_factory = + static_cast(m_framebuffer_factory); + + ShadingBufferCanvas weight_canvas(m_frame, buffer_factory); + + // Create channel names. + static const char* ChannelNames[] = { "R", "G", "B", "A" }; // for beauty + + const size_t shading_channel_count = weight_canvas.properties().m_channel_count; + vector shading_channel_names; // for shading buffer + vector shading_channel_names_cr; + + static const string channel_name_prefix = "channel_"; + for (size_t i = 0; i < shading_channel_count; ++i) + { + shading_channel_names.push_back(channel_name_prefix + to_string(i + 1)); + shading_channel_names_cr.push_back(shading_channel_names[i].c_str()); + } + + assert(shading_channel_names.size() == shading_channel_count); + + // Add layers in the file. + writer.append_part( + "beauty", + m_frame.image(), + image_attributes, + 4, + ChannelNames); + + writer.append_part( + "appleseed:RenderingBuffer", + weight_canvas, + image_attributes, + shading_channel_count, + &shading_channel_names_cr[0]); + + // Add AOV layers. + for (size_t i = 0, e = m_frame.aovs().size(); i < e; ++i) + { + const AOV* aov = m_frame.aovs().get_by_index(i); + + const char* aov_name = aov->get_name(); + const Image& aov_image = aov->get_image(); + writer.append_part( + aov_name, + aov_image, + image_attributes, + aov->get_channel_count(), + aov->get_channel_names()); + } + + // Write the file. + writer.write_multipart_exr(m_checkpoint_path.c_str()); + + // Add internal AOVs layers (in external files). + for (size_t i = 0, e = m_frame.internal_aovs().size(); i < e; ++i) + { + const AOV* aov = m_frame.internal_aovs().get_by_index(i); + + const DenoiserAOV* denoiser_aov = dynamic_cast(aov); + + // Save denoiser checkpoint. + if (denoiser_aov != nullptr) + { + save_denoiser_checkpoint(denoiser_aov); + continue; + } + } + + RENDERER_LOG_INFO("wrote checkpoint for pass %s.", + pretty_uint(pass + 1).c_str()); + } + + void load_denoiser_checkpoint(DenoiserAOV* denoiser_aov) + { + // todo: reload denoiser checkpoint from the same file. + Deepimf& histograms_image = denoiser_aov->histograms_image(); + Deepimf& covariance_image = denoiser_aov->covariance_image(); + Deepimf& sum_image = denoiser_aov->sum_image(); + + string hist_file_path, cov_file_path, sum_file_path; + denoiser_checkpoint_paths(hist_file_path, cov_file_path, sum_file_path); + + // Load histograms. + bool result = ImageIO::loadMultiChannelsEXR(histograms_image, hist_file_path.c_str()); + // Load covariance accumulator. + result = result && ImageIO::loadMultiChannelsEXR(covariance_image, cov_file_path.c_str()); + // Load sum accumulator. + result = result && ImageIO::loadMultiChannelsEXR(sum_image, sum_file_path.c_str()); + + if (!result) + RENDERER_LOG_ERROR("could not load denoiser checkpoint."); + } + + void save_denoiser_checkpoint(const DenoiserAOV* denoiser_aov) const + { + // todo: save denoiser checkpoint in the same file. + const Deepimf& histograms_image = denoiser_aov->histograms_image(); + const Deepimf& covariance_image = denoiser_aov->covariance_image(); + const Deepimf& sum_image = denoiser_aov->sum_image(); + + string hist_file_path, cov_file_path, sum_file_path; + denoiser_checkpoint_paths(hist_file_path, cov_file_path, sum_file_path); + + // Add histograms layer. + bool result = ImageIO::writeMultiChannelsEXR(histograms_image, hist_file_path.c_str()); + // Add covariances layer. + result = result && ImageIO::writeMultiChannelsEXR(covariance_image, cov_file_path.c_str()); + // Add sum layer. + result = result && ImageIO::writeMultiChannelsEXR(sum_image, sum_file_path.c_str()); + + if (!result) + RENDERER_LOG_ERROR("could not save denoiser checkpoint."); + } + + void denoiser_checkpoint_paths(string& hist_path, string& cov_path, string& sum_path) const + { + const bf::path boost_file_path(m_checkpoint_path); + const bf::path directory = boost_file_path.parent_path(); + const string base_file_name = boost_file_path.stem().string() + ".denoiser"; + const string extension = boost_file_path.extension().string(); + + const string hist_file_name = base_file_name + ".hist" + extension; + hist_path = (directory / hist_file_name).string(); + + const string cov_file_name = base_file_name + ".cov" + extension; + cov_path = (directory / cov_file_name).string(); + + const string sum_file_name = base_file_name + ".sum" + extension; + sum_path = (directory / sum_file_name).string(); + } + + void backup_checkpoint() const + { + const bf::path boost_file_path(m_checkpoint_path); + const bf::path directory = boost_file_path.parent_path(); + const string base_file_name = boost_file_path.stem().string(); + const string extension = boost_file_path.extension().string(); + const string backup_file_name = base_file_name + ".checkpoint_backup" + extension; + + assert(bf::exists(boost_file_path)); + + // Rename last checkpoint. + bf::rename(m_checkpoint_path, backup_file_name.c_str()); + + RENDERER_LOG_INFO( + "moved checkpoint to %s, you can use it to render the scene with a higher number of passes.", + backup_file_name.c_str()); + } + private: const Frame& m_frame; const TileJobFactory::TileOrdering m_tile_ordering; vector& m_tile_renderers; + IShadingResultFrameBufferFactory* m_framebuffer_factory; vector& m_tile_callbacks; IPassCallback* m_pass_callback; const size_t m_pass_count; @@ -420,6 +953,8 @@ namespace JobQueue& m_job_queue; const size_t m_thread_count; IAbortSwitch& m_abort_switch; + const bool m_checkpoint; + const string m_checkpoint_path; bool& m_is_rendering; TileJobFactory m_tile_job_factory; @@ -454,22 +989,23 @@ namespace } }; - const Frame& m_frame; // target framebuffer - const Parameters m_params; + const Frame& m_frame; // target framebuffer + const Parameters m_params; - JobQueue m_job_queue; - unique_ptr m_job_manager; - AbortSwitch m_abort_switch; + JobQueue m_job_queue; + unique_ptr m_job_manager; + AbortSwitch m_abort_switch; - vector m_tile_renderers; // tile renderers, one per thread - vector m_tile_callbacks; // tile callbacks, none or one per thread - IPassCallback* m_pass_callback; + vector m_tile_renderers; // tile renderers, one per thread + vector m_tile_callbacks; // tile callbacks, none or one per thread + IShadingResultFrameBufferFactory* m_framebuffer_factory; + IPassCallback* m_pass_callback; - TileJobFactory m_tile_job_factory; + TileJobFactory m_tile_job_factory; - bool m_is_rendering; - unique_ptr m_pass_manager_func; - unique_ptr m_pass_manager_thread; + bool m_is_rendering; + unique_ptr m_pass_manager_func; + unique_ptr m_pass_manager_thread; void print_tile_renderers_stats() const { @@ -491,13 +1027,15 @@ namespace // GenericFrameRendererFactory::GenericFrameRendererFactory( - const Frame& frame, - ITileRendererFactory* tile_renderer_factory, - ITileCallbackFactory* tile_callback_factory, - IPassCallback* pass_callback, - const ParamArray& params) + const Frame& frame, + ITileRendererFactory* tile_renderer_factory, + IShadingResultFrameBufferFactory* framebuffer_factory, + ITileCallbackFactory* tile_callback_factory, + IPassCallback* pass_callback, + const ParamArray& params) : m_frame(frame) , m_tile_renderer_factory(tile_renderer_factory) + , m_framebuffer_factory(framebuffer_factory) , m_tile_callback_factory(tile_callback_factory) , m_pass_callback(pass_callback) , m_params(params) @@ -515,22 +1053,25 @@ IFrameRenderer* GenericFrameRendererFactory::create() new GenericFrameRenderer( m_frame, m_tile_renderer_factory, + m_framebuffer_factory, m_tile_callback_factory, m_pass_callback, m_params); } IFrameRenderer* GenericFrameRendererFactory::create( - const Frame& frame, - ITileRendererFactory* tile_renderer_factory, - ITileCallbackFactory* tile_callback_factory, - IPassCallback* pass_callback, - const ParamArray& params) + const Frame& frame, + ITileRendererFactory* tile_renderer_factory, + IShadingResultFrameBufferFactory* framebuffer_factory, + ITileCallbackFactory* tile_callback_factory, + IPassCallback* pass_callback, + const ParamArray& params) { return new GenericFrameRenderer( frame, tile_renderer_factory, + framebuffer_factory, tile_callback_factory, pass_callback, params); diff --git a/src/appleseed/renderer/kernel/rendering/generic/genericframerenderer.h b/src/appleseed/renderer/kernel/rendering/generic/genericframerenderer.h index 2ac129e0cb..0634a2e8a6 100644 --- a/src/appleseed/renderer/kernel/rendering/generic/genericframerenderer.h +++ b/src/appleseed/renderer/kernel/rendering/generic/genericframerenderer.h @@ -41,6 +41,7 @@ namespace foundation { class Dictionary; } namespace renderer { class Frame; } namespace renderer { class IPassCallback; } +namespace renderer { class IShadingResultFrameBufferFactory; } namespace renderer { class ITileCallbackFactory; } namespace renderer { class ITileRendererFactory; } @@ -57,11 +58,12 @@ class GenericFrameRendererFactory public: // Constructor. GenericFrameRendererFactory( - const Frame& frame, - ITileRendererFactory* tile_renderer_factory, - ITileCallbackFactory* tile_callback_factory, // may be 0 - IPassCallback* pass_callback, // may be 0 - const ParamArray& params); + const Frame& frame, + ITileRendererFactory* tile_renderer_factory, + IShadingResultFrameBufferFactory* framebuffer_factory, + ITileCallbackFactory* tile_callback_factory, // may be 0 + IPassCallback* pass_callback, // may be 0 + const ParamArray& params); // Delete this instance. void release() override; @@ -71,21 +73,23 @@ class GenericFrameRendererFactory // Return a new generic frame renderer instance. static IFrameRenderer* create( - const Frame& frame, - ITileRendererFactory* tile_renderer_factory, - ITileCallbackFactory* tile_callback_factory, // may be 0 - IPassCallback* pass_callback, // may be 0 - const ParamArray& params); + const Frame& frame, + ITileRendererFactory* tile_renderer_factory, + IShadingResultFrameBufferFactory* framebuffer_factory, + ITileCallbackFactory* tile_callback_factory, // may be 0 + IPassCallback* pass_callback, // may be 0 + const ParamArray& params); // Return the metadata of the generic frame renderer parameters. static foundation::Dictionary get_params_metadata(); private: - const Frame& m_frame; - ITileRendererFactory* m_tile_renderer_factory; - ITileCallbackFactory* m_tile_callback_factory; // may be 0 - IPassCallback* m_pass_callback; // may be 0 - const ParamArray m_params; + const Frame& m_frame; + ITileRendererFactory* m_tile_renderer_factory; + IShadingResultFrameBufferFactory* m_framebuffer_factory; + ITileCallbackFactory* m_tile_callback_factory; // may be 0 + IPassCallback* m_pass_callback; // may be 0 + const ParamArray m_params; }; } // namespace renderer diff --git a/src/appleseed/renderer/kernel/rendering/generic/generictilerenderer.cpp b/src/appleseed/renderer/kernel/rendering/generic/generictilerenderer.cpp index 016407fc8e..58be1bbf7d 100644 --- a/src/appleseed/renderer/kernel/rendering/generic/generictilerenderer.cpp +++ b/src/appleseed/renderer/kernel/rendering/generic/generictilerenderer.cpp @@ -41,6 +41,7 @@ #include "renderer/kernel/rendering/pixelcontext.h" #include "renderer/kernel/rendering/shadingresultframebuffer.h" #include "renderer/modeling/frame/frame.h" +#include "renderer/utility/bbox.h" // appleseed.foundation headers. #include "foundation/image/canvasproperties.h" @@ -134,22 +135,16 @@ namespace const int tile_origin_x = static_cast(frame_properties.m_tile_width * tile_x); const int tile_origin_y = static_cast(frame_properties.m_tile_height * tile_y); - // Compute the image space bounding box of the pixels to render. - AABB2i tile_bbox; - tile_bbox.min.x = tile_origin_x; - tile_bbox.min.y = tile_origin_y; - tile_bbox.max.x = tile_origin_x + static_cast(tile.get_width()) - 1; - tile_bbox.max.y = tile_origin_y + static_cast(tile.get_height()) - 1; - tile_bbox = AABB2i::intersect(tile_bbox, AABB2i(frame.get_crop_window())); + // Compute the tile space bounding box of the pixels to render. + AABB2i tile_bbox = compute_tile_space_bbox( + tile, + tile_origin_x, + tile_origin_y, + frame.get_crop_window()); + if (!tile_bbox.is_valid()) return; - // Transform the bounding box to local (tile) space. - tile_bbox.min.x -= tile_origin_x; - tile_bbox.min.y -= tile_origin_y; - tile_bbox.max.x -= tile_origin_x; - tile_bbox.max.y -= tile_origin_y; - // Pad the bounding box with tile margins. AABB2i padded_tile_bbox; padded_tile_bbox.min.x = tile_bbox.min.x - m_margin_width; diff --git a/src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.cpp b/src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.cpp index 25e0f3322a..794434c2e5 100644 --- a/src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.cpp +++ b/src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.cpp @@ -51,7 +51,7 @@ void PermanentShadingResultFrameBufferFactory::release() } PermanentShadingResultFrameBufferFactory::PermanentShadingResultFrameBufferFactory( - const Frame& frame) + const Frame& frame) { const size_t tile_count_x = frame.image().properties().m_tile_count_x; const size_t tile_count_y = frame.image().properties().m_tile_count_y; @@ -66,10 +66,10 @@ PermanentShadingResultFrameBufferFactory::~PermanentShadingResultFrameBufferFact } ShadingResultFrameBuffer* PermanentShadingResultFrameBufferFactory::create( - const Frame& frame, - const size_t tile_x, - const size_t tile_y, - const AABB2u& tile_bbox) + const Frame& frame, + const size_t tile_x, + const size_t tile_y, + const AABB2u& tile_bbox) { const size_t tile_count_x = frame.image().properties().m_tile_count_x; const size_t index = tile_y * tile_count_x + tile_x; @@ -97,4 +97,28 @@ void PermanentShadingResultFrameBufferFactory::destroy( { } +ShadingResultFrameBuffer* PermanentShadingResultFrameBufferFactory::get( + const Frame& frame, + const size_t tile_x, + const size_t tile_y) +{ + const size_t tile_count_x = frame.image().properties().m_tile_count_x; + const size_t index = tile_y * tile_count_x + tile_x; + return m_framebuffers[index]; +} + +void PermanentShadingResultFrameBufferFactory::set( + const Frame& frame, + const size_t tile_x, + const size_t tile_y, + ShadingResultFrameBuffer* buffer) +{ + const size_t tile_count_x = frame.image().properties().m_tile_count_x; + const size_t index = tile_y * tile_count_x + tile_x; + + assert(m_framebuffers[index] == nullptr); + + m_framebuffers[index] = buffer; +} + } // namespace renderer diff --git a/src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.h b/src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.h index ab6be70e50..f976dbd0b9 100644 --- a/src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.h +++ b/src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.h @@ -54,7 +54,7 @@ class PermanentShadingResultFrameBufferFactory public: // Constructor. explicit PermanentShadingResultFrameBufferFactory( - const Frame& frame); + const Frame& frame); // Destructor. ~PermanentShadingResultFrameBufferFactory() override; @@ -63,13 +63,24 @@ class PermanentShadingResultFrameBufferFactory void release() override; ShadingResultFrameBuffer* create( - const Frame& frame, - const size_t tile_x, - const size_t tile_y, - const foundation::AABB2u& tile_bbox) override; + const Frame& frame, + const size_t tile_x, + const size_t tile_y, + const foundation::AABB2u& tile_bbox) override; void destroy( - ShadingResultFrameBuffer* framebuffer) override; + ShadingResultFrameBuffer* framebuffer) override; + + ShadingResultFrameBuffer* get( + const Frame& frame, + const size_t tile_x, + const size_t tile_y); + + void set( + const Frame& frame, + const size_t tile_x, + const size_t tile_y, + ShadingResultFrameBuffer* buffer); private: std::vector m_framebuffers; diff --git a/src/appleseed/renderer/kernel/rendering/renderercomponents.cpp b/src/appleseed/renderer/kernel/rendering/renderercomponents.cpp index a408311221..b5a7c9cd0a 100644 --- a/src/appleseed/renderer/kernel/rendering/renderercomponents.cpp +++ b/src/appleseed/renderer/kernel/rendering/renderercomponents.cpp @@ -485,6 +485,12 @@ bool RendererComponents::create_frame_renderer_factory() } else if (name == "generic") { + if (m_shading_result_framebuffer_factory.get() == nullptr) + { + RENDERER_LOG_ERROR("cannot use the generic frame renderer without a shading result framebuffer."); + return false; + } + if (m_tile_renderer_factory.get() == nullptr) { RENDERER_LOG_ERROR("cannot use the generic frame renderer without a tile renderer."); @@ -495,6 +501,7 @@ bool RendererComponents::create_frame_renderer_factory() GenericFrameRendererFactory::create( m_frame, m_tile_renderer_factory.get(), + m_shading_result_framebuffer_factory.get(), m_tile_callback_factory, m_pass_callback.get(), get_child_and_inherit_globals(m_params, "generic_frame_renderer"))); diff --git a/src/appleseed/renderer/modeling/aov/aov.cpp b/src/appleseed/renderer/modeling/aov/aov.cpp index 594087d76b..1f825c7a08 100644 --- a/src/appleseed/renderer/modeling/aov/aov.cpp +++ b/src/appleseed/renderer/modeling/aov/aov.cpp @@ -34,9 +34,16 @@ // appleseed.foundation headers. #include "foundation/image/color.h" +#include "foundation/image/exrimagefilewriter.h" +#include "foundation/image/genericprogressiveimagefilereader.h" #include "foundation/image/image.h" +#include "foundation/image/imageattributes.h" + +// Standard headers. +#include using namespace foundation; +using namespace std; namespace renderer { @@ -56,8 +63,8 @@ UniqueID AOV::get_class_uid() } AOV::AOV( - const char* name, - const ParamArray& params) + const char* name, + const ParamArray& params) : Entity(g_class_uid, params) , m_image(nullptr) , m_image_index(~size_t(0)) @@ -70,31 +77,14 @@ void AOV::release() delete this; } -void AOV::create_image( - const size_t canvas_width, - const size_t canvas_height, - const size_t tile_width, - const size_t tile_height, - ImageStack& aov_images) -{ - m_image_index = aov_images.append( - get_name(), - 4, // todo: check if we can pass aov->get_channel_count() here - PixelFormatFloat); - m_image = &aov_images.get_image(m_image_index); -} - Image& AOV::get_image() const { return *m_image; } -void AOV::clear_image() -{ - m_image->clear(Color4f(0.0f)); -} - -void AOV::post_process_image(const AABB2u& crop_window) +void AOV::post_process_image( + const Frame& frame, + const AABB2u& crop_window) { } @@ -110,12 +100,12 @@ ColorAOV::ColorAOV(const char* name, const ParamArray& params) size_t ColorAOV::get_channel_count() const { - return 3; + return 4; } const char** ColorAOV::get_channel_names() const { - static const char* ChannelNames[] = {"R", "G", "B"}; + static const char* ChannelNames[] = {"R", "G", "B", "A"}; return ChannelNames; } @@ -124,6 +114,31 @@ bool ColorAOV::has_color_data() const return true; } +void ColorAOV::clear_image() +{ + m_image->clear(Color4f(0.0f)); +} + +void ColorAOV::create_image( + const size_t canvas_width, + const size_t canvas_height, + const size_t tile_width, + const size_t tile_height, + ImageStack& aov_images) +{ + m_image_index = aov_images.get_index(get_name()); + + if (m_image_index == ~size_t(0)) + { + m_image_index = aov_images.append( + get_name(), + get_channel_count(), + PixelFormatFloat); + } + + m_image = &aov_images.get_image(m_image_index); +} + // // UnfilteredAOV class implementation. @@ -131,14 +146,23 @@ bool ColorAOV::has_color_data() const UnfilteredAOV::UnfilteredAOV(const char* name, const ParamArray& params) : AOV(name, params) - , m_filter_image(nullptr) { } UnfilteredAOV::~UnfilteredAOV() { delete m_image; - delete m_filter_image; +} + +size_t UnfilteredAOV::get_channel_count() const +{ + return 3; +} + +const char** UnfilteredAOV::get_channel_names() const +{ + static const char* ChannelNames[] = {"R", "G", "B"}; + return ChannelNames; } bool UnfilteredAOV::has_color_data() const @@ -146,6 +170,11 @@ bool UnfilteredAOV::has_color_data() const return false; } +void UnfilteredAOV::clear_image() +{ + m_image->clear(Color3f(0.0f, 0.0f, 0.0f)); +} + void UnfilteredAOV::create_image( const size_t canvas_width, const size_t canvas_height, @@ -161,21 +190,6 @@ void UnfilteredAOV::create_image( tile_height, get_channel_count(), PixelFormatFloat); - - // Extra image to keep track of the distance - // to the nearest sample for each pixel. - m_filter_image = - new Image( - canvas_width, - canvas_height, - tile_width, - tile_height, - 1, - PixelFormatFloat); - - // We need to clear the image because the default channel value - // might not be zero and also to initialize the pixel distance channel. - clear_image(); } } // namespace renderer diff --git a/src/appleseed/renderer/modeling/aov/aov.h b/src/appleseed/renderer/modeling/aov/aov.h index 2d00430b2b..a7a305a8ab 100644 --- a/src/appleseed/renderer/modeling/aov/aov.h +++ b/src/appleseed/renderer/modeling/aov/aov.h @@ -46,6 +46,8 @@ // Forward declarations. namespace foundation { class Image; } +namespace foundation { class EXRImageFileWriter; } +namespace foundation { class GenericProgressiveImageFileReader; } namespace renderer { class AOVAccumulator; } namespace renderer { class AOVAccumulatorContainer; } namespace renderer { class Frame; } @@ -88,10 +90,12 @@ class APPLESEED_DLLSYMBOL AOV foundation::Image& get_image() const; // Clear the AOV image to default values. - virtual void clear_image(); + virtual void clear_image() = 0; // Apply any post processing needed to the AOV image. - virtual void post_process_image(const foundation::AABB2u& crop_window); + virtual void post_process_image( + const Frame& frame, + const foundation::AABB2u& crop_window); protected: friend class AOVAccumulatorContainer; @@ -106,7 +110,7 @@ class APPLESEED_DLLSYMBOL AOV const size_t canvas_height, const size_t tile_width, const size_t tile_height, - ImageStack& aov_images); + ImageStack& aov_images) = 0; // Create an accumulator for this AOV. virtual foundation::auto_release_ptr create_accumulator() const = 0; @@ -132,6 +136,17 @@ class ColorAOV // Return true if this AOV contains color data. bool has_color_data() const override; + + // Clear the AOV image to default values. + void clear_image() override; + + protected: + void create_image( + const size_t canvas_width, + const size_t canvas_height, + const size_t tile_width, + const size_t tile_height, + ImageStack& aov_images) override; }; @@ -149,12 +164,19 @@ class UnfilteredAOV // Destructor. ~UnfilteredAOV() override; + // Return the number of channels of this AOV. + size_t get_channel_count() const override; + + // Return the AOV channel names. + const char** get_channel_names() const override; + // Return true if this AOV contains color data. bool has_color_data() const override; - protected: - foundation::Image* m_filter_image; + // Clear the AOV image to default values. + void clear_image() override; + protected: void create_image( const size_t canvas_width, const size_t canvas_height, diff --git a/src/appleseed/renderer/modeling/aov/aovfactoryregistrar.cpp b/src/appleseed/renderer/modeling/aov/aovfactoryregistrar.cpp index 568c219f1c..3038d4773e 100644 --- a/src/appleseed/renderer/modeling/aov/aovfactoryregistrar.cpp +++ b/src/appleseed/renderer/modeling/aov/aovfactoryregistrar.cpp @@ -33,13 +33,15 @@ #include "renderer/modeling/aov/aovtraits.h" #include "renderer/modeling/aov/albedoaov.h" #include "renderer/modeling/aov/depthaov.h" -#include "renderer/modeling/aov/diagnosticaov.h" #include "renderer/modeling/aov/diffuseaov.h" #include "renderer/modeling/aov/emissionaov.h" #include "renderer/modeling/aov/glossyaov.h" +#include "renderer/modeling/aov/invalidsamplesaov.h" #include "renderer/modeling/aov/normalaov.h" #include "renderer/modeling/aov/npraovs.h" +#include "renderer/modeling/aov/pixelsamplecountaov.h" #include "renderer/modeling/aov/pixeltimeaov.h" +#include "renderer/modeling/aov/pixelvariationaov.h" #include "renderer/modeling/aov/positionaov.h" #include "renderer/modeling/aov/uvaov.h" #include "renderer/modeling/entity/registerentityfactories.h" diff --git a/src/appleseed/renderer/modeling/aov/denoiseraov.cpp b/src/appleseed/renderer/modeling/aov/denoiseraov.cpp index 39a3864279..0366947657 100644 --- a/src/appleseed/renderer/modeling/aov/denoiseraov.cpp +++ b/src/appleseed/renderer/modeling/aov/denoiseraov.cpp @@ -40,6 +40,8 @@ // appleseed.foundation headers. #include "foundation/image/color.h" +#include "foundation/image/exrimagefilewriter.h" +#include "foundation/image/genericprogressiveimagefilereader.h" #include "foundation/image/image.h" #include "foundation/utility/api/apistring.h" #include "foundation/utility/api/specializedapiarrays.h" @@ -276,7 +278,6 @@ struct DenoiserAOV::Impl Deepimf m_covariance_accum; Deepimf m_histograms; - Deepimf m_covariances; }; DenoiserAOV::DenoiserAOV( @@ -388,6 +389,26 @@ Deepimf& DenoiserAOV::histograms_image() return impl->m_histograms; } +const Deepimf& DenoiserAOV::covariance_image() const +{ + return impl->m_covariance_accum; +} + +Deepimf& DenoiserAOV::covariance_image() +{ + return impl->m_covariance_accum; +} + +const Deepimf& DenoiserAOV::sum_image() const +{ + return impl->m_sum_accum; +} + +Deepimf& DenoiserAOV::sum_image() +{ + return impl->m_sum_accum; +} + void DenoiserAOV::extract_num_samples_image(bcd::Deepimf& num_samples) const { const int w = impl->m_histograms.getWidth(); @@ -484,6 +505,7 @@ bool DenoiserAOV::write_images(const char* file_path) const return result; } + // // DenoiserAOVFactory class implementation. // diff --git a/src/appleseed/renderer/modeling/aov/denoiseraov.h b/src/appleseed/renderer/modeling/aov/denoiseraov.h index 5daff08974..7954d8f56b 100644 --- a/src/appleseed/renderer/modeling/aov/denoiseraov.h +++ b/src/appleseed/renderer/modeling/aov/denoiseraov.h @@ -33,6 +33,7 @@ #include "renderer/modeling/aov/aov.h" // appleseed.foundation headers. +#include "foundation/image/exrimagefilewriter.h" #include "foundation/utility/autoreleaseptr.h" // BCD headers. @@ -82,6 +83,12 @@ class DenoiserAOV const bcd::Deepimf& histograms_image() const; bcd::Deepimf& histograms_image(); + const bcd::Deepimf& covariance_image() const; + bcd::Deepimf& covariance_image(); + + const bcd::Deepimf& sum_image() const; + bcd::Deepimf& sum_image(); + void extract_num_samples_image(bcd::Deepimf& num_samples) const; void compute_covariances_image(bcd::Deepimf& covariances) const; diff --git a/src/appleseed/renderer/modeling/aov/depthaov.cpp b/src/appleseed/renderer/modeling/aov/depthaov.cpp index a3b24bf19b..86de096bca 100644 --- a/src/appleseed/renderer/modeling/aov/depthaov.cpp +++ b/src/appleseed/renderer/modeling/aov/depthaov.cpp @@ -67,8 +67,8 @@ namespace : public UnfilteredAOVAccumulator { public: - DepthAOVAccumulator(Image& image, Image& filter_image) - : UnfilteredAOVAccumulator(image, filter_image) + DepthAOVAccumulator(Image& image) + : UnfilteredAOVAccumulator(image) { } @@ -86,24 +86,13 @@ namespace return; float* p = reinterpret_cast( - get_tile().pixel(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y)); + get_tile().pixel(pi.x - m_tile_bbox.min.x, pi.y - m_tile_bbox.min.y)); - float* f = reinterpret_cast( - get_filter_tile().pixel(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y)); + const float depth = shading_point.hit_surface() + ? static_cast(shading_point.get_distance()) + : numeric_limits::max(); - const float min_sample_square_distance = *f; - const float sample_square_distance = - square_distance_to_pixel_center(pixel_context.get_sample_position()); - - if (sample_square_distance < min_sample_square_distance) - { - const float depth = shading_point.hit_surface() - ? static_cast(shading_point.get_distance()) - : numeric_limits::max(); - - *p = depth; - *f = sample_square_distance; - } + *p = depth; } }; @@ -147,13 +136,29 @@ namespace void clear_image() override { m_image->clear(Color(numeric_limits::max())); - m_filter_image->clear(Color(numeric_limits::max())); + } + + protected: + void create_image( + const size_t canvas_width, + const size_t canvas_height, + const size_t tile_width, + const size_t tile_height, + ImageStack& aov_images) override + { + UnfilteredAOV::create_image( + canvas_width, + canvas_height, + tile_width, + tile_height, + aov_images); + clear_image(); } auto_release_ptr create_accumulator() const override { return auto_release_ptr( - new DepthAOVAccumulator(get_image(), *m_filter_image)); + new DepthAOVAccumulator(get_image())); } }; } diff --git a/src/appleseed/renderer/modeling/aov/diagnosticaov.cpp b/src/appleseed/renderer/modeling/aov/diagnosticaov.cpp deleted file mode 100644 index 452ee8ca59..0000000000 --- a/src/appleseed/renderer/modeling/aov/diagnosticaov.cpp +++ /dev/null @@ -1,500 +0,0 @@ - -// -// This source file is part of appleseed. -// Visit https://appleseedhq.net/ for additional information and resources. -// -// This software is released under the MIT license. -// -// Copyright (c) 2018 Kevin Masson, The appleseedhq Organization -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -// Interface header. -#include "diagnosticaov.h" - -// appleseed.renderer headers. -#include "renderer/kernel/aov/aovaccumulator.h" -#include "renderer/kernel/aov/imagestack.h" -#include "renderer/kernel/rendering/pixelcontext.h" -#include "renderer/kernel/shading/shadingpoint.h" -#include "renderer/kernel/shading/shadingresult.h" -#include "renderer/modeling/aov/aov.h" -#include "renderer/modeling/frame/frame.h" - -// appleseed.foundation headers. -#include "foundation/math/aabb.h" -#include "foundation/image/color.h" -#include "foundation/image/image.h" -#include "foundation/image/tile.h" -#include "foundation/utility/api/apistring.h" -#include "foundation/utility/api/specializedapiarrays.h" -#include "foundation/utility/containers/dictionary.h" - -// Standard headers. -#include -#include - -using namespace foundation; -using namespace std; - -namespace renderer -{ - -namespace -{ - // - // Diagnostic AOV accumulator. - // - - class DiagnosticAOVAccumulator - : public AOVAccumulator - { - public: - DiagnosticAOVAccumulator() - { - } - }; - - - // - // Invalid Samples AOV accumulator. - // - - const uint8 NoState = 0; - const uint8 InvalidSample = 1; - const uint8 ValidSample = 2; - - class InvalidSamplesAOVAccumulator - : public AOVAccumulator - { - public: - explicit InvalidSamplesAOVAccumulator(Image& image) - : m_image(image) - , m_invalid_sample_count(0) - { - } - - void on_tile_begin( - const Frame& frame, - const size_t tile_x, - const size_t tile_y, - const size_t max_spp) override - { - // Create a tile that stores samples hint. - const Tile& tile = frame.image().tile(tile_x, tile_y); - - m_invalid_sample_tile.reset( - new Tile(tile.get_width(), tile.get_height(), 1, PixelFormatUInt8)); - m_invalid_sample_tile->clear(Color(0)); - - const CanvasProperties& props = frame.image().properties(); - - m_tile_origin_x = static_cast(tile_x * props.m_tile_width); - m_tile_origin_y = static_cast(tile_y * props.m_tile_height); - m_tile_end_x = static_cast(m_tile_origin_x + m_invalid_sample_tile->get_width()); - m_tile_end_y = static_cast(m_tile_origin_y + m_invalid_sample_tile->get_height()); - } - - void on_tile_end( - const Frame& frame, - const size_t tile_x, - const size_t tile_y) override - { - // Fill the tile according to samples state. - const Tile& tile = frame.image().tile(tile_x, tile_y); - Tile& aov_tile = m_image.tile(tile_x, tile_y); - - const size_t width = tile.get_width(); - const size_t height = tile.get_height(); - - for (size_t y = 0; y < height; ++y) - { - for (size_t x = 0; x < width; ++x) - { - Color sample_state; - m_invalid_sample_tile->get_pixel(x, y, sample_state); - - Color3f color; - Color4f beauty_color; - - switch (sample_state[0]) - { - case NoState: - color = Color3f(1.0f, 0.0f, 0.0f); - break; - - case InvalidSample: - color = Color3f(1.0f, 0.0f, 1.0f); - break; - - case ValidSample: - tile.get_pixel(x, y, beauty_color); - color.set(0.2f * luminance(beauty_color.rgb())); // 20% of luminance - break; - - assert_otherwise; - } - - aov_tile.set_pixel(x, y, color); - } - } - } - - void on_pixel_begin(const Vector2i& pi) override - { - m_invalid_sample_count = 0; - } - - void on_pixel_end(const Vector2i& pi) override - { - // Store a hint corresponding to the sample state in the tile. - if (pi.x >= m_tile_origin_x && - pi.y >= m_tile_origin_y && - pi.x < m_tile_end_x && - pi.y < m_tile_end_y) - { - const Vector2i pt(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y); - m_invalid_sample_tile->set_pixel(pt.x, pt.y, - m_invalid_sample_count > 0 ? &InvalidSample : &ValidSample); - } - } - - void write( - const PixelContext& pixel_context, - const ShadingPoint& shading_point, - const ShadingComponents& shading_components, - const AOVComponents& aov_components, - ShadingResult& shading_result) override - { - // Detect invalid samples. - if (!shading_result.is_valid()) - m_invalid_sample_count++; - } - - private: - Image& m_image; - size_t m_invalid_sample_count; - std::unique_ptr m_invalid_sample_tile; - int m_tile_origin_x; - int m_tile_origin_y; - int m_tile_end_x; - int m_tile_end_y; - }; - - - // - // Diagnostic AOV. - // - - class DiagnosticAOV - : public AOV - { - public: - DiagnosticAOV(const char* name, const ParamArray& params) - : AOV(name, params) - { - } - - void release() override - { - delete this; - } - - size_t get_channel_count() const override - { - return 3; - } - - const char** get_channel_names() const override - { - static const char* ChannelNames[] = {"R", "G", "B"}; - return ChannelNames; - } - - bool has_color_data() const override - { - return false; - } - - void create_image( - const size_t canvas_width, - const size_t canvas_height, - const size_t tile_width, - const size_t tile_height, - ImageStack& aov_images) override - { - m_image = - new Image( - canvas_width, - canvas_height, - tile_width, - tile_height, - get_channel_count(), - PixelFormatFloat); - } - - void clear_image() override - { - m_image->clear(Color3f(0.0f, 0.0f, 0.0f)); - } - - auto_release_ptr create_accumulator() const override - { - return auto_release_ptr( - new DiagnosticAOVAccumulator()); - } - }; - - - // - // Invalid Sample AOV. - // - - const char* Invalid_Samples_Model = "invalid_samples_aov"; - - class InvalidSamplesAOV - : public DiagnosticAOV - { - public: - explicit InvalidSamplesAOV(const ParamArray& params) - : DiagnosticAOV("invalid_samples", params) - { - } - - void release() override - { - delete this; - } - - const char* get_model() const override - { - return Invalid_Samples_Model; - } - - auto_release_ptr create_accumulator() const override - { - return auto_release_ptr( - new InvalidSamplesAOVAccumulator(get_image())); - } - }; - - - // - // Pixel Sample Count AOV. - // - - const char* Pixel_Sample_Count_Model = "pixel_sample_count_aov"; - - class PixelSampleCountAOV - : public DiagnosticAOV - { - public: - explicit PixelSampleCountAOV(const ParamArray& params) - : DiagnosticAOV("pixel_sample_count", params) - { - } - - const char* get_model() const override - { - return Pixel_Sample_Count_Model; - } - - void post_process_image(const AABB2u& crop_window) override - { - static const Color3f Blue(0.0f, 0.0f, 1.0f); - static const Color3f Red(1.0f, 0.0f, 0.0f); - - // Find the maximum and minimum samples count. - float max_samples = 0.0f; - - Color3f color; - - for (size_t y = crop_window.min.y; y <= crop_window.max.y; ++y) - { - for (size_t x = crop_window.min.x; x <= crop_window.max.x; ++x) - { - m_image->get_pixel(x, y, color); - max_samples = max(color[0], max_samples); - } - } - - // Normalize. - for (size_t y = crop_window.min.y; y <= crop_window.max.y; ++y) - { - for (size_t x = crop_window.min.x; x <= crop_window.max.x; ++x) - { - m_image->get_pixel(x, y, color); - - float c = fit(color[0], 0.0f, max_samples, 0.0f, 1.0f); - - color = lerp(Blue, Red, saturate(c)); - m_image->set_pixel(x, y, color); - } - } - } - }; - - - // - // Pixel Variation AOV. - // - - const char* Pixel_Variation_Model = "pixel_variation_aov"; - - class PixelVariationAOV - : public DiagnosticAOV - { - public: - explicit PixelVariationAOV(const ParamArray& params) - : DiagnosticAOV("pixel_variation", params) - { - } - - void post_process_image(const AABB2u& crop_window) override - { - static const Color3f Blue(0.0f, 0.0f, 1.0f); - static const Color3f Red(1.0f, 0.0f, 0.0f); - - Color3f color; - - for (size_t y = crop_window.min.y; y <= crop_window.max.y; ++y) - { - for (size_t x = crop_window.min.x; x <= crop_window.max.x; ++x) - { - m_image->get_pixel(x, y, color); - color = lerp(Blue, Red, saturate(color[0])); - m_image->set_pixel(x, y, color); - } - } - } - - const char* get_model() const override - { - return Pixel_Variation_Model; - } - }; -} - - -// -// InvalidSamplesAOVFactory class implementation. -// - -void InvalidSamplesAOVFactory::release() -{ - delete this; -} - -const char* InvalidSamplesAOVFactory::get_model() const -{ - return Invalid_Samples_Model; -} - -Dictionary InvalidSamplesAOVFactory::get_model_metadata() const -{ - return - Dictionary() - .insert("name", Invalid_Samples_Model) - .insert("label", "Invalid Samples"); -} - -DictionaryArray InvalidSamplesAOVFactory::get_input_metadata() const -{ - DictionaryArray metadata; - return metadata; -} - -auto_release_ptr InvalidSamplesAOVFactory::create( - const ParamArray& params) const -{ - return auto_release_ptr(new InvalidSamplesAOV(params)); -} - - -// -// PixelSampleCountAOVFactory class implementation. -// - -void PixelSampleCountAOVFactory::release() -{ - delete this; -} - -const char* PixelSampleCountAOVFactory::get_model() const -{ - return Pixel_Sample_Count_Model; -} - -Dictionary PixelSampleCountAOVFactory::get_model_metadata() const -{ - return - Dictionary() - .insert("name", Pixel_Sample_Count_Model) - .insert("label", "Pixel Sample Count"); -} - -DictionaryArray PixelSampleCountAOVFactory::get_input_metadata() const -{ - DictionaryArray metadata; - return metadata; -} - -auto_release_ptr PixelSampleCountAOVFactory::create( - const ParamArray& params) const -{ - return auto_release_ptr(new PixelSampleCountAOV(params)); -} - - -// -// PixelVariationAOVFactory class implementation. -// - -void PixelVariationAOVFactory::release() -{ - delete this; -} - -const char* PixelVariationAOVFactory::get_model() const -{ - return Pixel_Variation_Model; -} - -Dictionary PixelVariationAOVFactory::get_model_metadata() const -{ - return - Dictionary() - .insert("name", Pixel_Variation_Model) - .insert("label", "Pixel Variation"); -} - -DictionaryArray PixelVariationAOVFactory::get_input_metadata() const -{ - DictionaryArray metadata; - return metadata; -} - -auto_release_ptr PixelVariationAOVFactory::create( - const ParamArray& params) const -{ - return auto_release_ptr(new PixelVariationAOV(params)); -} - -} // namespace renderer diff --git a/src/appleseed/renderer/modeling/aov/invalidsamplesaov.cpp b/src/appleseed/renderer/modeling/aov/invalidsamplesaov.cpp new file mode 100644 index 0000000000..d81c7ce9c3 --- /dev/null +++ b/src/appleseed/renderer/modeling/aov/invalidsamplesaov.cpp @@ -0,0 +1,258 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2018 Kevin Masson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Interface header. +#include "invalidsamplesaov.h" + +// appleseed.renderer headers. +#include "renderer/kernel/aov/aovaccumulator.h" +#include "renderer/kernel/aov/imagestack.h" +#include "renderer/kernel/rendering/pixelcontext.h" +#include "renderer/kernel/shading/shadingpoint.h" +#include "renderer/kernel/shading/shadingresult.h" +#include "renderer/modeling/aov/aov.h" +#include "renderer/modeling/frame/frame.h" +#include "renderer/utility/bbox.h" + +// appleseed.foundation headers. +#include "foundation/math/aabb.h" +#include "foundation/image/color.h" +#include "foundation/image/image.h" +#include "foundation/image/tile.h" +#include "foundation/utility/api/apistring.h" +#include "foundation/utility/api/specializedapiarrays.h" +#include "foundation/utility/containers/dictionary.h" + +// Standard headers. +#include +#include + +using namespace foundation; +using namespace std; + +namespace renderer +{ + +namespace +{ + + // + // Invalid Samples AOV accumulator. + // + + const float NoState = 0.0f; + const float InvalidSample = 1.0f; + const float ValidSample = 2.0f; + + class InvalidSamplesAOVAccumulator + : public UnfilteredAOVAccumulator + { + public: + explicit InvalidSamplesAOVAccumulator(Image& image) + : UnfilteredAOVAccumulator(image) + , m_invalid_sample_count(0) + { + } + + void on_tile_begin( + const Frame& frame, + const size_t tile_x, + const size_t tile_y, + const size_t max_spp) + { + UnfilteredAOVAccumulator::on_tile_begin(frame, tile_x, tile_y, max_spp); + m_crop_window = frame.get_crop_window(); + } + + void on_pixel_begin(const Vector2i& pi) override + { + m_invalid_sample_count = 0; + } + + void on_pixel_end(const Vector2i& pi) override + { + // Store a hint corresponding to the sample state in the tile. + if (!outside_tile(pi) && m_crop_window.contains(pi)) + { + const Vector2i pt(pi.x - m_tile_bbox.min.x, pi.y - m_tile_bbox.min.y); + + Color3f color(0.0f); + color[0] = m_invalid_sample_count > 0 ? InvalidSample : ValidSample; + + m_tile->set_pixel(pt.x, pt.y, color); + } + } + + void write( + const PixelContext& pixel_context, + const ShadingPoint& shading_point, + const ShadingComponents& shading_components, + const AOVComponents& aov_components, + ShadingResult& shading_result) override + { + // Detect invalid samples. + if (!outside_tile(pixel_context.get_pixel_coords())) + { + if (!shading_result.is_valid()) + m_invalid_sample_count++; + } + } + + private: + size_t m_invalid_sample_count; + AABB2i m_crop_window; + }; + + + // + // Invalid Sample AOV. + // + + const char* Invalid_Samples_Model = "invalid_samples_aov"; + + class InvalidSamplesAOV + : public UnfilteredAOV + { + public: + explicit InvalidSamplesAOV(const ParamArray& params) + : UnfilteredAOV("invalid_samples", params) + { + } + + void release() override + { + delete this; + } + + const char* get_model() const override + { + return Invalid_Samples_Model; + } + + void post_process_image( + const Frame& frame, + const AABB2u& crop_window) override + { + const Image& beauty = frame.image(); + const CanvasProperties& props = beauty.properties(); + + for (size_t tile_y = 0; tile_y < props.m_tile_count_y; ++tile_y) + { + const int tile_origin_y = static_cast(props.m_tile_height * tile_y); + for (size_t tile_x = 0; tile_x < props.m_tile_count_x; ++tile_x) + { + const int tile_origin_x = static_cast(props.m_tile_width * tile_x); + + const Tile& tile = beauty.tile(tile_x, tile_y); + Tile& aov_tile = m_image->tile(tile_x, tile_y); + + // Compute the image space bounding box of the pixels to render. + AABB2i tile_bbox = compute_tile_space_bbox( + tile, + tile_origin_x, + tile_origin_y, + crop_window); + + if (!tile_bbox.is_valid()) + continue; + + for (size_t y = tile_bbox.min.y; y <= tile_bbox.max.y; ++y) + { + for (size_t x = tile_bbox.min.x; x <= tile_bbox.max.x; ++x) + { + Color3f color; + aov_tile.get_pixel(x, y, color); + + if (color[0] == NoState) + { + color = Color3f(0.0f, 1.0f, 1.0f); + } + else if (color[0] == InvalidSample) + { + color = Color3f(1.0f, 0.0f, 1.0f); + } + else if (color[0] == ValidSample) + { + Color4f beauty_color; + tile.get_pixel(x, y, beauty_color); + color.set(0.2f * luminance(beauty_color.rgb())); // 20% of luminance + } + + aov_tile.set_pixel(x, y, color); + } + } + } + } + } + + protected: + auto_release_ptr create_accumulator() const override + { + return auto_release_ptr( + new InvalidSamplesAOVAccumulator(get_image())); + } + }; +} + + +// +// InvalidSamplesAOVFactory class implementation. +// + +void InvalidSamplesAOVFactory::release() +{ + delete this; +} + +const char* InvalidSamplesAOVFactory::get_model() const +{ + return Invalid_Samples_Model; +} + +Dictionary InvalidSamplesAOVFactory::get_model_metadata() const +{ + return + Dictionary() + .insert("name", Invalid_Samples_Model) + .insert("label", "Invalid Samples"); +} + +DictionaryArray InvalidSamplesAOVFactory::get_input_metadata() const +{ + DictionaryArray metadata; + return metadata; +} + +auto_release_ptr InvalidSamplesAOVFactory::create( + const ParamArray& params) const +{ + return auto_release_ptr(new InvalidSamplesAOV(params)); +} + +} // namespace renderer + diff --git a/src/appleseed/renderer/modeling/aov/diagnosticaov.h b/src/appleseed/renderer/modeling/aov/invalidsamplesaov.h similarity index 63% rename from src/appleseed/renderer/modeling/aov/diagnosticaov.h rename to src/appleseed/renderer/modeling/aov/invalidsamplesaov.h index 728f56809b..a31cdbbe2d 100644 --- a/src/appleseed/renderer/modeling/aov/diagnosticaov.h +++ b/src/appleseed/renderer/modeling/aov/invalidsamplesaov.h @@ -26,8 +26,8 @@ // THE SOFTWARE. // -#ifndef APPLESEED_RENDERER_MODELING_AOV_DIAGNOSTICAOV_H -#define APPLESEED_RENDERER_MODELING_AOV_DIAGNOSTICAOV_H +#ifndef APPLESEED_RENDERER_MODELING_AOV_INVALIDSAMPLESAOV_H +#define APPLESEED_RENDERER_MODELING_AOV_INVALIDSAMPLESAOV_H // appleseed.renderer headers. #include "renderer/modeling/aov/iaovfactory.h" @@ -72,58 +72,6 @@ class APPLESEED_DLLSYMBOL InvalidSamplesAOVFactory const ParamArray& params) const override; }; - -// -// A factory for pixel sample count AOVs. -// - -class APPLESEED_DLLSYMBOL PixelSampleCountAOVFactory - : public IAOVFactory -{ - public: - // Delete this instance. - void release() override; - - // Return a string identifying this AOV model. - const char* get_model() const override; - - // Return metadata for this AOV model. - foundation::Dictionary get_model_metadata() const override; - - // Return metadata for the inputs of this AOV model. - foundation::DictionaryArray get_input_metadata() const override; - - // Create a new AOV instance. - foundation::auto_release_ptr create( - const ParamArray& params) const override; -}; - - -// -// A factory for pixel variation AOVs. -// - -class APPLESEED_DLLSYMBOL PixelVariationAOVFactory - : public IAOVFactory -{ - public: - // Delete this instance. - void release() override; - - // Return a string identifying this AOV model. - const char* get_model() const override; - - // Return metadata for this AOV model. - foundation::Dictionary get_model_metadata() const override; - - // Return metadata for the inputs of this AOV model. - foundation::DictionaryArray get_input_metadata() const override; - - // Create a new AOV instance. - foundation::auto_release_ptr create( - const ParamArray& params) const override; -}; - } // namespace renderer -#endif // !APPLESEED_RENDERER_MODELING_AOV_DIAGNOSTICAOV_H +#endif // !APPLESEED_RENDERER_MODELING_AOV_INVALIDSAMPLESAOV_H diff --git a/src/appleseed/renderer/modeling/aov/normalaov.cpp b/src/appleseed/renderer/modeling/aov/normalaov.cpp index 5881687988..c85501a8af 100644 --- a/src/appleseed/renderer/modeling/aov/normalaov.cpp +++ b/src/appleseed/renderer/modeling/aov/normalaov.cpp @@ -63,8 +63,8 @@ namespace : public UnfilteredAOVAccumulator { public: - NormalAOVAccumulator(Image& image, Image& filter_image) - : UnfilteredAOVAccumulator(image, filter_image) + NormalAOVAccumulator(Image& image) + : UnfilteredAOVAccumulator(image) { } @@ -82,32 +82,20 @@ namespace return; float* p = reinterpret_cast( - get_tile().pixel(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y)); + get_tile().pixel(pi.x - m_tile_bbox.min.x, pi.y - m_tile_bbox.min.y)); - float* f = reinterpret_cast( - get_filter_tile().pixel(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y)); - - const float min_sample_square_distance = *f; - const float sample_square_distance = - square_distance_to_pixel_center(pixel_context.get_sample_position()); - - if (sample_square_distance < min_sample_square_distance) + if (shading_point.hit_surface()) + { + const Vector3d& n = shading_point.get_shading_normal(); + p[0] = static_cast(n[0]) * 0.5f + 0.5f; + p[1] = static_cast(n[1]) * 0.5f + 0.5f; + p[2] = static_cast(n[2]) * 0.5f + 0.5f; + } + else { - if (shading_point.hit_surface()) - { - const Vector3d& n = shading_point.get_shading_normal(); - p[0] = static_cast(n[0]) * 0.5f + 0.5f; - p[1] = static_cast(n[1]) * 0.5f + 0.5f; - p[2] = static_cast(n[2]) * 0.5f + 0.5f; - *f = sample_square_distance; - } - else - { - p[0] = 0.5f; - p[1] = 0.5f; - p[2] = 0.5f; - *f = sample_square_distance; - } + p[0] = 0.5f; + p[1] = 0.5f; + p[2] = 0.5f; } } }; @@ -138,27 +126,32 @@ namespace return Model; } - size_t get_channel_count() const override - { - return 3; - } - - const char** get_channel_names() const override + void clear_image() override { - static const char* ChannelNames[] = {"R", "G", "B"}; - return ChannelNames; + m_image->clear(Color3f(0.5f, 0.5f, 0.5f)); } - void clear_image() override + protected: + void create_image( + const size_t canvas_width, + const size_t canvas_height, + const size_t tile_width, + const size_t tile_height, + ImageStack& aov_images) override { - m_image->clear(Color3f(0.5f, 0.5f, 0.5f)); - m_filter_image->clear(Color(numeric_limits::max())); + UnfilteredAOV::create_image( + canvas_width, + canvas_height, + tile_width, + tile_height, + aov_images); + clear_image(); } auto_release_ptr create_accumulator() const override { return auto_release_ptr( - new NormalAOVAccumulator(get_image(), *m_filter_image)); + new NormalAOVAccumulator(get_image())); } }; } diff --git a/src/appleseed/renderer/modeling/aov/npraovs.cpp b/src/appleseed/renderer/modeling/aov/npraovs.cpp index 8dcef48dc6..0a0cb61747 100644 --- a/src/appleseed/renderer/modeling/aov/npraovs.cpp +++ b/src/appleseed/renderer/modeling/aov/npraovs.cpp @@ -108,17 +108,7 @@ namespace return NPRShadingModel; } - size_t get_channel_count() const override - { - return 4; - } - - const char** get_channel_names() const override - { - static const char* ChannelNames[] = {"R", "G", "B", "A"}; - return ChannelNames; - } - + protected: auto_release_ptr create_accumulator() const override { return auto_release_ptr( @@ -181,17 +171,7 @@ namespace return NPRContourModel; } - size_t get_channel_count() const override - { - return 4; - } - - const char** get_channel_names() const override - { - static const char* ChannelNames[] = {"R", "G", "B", "A"}; - return ChannelNames; - } - + protected: auto_release_ptr create_accumulator() const override { return auto_release_ptr( diff --git a/src/appleseed/renderer/modeling/aov/pixelsamplecountaov.cpp b/src/appleseed/renderer/modeling/aov/pixelsamplecountaov.cpp new file mode 100644 index 0000000000..a89657ca2e --- /dev/null +++ b/src/appleseed/renderer/modeling/aov/pixelsamplecountaov.cpp @@ -0,0 +1,167 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2018 Kevin Masson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Interface header. +#include "pixelsamplecountaov.h" + +// appleseed.renderer headers. +#include "renderer/kernel/aov/aovaccumulator.h" +#include "renderer/kernel/aov/imagestack.h" +#include "renderer/kernel/rendering/pixelcontext.h" +#include "renderer/kernel/shading/shadingpoint.h" +#include "renderer/kernel/shading/shadingresult.h" +#include "renderer/modeling/aov/aov.h" +#include "renderer/modeling/frame/frame.h" + +// appleseed.foundation headers. +#include "foundation/math/aabb.h" +#include "foundation/image/color.h" +#include "foundation/image/image.h" +#include "foundation/image/tile.h" +#include "foundation/utility/api/apistring.h" +#include "foundation/utility/api/specializedapiarrays.h" +#include "foundation/utility/containers/dictionary.h" + +// Standard headers. +#include +#include + +using namespace foundation; +using namespace std; + +namespace renderer +{ + +namespace +{ + + // + // Pixel Sample Count AOV. + // + + const char* Pixel_Sample_Count_Model = "pixel_sample_count_aov"; + + class PixelSampleCountAOV + : public UnfilteredAOV + { + public: + explicit PixelSampleCountAOV(const ParamArray& params) + : UnfilteredAOV("pixel_sample_count", params) + { + } + + const char* get_model() const override + { + return Pixel_Sample_Count_Model; + } + + void post_process_image( + const Frame& frame, + const AABB2u& crop_window) override + { + static const Color3f Blue(0.0f, 0.0f, 1.0f); + static const Color3f Red(1.0f, 0.0f, 0.0f); + + // Find the maximum samples count. + float max_samples = 0.0f; + + Color3f color; + + for (size_t y = crop_window.min.y; y <= crop_window.max.y; ++y) + { + for (size_t x = crop_window.min.x; x <= crop_window.max.x; ++x) + { + m_image->get_pixel(x, y, color); + max_samples = max(color[0], max_samples); + } + } + + if (max_samples == 0.0f) + return; + + // Normalize. + for (size_t y = crop_window.min.y; y <= crop_window.max.y; ++y) + { + for (size_t x = crop_window.min.x; x <= crop_window.max.x; ++x) + { + m_image->get_pixel(x, y, color); + + float c = fit(color[0], 0.0f, max_samples, 0.0f, 1.0f); + + color = lerp(Blue, Red, saturate(c)); + m_image->set_pixel(x, y, color); + } + } + } + + protected: + auto_release_ptr create_accumulator() const override + { + return auto_release_ptr( + new AOVAccumulator()); + } + }; +} + + +// +// PixelSampleCountAOVFactory class implementation. +// + +void PixelSampleCountAOVFactory::release() +{ + delete this; +} + +const char* PixelSampleCountAOVFactory::get_model() const +{ + return Pixel_Sample_Count_Model; +} + +Dictionary PixelSampleCountAOVFactory::get_model_metadata() const +{ + return + Dictionary() + .insert("name", Pixel_Sample_Count_Model) + .insert("label", "Pixel Sample Count"); +} + +DictionaryArray PixelSampleCountAOVFactory::get_input_metadata() const +{ + DictionaryArray metadata; + return metadata; +} + +auto_release_ptr PixelSampleCountAOVFactory::create( + const ParamArray& params) const +{ + return auto_release_ptr(new PixelSampleCountAOV(params)); +} + +} // namespace renderer + diff --git a/src/appleseed/renderer/modeling/aov/pixelsamplecountaov.h b/src/appleseed/renderer/modeling/aov/pixelsamplecountaov.h new file mode 100644 index 0000000000..84086975c4 --- /dev/null +++ b/src/appleseed/renderer/modeling/aov/pixelsamplecountaov.h @@ -0,0 +1,77 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2018 Kevin Masson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#ifndef APPLESEED_RENDERER_MODELING_AOV_PIXELSAMPLECOUNTAOV_H +#define APPLESEED_RENDERER_MODELING_AOV_PIXELSAMPLECOUNTAOV_H + +// appleseed.renderer headers. +#include "renderer/modeling/aov/iaovfactory.h" + +// appleseed.foundation headers. +#include "foundation/utility/autoreleaseptr.h" + +// appleseed.main headers. +#include "main/dllsymbol.h" + +// Forward declarations. +namespace foundation { class Dictionary; } +namespace foundation { class DictionaryArray; } +namespace renderer { class AOV; } +namespace renderer { class ParamArray; } + +namespace renderer +{ + +// +// A factory for pixel sample count AOVs. +// + +class APPLESEED_DLLSYMBOL PixelSampleCountAOVFactory + : public IAOVFactory +{ + public: + // Delete this instance. + void release() override; + + // Return a string identifying this AOV model. + const char* get_model() const override; + + // Return metadata for this AOV model. + foundation::Dictionary get_model_metadata() const override; + + // Return metadata for the inputs of this AOV model. + foundation::DictionaryArray get_input_metadata() const override; + + // Create a new AOV instance. + foundation::auto_release_ptr create( + const ParamArray& params) const override; +}; + +} // namespace renderer + +#endif // !APPLESEED_RENDERER_MODELING_AOV_PIXELSAMPLECOUNTAOV_H diff --git a/src/appleseed/renderer/modeling/aov/pixeltimeaov.cpp b/src/appleseed/renderer/modeling/aov/pixeltimeaov.cpp index a2885a4410..f7fd89719c 100644 --- a/src/appleseed/renderer/modeling/aov/pixeltimeaov.cpp +++ b/src/appleseed/renderer/modeling/aov/pixeltimeaov.cpp @@ -65,11 +65,11 @@ namespace // class PixelTimeAOVAccumulator - : public AOVAccumulator + : public UnfilteredAOVAccumulator { public: explicit PixelTimeAOVAccumulator(Image& image) - : m_image(image) + : UnfilteredAOVAccumulator(image) { } @@ -79,15 +79,7 @@ namespace const size_t tile_y, const size_t max_spp) override { - // Fetch the destination tile. - const CanvasProperties& props = frame.image().properties(); - m_tile = &m_image.tile(tile_x, tile_y); - - // Fetch the tile bounds (inclusive). - m_tile_origin_x = static_cast(tile_x * props.m_tile_width); - m_tile_origin_y = static_cast(tile_y * props.m_tile_height); - m_tile_end_x = static_cast(m_tile_origin_x + m_tile->get_width()); - m_tile_end_y = static_cast(m_tile_origin_y + m_tile->get_height()); + UnfilteredAOVAccumulator::on_tile_begin(frame, tile_x, tile_y, max_spp); m_samples.reserve(max_spp); } @@ -128,31 +120,14 @@ namespace const double median = m_samples[mid]; float* p = reinterpret_cast( - m_tile->pixel(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y)); + get_tile().pixel(pi.x - m_tile_bbox.min.x, pi.y - m_tile_bbox.min.y)); *p += static_cast(median) * m_samples.size(); } private: - Image& m_image; - foundation::Tile* m_tile; - - int m_tile_origin_x; - int m_tile_origin_y; - int m_tile_end_x; - int m_tile_end_y; - Stopwatch m_stopwatch; std::vector m_samples; - - bool outside_tile(const Vector2i& pi) const - { - return - pi.x < m_tile_origin_x || - pi.y < m_tile_origin_y || - pi.x >= m_tile_end_x || - pi.y >= m_tile_end_y; - } }; @@ -163,11 +138,11 @@ namespace const char* PixelTimeAOVModel = "pixel_time_aov"; class PixelTimeAOV - : public AOV + : public UnfilteredAOV { public: explicit PixelTimeAOV(const ParamArray& params) - : AOV("pixel_time", params) + : UnfilteredAOV("pixel_time", params) { } @@ -192,34 +167,14 @@ namespace return ChannelNames; } - bool has_color_data() const override - { - return false; - } - - void create_image( - const size_t canvas_width, - const size_t canvas_height, - const size_t tile_width, - const size_t tile_height, - ImageStack& aov_images) override - { - m_image = - new Image( - canvas_width, - canvas_height, - tile_width, - tile_height, - get_channel_count(), - PixelFormatFloat); - } - void clear_image() override { m_image->clear(Color(0.0f)); } - void post_process_image(const AABB2u& crop_window) override + void post_process_image( + const Frame& frame, + const AABB2u& crop_window) override { // Find the maximum value. float max_time = 0.0f; @@ -254,6 +209,7 @@ namespace } } + protected: auto_release_ptr create_accumulator() const override { return auto_release_ptr(new PixelTimeAOVAccumulator(get_image())); diff --git a/src/appleseed/renderer/modeling/aov/pixelvariationaov.cpp b/src/appleseed/renderer/modeling/aov/pixelvariationaov.cpp new file mode 100644 index 0000000000..f1748209b3 --- /dev/null +++ b/src/appleseed/renderer/modeling/aov/pixelvariationaov.cpp @@ -0,0 +1,166 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2018 Kevin Masson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Interface header. +#include "pixelvariationaov.h" + +// appleseed.renderer headers. +#include "renderer/kernel/aov/aovaccumulator.h" +#include "renderer/kernel/aov/imagestack.h" +#include "renderer/kernel/rendering/pixelcontext.h" +#include "renderer/kernel/shading/shadingpoint.h" +#include "renderer/kernel/shading/shadingresult.h" +#include "renderer/modeling/aov/aov.h" +#include "renderer/modeling/frame/frame.h" + +// appleseed.foundation headers. +#include "foundation/math/aabb.h" +#include "foundation/image/color.h" +#include "foundation/image/image.h" +#include "foundation/image/tile.h" +#include "foundation/utility/api/apistring.h" +#include "foundation/utility/api/specializedapiarrays.h" +#include "foundation/utility/containers/dictionary.h" + +// Standard headers. +#include +#include + +using namespace foundation; +using namespace std; + +namespace renderer +{ + +namespace +{ + + // + // Pixel Variation AOV. + // + + const char* Pixel_Variation_Model = "pixel_variation_aov"; + + class PixelVariationAOV + : public UnfilteredAOV + { + public: + explicit PixelVariationAOV(const ParamArray& params) + : UnfilteredAOV("pixel_variation", params) + { + } + + const char* get_model() const override + { + return Pixel_Variation_Model; + } + + void post_process_image( + const Frame& frame, + const AABB2u& crop_window) override + { + static const Color3f Blue(0.0f, 0.0f, 1.0f); + static const Color3f Red(1.0f, 0.0f, 0.0f); + + // Find the maximum variation. + float max_variation = 0.0f; + + Color3f color; + + for (size_t y = crop_window.min.y; y <= crop_window.max.y; ++y) + { + for (size_t x = crop_window.min.x; x <= crop_window.max.x; ++x) + { + m_image->get_pixel(x, y, color); + max_variation = max(color[0], max_variation); + } + } + + if (max_variation == 0.0f) + return; + + // Normalize. + for (size_t y = crop_window.min.y; y <= crop_window.max.y; ++y) + { + for (size_t x = crop_window.min.x; x <= crop_window.max.x; ++x) + { + m_image->get_pixel(x, y, color); + + float c = fit(color[0], 0.0f, max_variation, 0.0f, 1.0f); + + color = lerp(Blue, Red, saturate(c)); + m_image->set_pixel(x, y, color); + } + } + } + + protected: + auto_release_ptr create_accumulator() const override + { + return auto_release_ptr( + new AOVAccumulator()); + } + }; +} + + +// +// PixelVariationAOVFactory class implementation. +// + +void PixelVariationAOVFactory::release() +{ + delete this; +} + +const char* PixelVariationAOVFactory::get_model() const +{ + return Pixel_Variation_Model; +} + +Dictionary PixelVariationAOVFactory::get_model_metadata() const +{ + return + Dictionary() + .insert("name", Pixel_Variation_Model) + .insert("label", "Pixel Variation"); +} + +DictionaryArray PixelVariationAOVFactory::get_input_metadata() const +{ + DictionaryArray metadata; + return metadata; +} + +auto_release_ptr PixelVariationAOVFactory::create( + const ParamArray& params) const +{ + return auto_release_ptr(new PixelVariationAOV(params)); +} + +} // namespace renderer diff --git a/src/appleseed/renderer/modeling/aov/pixelvariationaov.h b/src/appleseed/renderer/modeling/aov/pixelvariationaov.h new file mode 100644 index 0000000000..18f5a7a702 --- /dev/null +++ b/src/appleseed/renderer/modeling/aov/pixelvariationaov.h @@ -0,0 +1,77 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2018 Kevin Masson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#ifndef APPLESEED_RENDERER_MODELING_AOV_PIXELVARIATIONAOV_H +#define APPLESEED_RENDERER_MODELING_AOV_PIXELVARIATIONAOV_H + +// appleseed.renderer headers. +#include "renderer/modeling/aov/iaovfactory.h" + +// appleseed.foundation headers. +#include "foundation/utility/autoreleaseptr.h" + +// appleseed.main headers. +#include "main/dllsymbol.h" + +// Forward declarations. +namespace foundation { class Dictionary; } +namespace foundation { class DictionaryArray; } +namespace renderer { class AOV; } +namespace renderer { class ParamArray; } + +namespace renderer +{ + +// +// A factory for pixel variation AOVs. +// + +class APPLESEED_DLLSYMBOL PixelVariationAOVFactory + : public IAOVFactory +{ + public: + // Delete this instance. + void release() override; + + // Return a string identifying this AOV model. + const char* get_model() const override; + + // Return metadata for this AOV model. + foundation::Dictionary get_model_metadata() const override; + + // Return metadata for the inputs of this AOV model. + foundation::DictionaryArray get_input_metadata() const override; + + // Create a new AOV instance. + foundation::auto_release_ptr create( + const ParamArray& params) const override; +}; + +} // namespace renderer + +#endif // !APPLESEED_RENDERER_MODELING_AOV_PIXELVARIATIONAOV_H diff --git a/src/appleseed/renderer/modeling/aov/positionaov.cpp b/src/appleseed/renderer/modeling/aov/positionaov.cpp index 40089e97c7..247234f2fc 100644 --- a/src/appleseed/renderer/modeling/aov/positionaov.cpp +++ b/src/appleseed/renderer/modeling/aov/positionaov.cpp @@ -63,8 +63,8 @@ namespace : public UnfilteredAOVAccumulator { public: - PositionAOVAccumulator(Image& image, Image& filter_image) - : UnfilteredAOVAccumulator(image, filter_image) + PositionAOVAccumulator(Image& image) + : UnfilteredAOVAccumulator(image) { } @@ -82,32 +82,20 @@ namespace return; float* p = reinterpret_cast( - get_tile().pixel(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y)); + get_tile().pixel(pi.x - m_tile_bbox.min.x, pi.y - m_tile_bbox.min.y)); - float* f = reinterpret_cast( - get_filter_tile().pixel(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y)); - - const float min_sample_square_distance = *f; - const float sample_square_distance = - square_distance_to_pixel_center(pixel_context.get_sample_position()); - - if (sample_square_distance < min_sample_square_distance) + if (shading_point.hit_surface()) + { + const Vector3d& q = shading_point.get_point(); + p[0] = static_cast(q[0]); + p[1] = static_cast(q[1]); + p[2] = static_cast(q[2]); + } + else { - if (shading_point.hit_surface()) - { - const Vector3d& q = shading_point.get_point(); - p[0] = static_cast(q[0]); - p[1] = static_cast(q[1]); - p[2] = static_cast(q[2]); - *f = sample_square_distance; - } - else - { - p[0] = 0.5f; - p[1] = 0.5f; - p[2] = 0.5f; - *f = sample_square_distance; - } + p[0] = 0.5f; + p[1] = 0.5f; + p[2] = 0.5f; } } }; @@ -138,27 +126,32 @@ namespace return Model; } - size_t get_channel_count() const override - { - return 3; - } - - const char** get_channel_names() const override + void clear_image() override { - static const char* ChannelNames[] = {"R", "G", "B"}; - return ChannelNames; + m_image->clear(Color3f(0.5f, 0.5f, 0.5f)); } - void clear_image() override + protected: + void create_image( + const size_t canvas_width, + const size_t canvas_height, + const size_t tile_width, + const size_t tile_height, + ImageStack& aov_images) override { - m_image->clear(Color3f(0.5f, 0.5f, 0.5f)); - m_filter_image->clear(Color(numeric_limits::max())); + UnfilteredAOV::create_image( + canvas_width, + canvas_height, + tile_width, + tile_height, + aov_images); + clear_image(); } auto_release_ptr create_accumulator() const override { return auto_release_ptr( - new PositionAOVAccumulator(get_image(), *m_filter_image)); + new PositionAOVAccumulator(get_image())); } }; } diff --git a/src/appleseed/renderer/modeling/aov/uvaov.cpp b/src/appleseed/renderer/modeling/aov/uvaov.cpp index 19d431294b..3fe7b16801 100644 --- a/src/appleseed/renderer/modeling/aov/uvaov.cpp +++ b/src/appleseed/renderer/modeling/aov/uvaov.cpp @@ -63,8 +63,8 @@ namespace : public UnfilteredAOVAccumulator { public: - UVAOVAccumulator(Image& image, Image& filter_image) - : UnfilteredAOVAccumulator(image, filter_image) + UVAOVAccumulator(Image& image) + : UnfilteredAOVAccumulator(image) { } @@ -82,32 +82,20 @@ namespace return; float* p = reinterpret_cast( - get_tile().pixel(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y)); + get_tile().pixel(pi.x - m_tile_bbox.min.x, pi.y - m_tile_bbox.min.y)); - float* f = reinterpret_cast( - get_filter_tile().pixel(pi.x - m_tile_origin_x, pi.y - m_tile_origin_y)); - - const float min_sample_square_distance = *f; - const float sample_square_distance = - square_distance_to_pixel_center(pixel_context.get_sample_position()); - - if (sample_square_distance < min_sample_square_distance) + if (shading_point.hit_surface()) + { + const Vector2f& uv = shading_point.get_uv(0); + p[0] = uv[0]; + p[1] = uv[1]; + p[2] = 0.0f; + } + else { - if (shading_point.hit_surface()) - { - const Vector2f& uv = shading_point.get_uv(0); - p[0] = uv[0]; - p[1] = uv[1]; - p[2] = 0.0f; - *f = sample_square_distance; - } - else - { - p[0] = 0.0f; - p[1] = 0.0f; - p[2] = 0.0f; - *f = sample_square_distance; - } + p[0] = 0.0f; + p[1] = 0.0f; + p[2] = 0.0f; } } }; @@ -138,27 +126,11 @@ namespace return Model; } - size_t get_channel_count() const override - { - return 3; - } - - const char** get_channel_names() const override - { - static const char* ChannelNames[] = {"R", "G", "B"}; - return ChannelNames; - } - - void clear_image() override - { - m_image->clear(Color3f(0.0f, 0.0f, 0.0f)); - m_filter_image->clear(Color(numeric_limits::max())); - } - + protected: auto_release_ptr create_accumulator() const override { return auto_release_ptr( - new UVAOVAccumulator(get_image(), *m_filter_image)); + new UVAOVAccumulator(get_image())); } }; } diff --git a/src/appleseed/renderer/modeling/frame/frame.cpp b/src/appleseed/renderer/modeling/frame/frame.cpp index 732ea5a707..a9ef4f55d7 100644 --- a/src/appleseed/renderer/modeling/frame/frame.cpp +++ b/src/appleseed/renderer/modeling/frame/frame.cpp @@ -40,6 +40,7 @@ #include "renderer/modeling/aov/denoiseraov.h" #include "renderer/modeling/aov/iaovfactory.h" #include "renderer/modeling/postprocessingstage/postprocessingstage.h" +#include "renderer/utility/filesystem.h" #include "renderer/utility/paramarray.h" // appleseed.foundation headers. @@ -237,6 +238,11 @@ AOVContainer& Frame::aovs() const return impl->m_aovs; } +AOVContainer& Frame::internal_aovs() const +{ + return impl->m_internal_aovs; +} + PostProcessingStageContainer& Frame::post_processing_stages() const { return impl->m_post_processing_stages; @@ -271,11 +277,6 @@ ImageStack& Frame::aov_images() const return *impl->m_aov_images; } -const AOVContainer& Frame::internal_aovs() const -{ - return impl->m_internal_aovs; -} - const Filter2f& Frame::get_filter() const { return *impl->m_filter.get(); @@ -360,10 +361,10 @@ bool Frame::on_frame_begin( void Frame::post_process_aov_images() const { for (size_t i = 0, e = aovs().size(); i < e; ++i) - aovs().get_by_index(i)->post_process_image(get_crop_window()); + aovs().get_by_index(i)->post_process_image(*this, get_crop_window()); for (size_t i = 0, e = internal_aovs().size(); i < e; ++i) - internal_aovs().get_by_index(i)->post_process_image(get_crop_window()); + internal_aovs().get_by_index(i)->post_process_image(*this, get_crop_window()); } ParamArray& Frame::render_info() @@ -453,28 +454,6 @@ void Frame::denoise( namespace { - void create_parent_directories(const bf::path& file_path) - { - const bf::path parent_path = file_path.parent_path(); - - if (!parent_path.empty() && !bf::exists(parent_path)) - { - bsys::error_code ec; - if (!bf::create_directories(parent_path, ec)) - { - RENDERER_LOG_ERROR( - "could not create directory %s: %s", - parent_path.c_str(), - ec.message().c_str()); - } - } - } - - void create_parent_directories(const char* file_path) - { - create_parent_directories(bf::path(file_path)); - } - void add_chromaticities(ImageAttributes& image_attributes) { // Scene-linear sRGB / Rec. 709 chromaticities. diff --git a/src/appleseed/renderer/modeling/frame/frame.h b/src/appleseed/renderer/modeling/frame/frame.h index 1ec62fa87f..001b43c8cd 100644 --- a/src/appleseed/renderer/modeling/frame/frame.h +++ b/src/appleseed/renderer/modeling/frame/frame.h @@ -92,6 +92,9 @@ class APPLESEED_DLLSYMBOL Frame // Access the AOVs. AOVContainer& aovs() const; + // Access the internal AOVs. + AOVContainer& internal_aovs() const; + // Access the post-processing stages. PostProcessingStageContainer& post_processing_stages() const; @@ -211,9 +214,6 @@ class APPLESEED_DLLSYMBOL Frame ~Frame() override; void extract_parameters(); - - // Access the internal AOVs. - const AOVContainer& internal_aovs() const; }; diff --git a/src/appleseed/renderer/modeling/input/texturesource.cpp b/src/appleseed/renderer/modeling/input/texturesource.cpp index ffa91a9a0c..d5879760a4 100644 --- a/src/appleseed/renderer/modeling/input/texturesource.cpp +++ b/src/appleseed/renderer/modeling/input/texturesource.cpp @@ -36,7 +36,6 @@ #include "renderer/modeling/texture/texture.h" // appleseed.foundation headers. -#include "foundation/image/color.h" #include "foundation/image/tile.h" #include "foundation/math/hash.h" #include "foundation/math/scalar.h" diff --git a/src/appleseed/renderer/modeling/project/configuration.cpp b/src/appleseed/renderer/modeling/project/configuration.cpp index e55c5d14f6..6a5b21bdd5 100644 --- a/src/appleseed/renderer/modeling/project/configuration.cpp +++ b/src/appleseed/renderer/modeling/project/configuration.cpp @@ -289,8 +289,6 @@ auto_release_ptr BaseConfigurationFactory::create_base_interactiv parameters.insert("spectrum_mode", "rgb"); parameters.insert("sampling_mode", "qmc"); - parameters.insert("passes", 1); - parameters.insert("frame_renderer", "progressive"); parameters.insert("sample_generator", "generic"); parameters.insert("sample_renderer", "generic"); diff --git a/src/appleseed/renderer/utility/bbox.h b/src/appleseed/renderer/utility/bbox.h index a08d0478d2..2c7a7afbd9 100644 --- a/src/appleseed/renderer/utility/bbox.h +++ b/src/appleseed/renderer/utility/bbox.h @@ -56,6 +56,19 @@ BBox compute_union(const Iterator begin, const Iterator end); template BBox interpolate(const Iterator begin, const Iterator end, const double time); +template +BBox compute_tile_image_space_bbox( + const Tile& tile, + const Int tile_x, + const Int tile_y); + +template +BBox compute_tile_space_bbox( + const Tile& tile, + const Int tile_origin_x, + const Int tile_origin_y, + const BBox& crop_window); + // // Implementation. @@ -116,6 +129,46 @@ BBox interpolate(const Iterator begin, const Iterator end, const double time) return foundation::lerp(first[prev_index], first[prev_index + 1], k); } +template +BBox compute_tile_image_space_bbox( + const Tile& tile, + const Int tile_x, + const Int tile_y) +{ + BBox tile_bbox; + const Int tile_origin_x = tile_x * static_cast(tile.get_width()); + const Int tile_origin_y = tile_y * static_cast(tile.get_height()); + tile_bbox.min.x = tile_origin_x; + tile_bbox.min.y = tile_origin_y; + tile_bbox.max.x = tile_origin_x + static_cast(tile.get_width()) - 1; + tile_bbox.max.y = tile_origin_y + static_cast(tile.get_height()) - 1; + + return tile_bbox; +} + +template +BBox compute_tile_space_bbox( + const Tile& tile, + const Int tile_origin_x, + const Int tile_origin_y, + const BBox& crop_window) +{ + BBox tile_bbox; + tile_bbox.min.x = tile_origin_x; + tile_bbox.min.y = tile_origin_y; + tile_bbox.max.x = tile_origin_x + static_cast(tile.get_width()) - 1; + tile_bbox.max.y = tile_origin_y + static_cast(tile.get_height()) - 1; + tile_bbox = BBox::intersect(tile_bbox, crop_window); + + // Transform the bounding box to local (tile) space. + tile_bbox.min.x -= tile_origin_x; + tile_bbox.min.y -= tile_origin_y; + tile_bbox.max.x -= tile_origin_x; + tile_bbox.max.y -= tile_origin_y; + + return tile_bbox; +} + } // namespace renderer #endif // !APPLESEED_RENDERER_UTILITY_BBOX_H diff --git a/src/appleseed/renderer/utility/filesystem.cpp b/src/appleseed/renderer/utility/filesystem.cpp new file mode 100644 index 0000000000..10533f1981 --- /dev/null +++ b/src/appleseed/renderer/utility/filesystem.cpp @@ -0,0 +1,63 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2018 Kevin Masson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +// Interface header. +#include "filesystem.h" + +// appleseed.renderer headers. +#include "renderer/global/globallogger.h" + +namespace bsys = boost::system; + +namespace renderer +{ + +void create_parent_directories(const bf::path& file_path) +{ + const bf::path parent_path = file_path.parent_path(); + + if (!parent_path.empty() && !bf::exists(parent_path)) + { + bsys::error_code ec; + if (!bf::create_directories(parent_path, ec)) + { + RENDERER_LOG_ERROR( + "could not create directory %s: %s", + parent_path.c_str(), + ec.message().c_str()); + } + } +} + +void create_parent_directories(const char* file_path) +{ + create_parent_directories(bf::path(file_path)); +} + +} // namespace renderer + diff --git a/src/appleseed/renderer/utility/filesystem.h b/src/appleseed/renderer/utility/filesystem.h new file mode 100644 index 0000000000..3ad4b01b0b --- /dev/null +++ b/src/appleseed/renderer/utility/filesystem.h @@ -0,0 +1,46 @@ + +// +// This source file is part of appleseed. +// Visit https://appleseedhq.net/ for additional information and resources. +// +// This software is released under the MIT license. +// +// Copyright (c) 2018 Kevin Masson, The appleseedhq Organization +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#ifndef APPLESEED_RENDERER_UTILITY_FILESYSTEM_H +#define APPLESEED_RENDERER_UTILITY_FILESYSTEM_H + +// Boost headers. +#include "boost/filesystem.hpp" + +namespace bf = boost::filesystem; + +namespace renderer +{ + +void create_parent_directories(const bf::path& file_path); + +void create_parent_directories(const char* file_path); + +} // namespace renderer + +#endif // !APPLESEED_RENDERER_UTILITY_FILESYSTEM_H