From 14da087326f91f5ec097ad1b554e368e084b237d Mon Sep 17 00:00:00 2001 From: Kevin Masson Date: Tue, 6 Mar 2018 13:36:01 +0100 Subject: [PATCH] Create an AOV for invalid samples (#1815) --- .../mainwindow/renderingsettingswindow.cpp | 5 + .../rendering/final/adaptivepixelrenderer.cpp | 52 ++++---- .../rendering/final/adaptivepixelrenderer.h | 4 +- .../rendering/final/uniformpixelrenderer.cpp | 27 ++-- .../rendering/final/uniformpixelrenderer.h | 7 +- .../kernel/rendering/pixelrendererbase.cpp | 115 ++++++++++++++++-- .../kernel/rendering/pixelrendererbase.h | 52 ++++++-- .../kernel/rendering/renderercomponents.cpp | 1 + 8 files changed, 203 insertions(+), 60 deletions(-) diff --git a/src/appleseed.studio/mainwindow/renderingsettingswindow.cpp b/src/appleseed.studio/mainwindow/renderingsettingswindow.cpp index 8674823a1e..762e146565 100644 --- a/src/appleseed.studio/mainwindow/renderingsettingswindow.cpp +++ b/src/appleseed.studio/mainwindow/renderingsettingswindow.cpp @@ -464,6 +464,7 @@ namespace create_direct_link("uniform_sampler.samples", "uniform_pixel_renderer.samples"); create_direct_link("uniform_sampler.force_antialiasing", "uniform_pixel_renderer.force_antialiasing"); create_direct_link("uniform_sampler.decorrelate_pixels", "uniform_pixel_renderer.decorrelate_pixels"); + create_direct_link("uniform_sampler.enable_diagnostics", "uniform_pixel_renderer.enable_diagnostics"); create_direct_link("adaptive_sampler.min_samples", "adaptive_pixel_renderer.min_samples"); create_direct_link("adaptive_sampler.max_samples", "adaptive_pixel_renderer.max_samples"); @@ -551,6 +552,10 @@ namespace m_uniform_sampler_decorrelate_pixels->setToolTip(m_params_metadata.get_path("uniform_pixel_renderer.decorrelate_pixels.help")); layout->addWidget(m_uniform_sampler_decorrelate_pixels); + QCheckBox* enable_diagnostics = create_checkbox("uniform_sampler.enable_diagnostics", "Enable Diagnostic AOVs"); + enable_diagnostics->setToolTip(m_params_metadata.get_path("uniform_pixel_renderer.enable_diagnostics.help")); + layout->addWidget(enable_diagnostics); + connect( m_image_plane_sampler_passes, SIGNAL(valueChanged(const int)), SLOT(slot_changed_image_plane_sampler_passes(const int))); diff --git a/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.cpp b/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.cpp index f0b1a86a22..1890a3c9f2 100644 --- a/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.cpp +++ b/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.cpp @@ -86,10 +86,11 @@ namespace ISampleRendererFactory* factory, const ParamArray& params, const size_t thread_index) - : m_params(params) + : PixelRendererBase(frame, thread_index, params) + , m_params(params) , m_sample_renderer(factory->create(thread_index)) { - if (m_params.m_diagnostics) + if (are_diagnostics_enabled()) { m_variation_aov_index = frame.create_extra_aov_image("variation"); m_samples_aov_index = frame.create_extra_aov_image("samples"); @@ -116,7 +117,7 @@ namespace m_params.m_min_samples, m_params.m_max_samples, m_params.m_max_variation, - m_params.m_diagnostics ? "on" : "off"); + are_diagnostics_enabled() ? "on" : "off"); } void release() override @@ -125,10 +126,12 @@ namespace } void on_tile_begin( - const Frame& frame, - Tile& tile, - TileStack& aov_tiles) override + const Frame& frame, + Tile& tile, + TileStack& aov_tiles) override { + PixelRendererBase::on_tile_begin(frame, tile, aov_tiles); + m_scratch_fb_half_width = truncate(ceil(frame.get_filter().get_xradius())); m_scratch_fb_half_height = truncate(ceil(frame.get_filter().get_yradius())); @@ -139,16 +142,21 @@ namespace frame.aov_images().size(), frame.get_filter())); - if (m_params.m_diagnostics) - m_diagnostics.reset(new Tile(tile.get_width(), tile.get_height(), 2, PixelFormatFloat)); + if (are_diagnostics_enabled()) + { + m_diagnostics.reset(new Tile( + tile.get_width(), tile.get_height(), 2, PixelFormatFloat)); + } } void on_tile_end( - const Frame& frame, - Tile& tile, - TileStack& aov_tiles) override + const Frame& frame, + Tile& tile, + TileStack& aov_tiles) override { - if (m_params.m_diagnostics) + PixelRendererBase::on_tile_end(frame, tile, aov_tiles); + + if (are_diagnostics_enabled()) { const size_t width = tile.get_width(); const size_t height = tile.get_height(); @@ -183,7 +191,7 @@ namespace { const size_t aov_count = frame.aov_images().size(); - on_pixel_begin(pi, aov_accumulators); + on_pixel_begin(pi, pt, tile_bbox, aov_accumulators); m_scratch_fb->clear(); @@ -285,7 +293,7 @@ namespace } // Store diagnostics values in the diagnostics tile. - if (m_params.m_diagnostics && tile_bbox.contains(pt)) + if (are_diagnostics_enabled() && tile_bbox.contains(pt)) { Color values; @@ -309,7 +317,7 @@ namespace m_diagnostics->set_pixel(pt.x, pt.y, values); } - on_pixel_end(pi, aov_accumulators); + on_pixel_end(pi, pt, tile_bbox, aov_accumulators); } StatisticsVector get_statistics() const override @@ -329,14 +337,12 @@ namespace const size_t m_min_samples; const size_t m_max_samples; const float m_max_variation; - const bool m_diagnostics; explicit Parameters(const ParamArray& params) : m_sampling_mode(get_sampling_context_mode(params)) , m_min_samples(params.get_required("min_samples", 16)) , m_max_samples(params.get_required("max_samples", 256)) , m_max_variation(pow(10.0f, -params.get_optional("quality", 2.0f))) - , m_diagnostics(params.get_optional("enable_diagnostics", false)) { } }; @@ -391,7 +397,7 @@ IPixelRenderer* AdaptivePixelRendererFactory::create( Dictionary AdaptivePixelRendererFactory::get_params_metadata() { - Dictionary metadata; + Dictionary metadata = PixelRendererBaseFactory::get_params_metadata(); metadata.dictionaries().insert( "min_samples", @@ -417,16 +423,6 @@ Dictionary AdaptivePixelRendererFactory::get_params_metadata() .insert("label", "Quality") .insert("help", "Quality factor")); - metadata.dictionaries().insert( - "enable_diagnostics", - Dictionary() - .insert("type", "bool") - .insert("default", "false") - .insert("label", "Enable Diagnostics") - .insert( - "help", - "Enable adaptive sampling diagnostics")); - return metadata; } diff --git a/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.h b/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.h index 98d5e802f9..1399711887 100644 --- a/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.h +++ b/src/appleseed/renderer/kernel/rendering/final/adaptivepixelrenderer.h @@ -31,7 +31,7 @@ #define APPLESEED_RENDERER_KERNEL_RENDERING_FINAL_ADAPTIVEPIXELRENDERER_H // appleseed.renderer headers. -#include "renderer/kernel/rendering/ipixelrenderer.h" +#include "renderer/kernel/rendering/pixelrendererbase.h" #include "renderer/utility/paramarray.h" // appleseed.foundation headers. @@ -53,7 +53,7 @@ namespace renderer // class AdaptivePixelRendererFactory - : public IPixelRendererFactory + : public PixelRendererBaseFactory { public: // Constructor. diff --git a/src/appleseed/renderer/kernel/rendering/final/uniformpixelrenderer.cpp b/src/appleseed/renderer/kernel/rendering/final/uniformpixelrenderer.cpp index 4da20dfb96..9a17619d06 100644 --- a/src/appleseed/renderer/kernel/rendering/final/uniformpixelrenderer.cpp +++ b/src/appleseed/renderer/kernel/rendering/final/uniformpixelrenderer.cpp @@ -59,12 +59,7 @@ // Standard headers. #include -// Forward declarations. -namespace foundation { class Tile; } -namespace renderer { class TileStack; } - using namespace foundation; -using namespace std; namespace renderer { @@ -80,10 +75,12 @@ namespace { public: UniformPixelRenderer( + const Frame& frame, ISampleRendererFactory* factory, const ParamArray& params, const size_t thread_index) - : m_params(params) + : PixelRendererBase(frame, thread_index, params) + , m_params(params) , m_sample_renderer(factory->create(thread_index)) , m_sample_count(m_params.m_samples) , m_sqrt_sample_count(round(sqrt(static_cast(m_params.m_samples)))) @@ -112,11 +109,13 @@ namespace " sampling mode %s\n" " samples " FMT_SIZE_T "\n" " force antialiasing %s\n" - " decorrelate pixels %s", + " decorrelate pixels %s\n" + " diagnostics %s", m_params.m_sampling_mode == SamplingContext::Mode::QMCMode ? "qmc" : "rng", m_params.m_samples, m_params.m_force_aa ? "on" : "off", - m_params.m_decorrelate ? "on" : "off"); + m_params.m_decorrelate ? "on" : "off", + are_diagnostics_enabled() ? "on" : "off"); } void release() override @@ -137,7 +136,7 @@ namespace { const size_t aov_count = frame.aov_images().size(); - on_pixel_begin(pi, aov_accumulators); + on_pixel_begin(pi, pt, tile_bbox, aov_accumulators); if (m_params.m_decorrelate) { @@ -251,7 +250,7 @@ namespace } } - on_pixel_end(pi, aov_accumulators); + on_pixel_end(pi, pt, tile_bbox, aov_accumulators); } StatisticsVector get_statistics() const override @@ -303,9 +302,11 @@ namespace // UniformPixelRendererFactory::UniformPixelRendererFactory( + const Frame& frame, ISampleRendererFactory* factory, const ParamArray& params) - : m_factory(factory) + : m_frame(frame) + , m_factory(factory) , m_params(params) { } @@ -318,12 +319,12 @@ void UniformPixelRendererFactory::release() IPixelRenderer* UniformPixelRendererFactory::create( const size_t thread_index) { - return new UniformPixelRenderer(m_factory, m_params, thread_index); + return new UniformPixelRenderer(m_frame, m_factory, m_params, thread_index); } Dictionary UniformPixelRendererFactory::get_params_metadata() { - Dictionary metadata; + Dictionary metadata = PixelRendererBaseFactory::get_params_metadata(); metadata.dictionaries().insert( "samples", diff --git a/src/appleseed/renderer/kernel/rendering/final/uniformpixelrenderer.h b/src/appleseed/renderer/kernel/rendering/final/uniformpixelrenderer.h index 80510b780d..981895acf5 100644 --- a/src/appleseed/renderer/kernel/rendering/final/uniformpixelrenderer.h +++ b/src/appleseed/renderer/kernel/rendering/final/uniformpixelrenderer.h @@ -31,7 +31,7 @@ #define APPLESEED_RENDERER_KERNEL_RENDERING_FINAL_UNIFORMPIXELRENDERER_H // appleseed.renderer headers. -#include "renderer/kernel/rendering/ipixelrenderer.h" +#include "renderer/kernel/rendering/pixelrendererbase.h" #include "renderer/utility/paramarray.h" // appleseed.foundation headers. @@ -42,6 +42,7 @@ // Forward declarations. namespace foundation { class Dictionary; } +namespace renderer { class Frame; } namespace renderer { class ISampleRendererFactory; } namespace renderer @@ -52,11 +53,12 @@ namespace renderer // class UniformPixelRendererFactory - : public IPixelRendererFactory + : public PixelRendererBaseFactory { public: // Constructor. UniformPixelRendererFactory( + const Frame& frame, ISampleRendererFactory* factory, const ParamArray& params); @@ -71,6 +73,7 @@ class UniformPixelRendererFactory static foundation::Dictionary get_params_metadata(); private: + const Frame& m_frame; ISampleRendererFactory* m_factory; ParamArray m_params; }; diff --git a/src/appleseed/renderer/kernel/rendering/pixelrendererbase.cpp b/src/appleseed/renderer/kernel/rendering/pixelrendererbase.cpp index be46d3d08f..333b3b74e2 100644 --- a/src/appleseed/renderer/kernel/rendering/pixelrendererbase.cpp +++ b/src/appleseed/renderer/kernel/rendering/pixelrendererbase.cpp @@ -33,8 +33,13 @@ // appleseed.renderer headers. #include "renderer/global/globallogger.h" #include "renderer/kernel/aov/aovaccumulator.h" +#include "renderer/kernel/aov/tilestack.h" +#include "renderer/modeling/frame/frame.h" // appleseed.foundation headers. +#include "foundation/image/color.h" +#include "foundation/image/colorspace.h" +#include "foundation/image/tile.h" #include "foundation/platform/types.h" using namespace foundation; @@ -46,27 +51,83 @@ namespace renderer // PixelRendererBase class implementation. // -PixelRendererBase::PixelRendererBase() - : m_invalid_pixel_count(0) +const uint8 InvalidSampleHint = 1; +const uint8 CorrectSampleHint = 2; + +PixelRendererBase::PixelRendererBase( + const Frame& frame, + const size_t thread_index, + const ParamArray& params) + : m_params(params) + , m_invalid_pixel_count(0) + , m_invalid_sample_aov_index(~0) { + if (m_params.m_diagnostics) + { + m_invalid_sample_aov_index = frame.create_extra_aov_image("invalid_samples"); + + if ((thread_index == 0) && (m_invalid_sample_aov_index == size_t(~0))) + { + RENDERER_LOG_WARNING( + "could not create invalid samples aov, maximum number of aovs (" FMT_SIZE_T ") reached.", + MaxAOVCount); + } + } +} + +bool PixelRendererBase::are_diagnostics_enabled() const +{ + return m_params.m_diagnostics; } void PixelRendererBase::on_tile_begin( - const Frame& frame, - Tile& tile, - TileStack& aov_tiles) + const Frame& frame, + Tile& tile, + TileStack& aov_tiles) { + if (m_invalid_sample_aov_index != size_t(~0)) + { + m_invalid_sample_diagnostic.reset( + new Tile(tile.get_width(), tile.get_height(), 1, PixelFormatUInt8)); + } } void PixelRendererBase::on_tile_end( - const Frame& frame, - Tile& tile, - TileStack& aov_tiles) + const Frame& frame, + Tile& tile, + TileStack& aov_tiles) { + if (m_invalid_sample_aov_index != size_t(~0)) + { + 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_diagnostic->get_pixel(x, y, sample_state); + + Color4f color(1.0f, 0.0f, 1.0f, 1.0f); + + if (sample_state[0] == CorrectSampleHint) + { + tile.get_pixel(x, y, color); + // 20% of luminance + color.rgb().set(luminance(color.rgb()) * 0.2f); + } + + aov_tiles.set_pixel(x, y, m_invalid_sample_aov_index, color); + } + } + } } void PixelRendererBase::on_pixel_begin( const Vector2i& pi, + const Vector2i& pt, + const AABB2i& tile_bbox, AOVAccumulatorContainer& aov_accumulators) { m_invalid_sample_count = 0; @@ -75,6 +136,8 @@ void PixelRendererBase::on_pixel_begin( void PixelRendererBase::on_pixel_end( const Vector2i& pi, + const Vector2i& pt, + const AABB2i& tile_bbox, AOVAccumulatorContainer& aov_accumulators) { aov_accumulators.on_pixel_end(pi); @@ -99,6 +162,20 @@ void PixelRendererBase::on_pixel_end( { RENDERER_LOG_WARNING("more invalid samples found, omitting warning messages for brevity."); } + + } + + // Invalid samples diagnostic + if (m_params.m_diagnostics && tile_bbox.contains(pt)) + { + Color sample_state; + + if (m_invalid_sample_count > 0) + sample_state[0] = InvalidSampleHint; + else + sample_state[0] = CorrectSampleHint; + + m_invalid_sample_diagnostic->set_pixel(pt.x, pt.y, sample_state); } } @@ -107,4 +184,26 @@ void PixelRendererBase::signal_invalid_sample() ++m_invalid_sample_count; } + +// +// PixelRendererBaseFactory class implementation. +// + +Dictionary PixelRendererBaseFactory::get_params_metadata() +{ + Dictionary metadata; + + metadata.dictionaries().insert( + "enable_diagnostics", + Dictionary() + .insert("type", "bool") + .insert("default", "false") + .insert("label", "Enable Diagnostics") + .insert( + "help", + "Enable pixel renderer diagnostics")); + + return metadata; +} + } // namespace renderer diff --git a/src/appleseed/renderer/kernel/rendering/pixelrendererbase.h b/src/appleseed/renderer/kernel/rendering/pixelrendererbase.h index 89a65d02ef..6e2f212c12 100644 --- a/src/appleseed/renderer/kernel/rendering/pixelrendererbase.h +++ b/src/appleseed/renderer/kernel/rendering/pixelrendererbase.h @@ -32,15 +32,19 @@ // appleseed.renderer headers. #include "renderer/kernel/rendering/ipixelrenderer.h" +#include "renderer/utility/paramarray.h" // appleseed.foundation headers. +#include "foundation/math/aabb.h" #include "foundation/math/vector.h" #include "foundation/platform/compiler.h" // Standard headers. #include +#include // Forward declarations. +namespace foundation { class Dictionary; } namespace foundation { class Tile; } namespace renderer { class AOVAccumulatorContainer; } namespace renderer { class Frame; } @@ -58,7 +62,12 @@ class PixelRendererBase { public: // Constructor. - PixelRendererBase(); + PixelRendererBase( + const Frame& frame, + const size_t thread_index, + const ParamArray& params); + + bool are_diagnostics_enabled() const; // This method is called before a tile gets rendered. void on_tile_begin( @@ -74,18 +83,47 @@ class PixelRendererBase protected: void on_pixel_begin( - const foundation::Vector2i& pi, - AOVAccumulatorContainer& aov_accumulators); + const foundation::Vector2i& pi, + const foundation::Vector2i& pt, + const foundation::AABB2i& tile_bbox, + AOVAccumulatorContainer& aov_accumulators); void on_pixel_end( - const foundation::Vector2i& pi, - AOVAccumulatorContainer& aov_accumulators); + const foundation::Vector2i& pi, + const foundation::Vector2i& pt, + const foundation::AABB2i& tile_bbox, + AOVAccumulatorContainer& aov_accumulators); void signal_invalid_sample(); private: - size_t m_invalid_sample_count; - size_t m_invalid_pixel_count; + struct Parameters + { + const bool m_diagnostics; + + explicit Parameters(const ParamArray& params) + : m_diagnostics(params.get_optional("enable_diagnostics", false)) + { + } + }; + + size_t m_invalid_sample_count; + size_t m_invalid_pixel_count; + size_t m_invalid_sample_aov_index; + std::unique_ptr m_invalid_sample_diagnostic; + const Parameters m_params; +}; + + +// +// Pixel renderer base factory. +// + +class PixelRendererBaseFactory + : public IPixelRendererFactory +{ + public: + static foundation::Dictionary get_params_metadata(); }; } // namespace renderer diff --git a/src/appleseed/renderer/kernel/rendering/renderercomponents.cpp b/src/appleseed/renderer/kernel/rendering/renderercomponents.cpp index 71052eca24..36eb5e92a8 100644 --- a/src/appleseed/renderer/kernel/rendering/renderercomponents.cpp +++ b/src/appleseed/renderer/kernel/rendering/renderercomponents.cpp @@ -335,6 +335,7 @@ bool RendererComponents::create_pixel_renderer_factory() copy_param(params, m_params, "passes"); m_pixel_renderer_factory.reset( new UniformPixelRendererFactory( + m_frame, m_sample_renderer_factory.get(), params));