Skip to content

Commit 5426da9

Browse files
committed
Add variance tracking framebuffer
Split from PR appleseedhq#2656 with variance estimation fix to keep reviews small. Contains the added VarianceTrackingShadingResultFramebuffer and matching factory, which keep track of the sum of squared samples to allow per pixel variance estimation. Also adapted the ishadingresultframebufferfactory interface and derived classes to allow dynamic calls to the get_total_channel_count() method for checkpoints.
1 parent 75f8a2c commit 5426da9

13 files changed

+602
-15
lines changed

src/appleseed/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,10 @@ set (renderer_kernel_rendering_sources
13791379
renderer/kernel/rendering/tilecallbackcollection.h
13801380
renderer/kernel/rendering/timedrenderercontroller.cpp
13811381
renderer/kernel/rendering/timedrenderercontroller.h
1382+
renderer/kernel/rendering/variancetrackingshadingresultframebuffer.cpp
1383+
renderer/kernel/rendering/variancetrackingshadingresultframebuffer.h
1384+
renderer/kernel/rendering/variancetrackingshadingresultframebufferfactory.cpp
1385+
renderer/kernel/rendering/variancetrackingshadingresultframebufferfactory.h
13821386
)
13831387
list (APPEND appleseed_sources
13841388
${renderer_kernel_rendering_sources}

src/appleseed/renderer/kernel/rendering/ephemeralshadingresultframebufferfactory.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,11 @@ void EphemeralShadingResultFrameBufferFactory::destroy(
7575
delete framebuffer;
7676
}
7777

78+
size_t EphemeralShadingResultFrameBufferFactory::get_total_channel_count(
79+
const size_t aov_count) const
80+
{
81+
return ShadingResultFrameBuffer::get_total_channel_count(
82+
aov_count);
83+
}
84+
7885
} // namespace renderer

src/appleseed/renderer/kernel/rendering/ephemeralshadingresultframebufferfactory.h

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ class EphemeralShadingResultFrameBufferFactory
6161

6262
void destroy(
6363
ShadingResultFrameBuffer* framebuffer) override;
64+
65+
size_t get_total_channel_count(
66+
const size_t aov_count) const override;
6467
};
6568

6669
} // namespace renderer

src/appleseed/renderer/kernel/rendering/ishadingresultframebufferfactory.h

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class IShadingResultFrameBufferFactory
5555

5656
virtual void destroy(
5757
ShadingResultFrameBuffer* framebuffer) = 0;
58+
59+
virtual size_t get_total_channel_count(
60+
const size_t aov_count) const = 0;
5861
};
5962

6063
} // namespace renderer

src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,11 @@ void PermanentShadingResultFrameBufferFactory::destroy(
9696
{
9797
}
9898

99+
size_t PermanentShadingResultFrameBufferFactory::get_total_channel_count(
100+
const size_t aov_count) const
101+
{
102+
return ShadingResultFrameBuffer::get_total_channel_count(
103+
aov_count);
104+
}
105+
99106
} // namespace renderer

src/appleseed/renderer/kernel/rendering/permanentshadingresultframebufferfactory.h

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ class PermanentShadingResultFrameBufferFactory
7070
void destroy(
7171
ShadingResultFrameBuffer* framebuffer) override;
7272

73+
size_t get_total_channel_count(
74+
const size_t aov_count) const override;
75+
7376
private:
7477
std::vector<ShadingResultFrameBuffer*> m_framebuffers;
7578
};

src/appleseed/renderer/kernel/rendering/shadingresultframebuffer.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
// Standard headers.
4343
#include <cassert>
44+
#include <typeinfo>
4445

4546
using namespace foundation;
4647
using namespace std;
@@ -107,6 +108,7 @@ void ShadingResultFrameBuffer::merge(
107108
const size_t source_y,
108109
const float scaling)
109110
{
111+
assert(typeid(this) == typeid(source));
110112
assert(m_channel_count == source.m_channel_count);
111113

112114
const float* APPLESEED_RESTRICT source_ptr = source.pixel(source_x, source_y);

src/appleseed/renderer/kernel/rendering/shadingresultframebuffer.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ class ShadingResultFrameBuffer
6161
const size_t aov_count,
6262
const foundation::AABB2u& crop_window);
6363

64+
virtual ~ShadingResultFrameBuffer() {};
65+
6466
static size_t get_total_channel_count(const size_t aov_count);
6567

66-
void add(
68+
virtual void add(
6769
const foundation::Vector2u& pi,
6870
const ShadingResult& sample);
6971

@@ -75,13 +77,15 @@ class ShadingResultFrameBuffer
7577
const size_t source_y,
7678
const float scaling);
7779

78-
void develop_to_tile(
80+
virtual void develop_to_tile(
7981
foundation::Tile& tile,
8082
TileStack& aov_tiles) const;
8183

84+
protected:
85+
std::vector<float> m_scratch;
86+
8287
private:
8388
const size_t m_aov_count;
84-
std::vector<float> m_scratch;
8589
};
8690

8791
inline size_t ShadingResultFrameBuffer::get_total_channel_count(const size_t aov_count)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
2+
//
3+
// This source file is part of appleseed.
4+
// Visit https://appleseedhq.net/ for additional information and resources.
5+
//
6+
// This software is released under the MIT license.
7+
//
8+
// Copyright (c) 2019 Stephen Agyemang, The appleseedhq Organization
9+
//
10+
// Permission is hereby granted, free of charge, to any person obtaining a copy
11+
// of this software and associated documentation files (the "Software"), to deal
12+
// in the Software without restriction, including without limitation the rights
13+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14+
// copies of the Software, and to permit persons to whom the Software is
15+
// furnished to do so, subject to the following conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be included in
18+
// all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26+
// THE SOFTWARE.
27+
//
28+
29+
// Interface header.
30+
#include "variancetrackingshadingresultframebuffer.h"
31+
32+
// appleseed.renderer headers.
33+
#include "renderer/kernel/aov/tilestack.h"
34+
#include "renderer/kernel/shading/shadingresult.h"
35+
36+
// appleseed.foundation headers.
37+
#include "foundation/image/color.h"
38+
#include "foundation/image/colorspace.h"
39+
#include "foundation/image/tile.h"
40+
#include "foundation/platform/compiler.h"
41+
42+
// Standard headers.
43+
#include <cassert>
44+
45+
using namespace foundation;
46+
using namespace std;
47+
48+
namespace renderer
49+
{
50+
51+
VarianceTrackingShadingResultFrameBuffer::VarianceTrackingShadingResultFrameBuffer(
52+
const size_t width,
53+
const size_t height,
54+
const size_t aov_count)
55+
: ShadingResultFrameBuffer(
56+
width,
57+
height,
58+
aov_count + 1)
59+
, m_aov_count(aov_count)
60+
{
61+
}
62+
63+
VarianceTrackingShadingResultFrameBuffer::VarianceTrackingShadingResultFrameBuffer(
64+
const size_t width,
65+
const size_t height,
66+
const size_t aov_count,
67+
const AABB2u& crop_window)
68+
: ShadingResultFrameBuffer(
69+
width,
70+
height,
71+
aov_count + 1,
72+
crop_window)
73+
, m_aov_count(aov_count)
74+
{
75+
}
76+
77+
void VarianceTrackingShadingResultFrameBuffer::add(
78+
const Vector2u& pi,
79+
const ShadingResult& sample)
80+
{
81+
float* ptr = &m_scratch[0];
82+
83+
const float main_0 = sample.m_main[0];
84+
const float main_1 = sample.m_main[1];
85+
const float main_2 = sample.m_main[2];
86+
const float main_3 = sample.m_main[3];
87+
88+
*ptr++ = main_0;
89+
*ptr++ = main_1;
90+
*ptr++ = main_2;
91+
*ptr++ = main_3;
92+
93+
for (size_t i = 0, e = m_aov_count; i < e; ++i)
94+
{
95+
const Color4f& aov = sample.m_aovs[i];
96+
*ptr++ = aov[0];
97+
*ptr++ = aov[1];
98+
*ptr++ = aov[2];
99+
*ptr++ = aov[3];
100+
}
101+
102+
// Put squared samples in the last buffer channels.
103+
*ptr++ = main_0 * main_0;
104+
*ptr++ = main_1 * main_1;
105+
*ptr++ = main_2 * main_2;
106+
*ptr++ = main_3 * main_3;
107+
108+
AccumulatorTile::add(pi, &m_scratch[0]);
109+
}
110+
111+
void VarianceTrackingShadingResultFrameBuffer::develop_to_tile(
112+
Tile& tile,
113+
TileStack& aov_tiles) const
114+
{
115+
const float* ptr = pixel(0);
116+
117+
for (size_t y = 0, h = m_height; y < h; ++y)
118+
{
119+
for (size_t x = 0, w = m_width; x < w; ++x)
120+
{
121+
const float weight = *ptr++;
122+
const float rcp_weight = weight == 0.0f ? 0.0f : 1.0f / weight;
123+
124+
const Color4f color(ptr[0], ptr[1], ptr[2], ptr[3]);
125+
tile.set_pixel(x, y, color * rcp_weight);
126+
ptr += 4;
127+
128+
for (size_t i = 0, e = m_aov_count; i < e; ++i)
129+
{
130+
const Color4f aov(ptr[0], ptr[1], ptr[2], ptr[3]);
131+
aov_tiles.set_pixel(x, y, i, aov * rcp_weight);
132+
ptr += 4;
133+
}
134+
135+
ptr += 4; // Skip sum of squared samples
136+
}
137+
}
138+
}
139+
140+
float VarianceTrackingShadingResultFrameBuffer::variance() const
141+
{
142+
float tile_variance = 0.0f;
143+
const float* ptr = pixel(0);
144+
145+
for (size_t y = 0, h = m_height; y < h; ++y)
146+
for (size_t x = 0, w = m_width; x < w; ++x)
147+
{
148+
const float weight = *ptr;
149+
150+
const Color3f sample_sum(
151+
ptr[1],
152+
ptr[2],
153+
ptr[3]);
154+
155+
ptr += m_channel_count - 4; // skip to beginning of summed squares
156+
157+
const Color3f square_sum(
158+
ptr[0],
159+
ptr[1],
160+
ptr[2]);
161+
162+
// Variance estimator: (1 / n) * Sum_i[(X_i - µ)²] = Sum_i[X_i²] - Sum_i[X_i]² / n .
163+
const Color3f pixel_variance(square_sum - (sample_sum * sample_sum) / weight);
164+
165+
// Clamp values to mitigate the effect of fireflies.
166+
tile_variance += std::min(luminance(pixel_variance), 10000.0f);
167+
168+
ptr += 4;
169+
}
170+
171+
return tile_variance;
172+
}
173+
174+
float VarianceTrackingShadingResultFrameBuffer::variance_to_tile(
175+
Tile& tile) const
176+
{
177+
float tile_variance = 0.0f;
178+
const float* ptr = pixel(0);
179+
180+
for (size_t y = 0, h = m_height; y < h; ++y)
181+
for (size_t x = 0, w = m_width; x < w; ++x)
182+
{
183+
const float weight = *ptr;
184+
185+
const Color3f sample_sum(
186+
ptr[1],
187+
ptr[2],
188+
ptr[3]);
189+
190+
ptr += m_channel_count - 4; // skip to beginning of summed squares
191+
192+
const Color3f square_sum(
193+
ptr[0],
194+
ptr[1],
195+
ptr[2]);
196+
197+
// Variance estimator: (1 / n) * Sum_i[(X_i - µ)²] = Sum_i[X_i²] - Sum_i[X_i]² / n .
198+
const Color3f pixel_variance(square_sum - (sample_sum * sample_sum) / weight);
199+
200+
// Clamp values to mitigate the effect of fireflies.
201+
const float variance_luminance = std::min(luminance(pixel_variance), 10000.0f);
202+
tile_variance += variance_luminance;
203+
tile.set_pixel(x, y, Color3f(variance_luminance));
204+
205+
ptr += 4;
206+
}
207+
208+
return tile_variance;
209+
}
210+
211+
} // namespace renderer

0 commit comments

Comments
 (0)