Skip to content

Commit b10570a

Browse files
committed
Merge branch 'temporal-accumulation'
2 parents d73d4d1 + ac5f5fd commit b10570a

File tree

6 files changed

+169
-21
lines changed

6 files changed

+169
-21
lines changed

src/common/camera.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ struct Camera
1414
glm::vec3 horizontal;
1515
glm::vec3 vertical;
1616
float lensRadius;
17+
18+
bool operator==(const Camera& rhs) const noexcept = default;
1719
};
1820

1921
// `aspectRatio` is defined as (width / height).

src/common/extent.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ struct Extent2
2424
y(static_cast<T>(other.y))
2525
{
2626
}
27+
28+
constexpr bool operator==(const Extent2& rhs) const noexcept
29+
{
30+
return x == rhs.x && y == rhs.y;
31+
}
2732
};
2833

2934
using Extent2i = Extent2<std::int32_t>;

src/pt/main.cpp

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <cassert>
1111
#include <chrono>
12+
#include <cstdint>
1213
#include <cstdio>
1314

1415
#include <GLFW/glfw3.h>
@@ -42,14 +43,14 @@ int main(int argc, char** argv)
4243
nlrs::Renderer renderer =
4344
[&cameraController, &gpuContext, &window, argv]() -> nlrs::Renderer {
4445
const nlrs::RendererDescriptor rendererDesc{
45-
.renderParams = [&window, &cameraController]() -> nlrs::RenderParameters {
46+
[&window, &cameraController]() -> nlrs::RenderParameters {
4647
const nlrs::Extent2i framebufferSize = window.resolution();
4748
return nlrs::RenderParameters{
48-
.framebufferSize = nlrs::Extent2u(framebufferSize),
49-
.camera = cameraController.getCamera(),
50-
};
49+
nlrs::Extent2u(framebufferSize),
50+
cameraController.getCamera(),
51+
nlrs::SamplingParams()};
5152
}(),
52-
.maxFramebufferSize = window.largestMonitorResolution(),
53+
window.largestMonitorResolution(),
5354
};
5455

5556
const nlrs::GltfModel model(argv[1]);
@@ -73,6 +74,8 @@ int main(int argc, char** argv)
7374
{
7475
nlrs::Extent2i curFramebufferSize = window.resolution();
7576
float vfovDegrees = 70.0f;
77+
int numSamplesPerPixel = 128;
78+
int numBounces = 4;
7679
auto lastTime = std::chrono::steady_clock::now();
7780
while (!glfwWindowShouldClose(window.ptr()))
7881
{
@@ -109,10 +112,11 @@ int main(int argc, char** argv)
109112
{
110113
ImGui::Begin("pt");
111114

112-
ImGui::Text("Performance");
115+
ImGui::Text("Renrerer stats");
113116
{
114117
const float drawAverageMs = renderer.averageDrawDurationMs();
115118
const float renderAverageMs = renderer.averageRenderpassDurationMs();
119+
const float progressPercentage = renderer.renderProgressPercentage();
116120
ImGui::Text(
117121
"render pass draw: %.2f ms (%.1f FPS)",
118122
drawAverageMs,
@@ -121,10 +125,28 @@ int main(int argc, char** argv)
121125
"render pass: %.2f ms (%.1f FPS)",
122126
renderAverageMs,
123127
1000.0f / renderAverageMs);
128+
ImGui::Text("render progress: %.2f %%", progressPercentage);
124129
}
125130
ImGui::Separator();
126131

127132
ImGui::Text("Parameters");
133+
134+
ImGui::Text("num samples:");
135+
ImGui::SameLine();
136+
ImGui::RadioButton("64", &numSamplesPerPixel, 64);
137+
ImGui::SameLine();
138+
ImGui::RadioButton("128", &numSamplesPerPixel, 128);
139+
ImGui::SameLine();
140+
ImGui::RadioButton("256", &numSamplesPerPixel, 256);
141+
142+
ImGui::Text("num bounces:");
143+
ImGui::SameLine();
144+
ImGui::RadioButton("4", &numBounces, 4);
145+
ImGui::SameLine();
146+
ImGui::RadioButton("8", &numBounces, 8);
147+
ImGui::SameLine();
148+
ImGui::RadioButton("16", &numBounces, 16);
149+
128150
ImGui::SliderFloat(
129151
"camera speed",
130152
&cameraController.speed(),
@@ -170,6 +192,10 @@ int main(int argc, char** argv)
170192
const nlrs::RenderParameters renderParams{
171193
nlrs::Extent2u(window.resolution()),
172194
cameraController.getCamera(),
195+
nlrs::SamplingParams{
196+
static_cast<std::uint32_t>(numSamplesPerPixel),
197+
static_cast<std::uint32_t>(numBounces),
198+
},
173199
};
174200
renderer.setRenderParameters(renderParams);
175201
renderer.render(gpuContext, gui);

src/pt/raytracer.wgsl

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ fn vsMain(in: VertexInput) -> VertexOutput {
3737
@group(2) @binding(5) var<storage, read_write> textureDescriptors: array<TextureDescriptor>;
3838
@group(2) @binding(6) var<storage, read_write> textures: array<u32>;
3939

40+
// image bind group
41+
@group(3) @binding(0) var<storage, read_write> imageBuffer: array<vec3f>;
42+
4043
@fragment
4144
fn fsMain(in: VertexOutput) -> @location(0) vec4f {
4245
let u = in.texCoord.x;
@@ -47,13 +50,24 @@ fn fsMain(in: VertexOutput) -> @location(0) vec4f {
4750

4851
let j = u32(u * f32(dimensions.x));
4952
let i = u32(v * f32(dimensions.y));
53+
let idx = i * dimensions.x + j;
54+
55+
var accumulatedSampleCount = renderParams.samplingState.accumulatedSampleCount;
5056

51-
var rngState = initRng(vec2(j, i), dimensions, frameCount);
57+
if accumulatedSampleCount == 0u {
58+
imageBuffer[idx] = vec3(0f);
59+
}
5260

53-
let primaryRay = generateCameraRay(renderParams.camera, &rngState, u, v);
54-
let rgb = rayColor(primaryRay, &rngState);
61+
if accumulatedSampleCount < renderParams.samplingState.numSamplesPerPixel {
62+
var rngState = initRng(vec2(j, i), dimensions, frameCount);
63+
let uoffset = rngNextFloat(&rngState) / f32(dimensions.x);
64+
let voffset = rngNextFloat(&rngState) / f32(dimensions.y);
65+
let primaryRay = generateCameraRay(renderParams.camera, &rngState, u + uoffset, v + voffset);
66+
imageBuffer[idx] += rayColor(primaryRay, &rngState);
67+
accumulatedSampleCount += 1u;
68+
}
5569

56-
return vec4f(rgb, 1f);
70+
return vec4f(imageBuffer[idx] / f32(accumulatedSampleCount), 1f);
5771
}
5872

5973
const EPSILON = 0.00001f;
@@ -71,6 +85,7 @@ const UNIFORM_HEMISPHERE_MULTIPLIER = 2f * PI;
7185
struct RenderParams {
7286
frameData: FrameData,
7387
camera: Camera,
88+
samplingState: SamplingState,
7489
}
7590

7691
struct FrameData {
@@ -86,6 +101,12 @@ struct Camera {
86101
lensRadius: f32,
87102
}
88103

104+
struct SamplingState {
105+
numSamplesPerPixel: u32,
106+
numBounces: u32,
107+
accumulatedSampleCount: u32,
108+
}
109+
89110
struct Aabb {
90111
min: vec3f,
91112
max: vec3f,

src/pt/renderer.cpp

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <glm/glm.hpp>
1111
#include <glm/gtc/matrix_transform.hpp>
1212

13+
#include <algorithm>
1314
#include <array>
1415
#include <cassert>
1516
#include <cstdint>
@@ -74,17 +75,38 @@ struct CameraLayout
7475
}
7576
};
7677

78+
struct SamplingStateLayout
79+
{
80+
std::uint32_t numSamplesPerPixel;
81+
std::uint32_t numBounces;
82+
std::uint32_t accumulatedSampleCount;
83+
std::uint32_t padding;
84+
85+
SamplingStateLayout(
86+
const SamplingParams& samplingParams,
87+
const std::uint32_t accumulatedSampleCount)
88+
: numSamplesPerPixel(samplingParams.numSamplesPerPixel),
89+
numBounces(samplingParams.numBounces),
90+
accumulatedSampleCount(accumulatedSampleCount),
91+
padding(0)
92+
{
93+
}
94+
};
95+
7796
struct RenderParamsLayout
7897
{
79-
FrameDataLayout frameData;
80-
CameraLayout camera;
98+
FrameDataLayout frameData;
99+
CameraLayout camera;
100+
SamplingStateLayout samplingState;
81101

82102
RenderParamsLayout(
83103
const Extent2u& dimensions,
84104
const std::uint32_t frameCount,
85-
const RenderParameters& renderParams)
105+
const RenderParameters& renderParams,
106+
const std::uint32_t accumulatedSampleCount)
86107
: frameData(dimensions, frameCount),
87-
camera(renderParams.camera)
108+
camera(renderParams.camera),
109+
samplingState(renderParams.samplingParams, accumulatedSampleCount)
88110
{
89111
}
90112
};
@@ -166,6 +188,12 @@ Renderer::Renderer(
166188
textureDescriptorBuffer(),
167189
textureBuffer(),
168190
sceneBindGroup(nullptr),
191+
imageBuffer(
192+
gpuContext.device,
193+
"image buffer",
194+
WGPUBufferUsage_Storage,
195+
sizeof(float[4]) * rendererDesc.maxFramebufferSize.x * rendererDesc.maxFramebufferSize.y),
196+
imageBindGroup(nullptr),
169197
querySet(nullptr),
170198
queryBuffer(
171199
gpuContext.device,
@@ -180,6 +208,7 @@ Renderer::Renderer(
180208
renderPipeline(nullptr),
181209
currentRenderParams(rendererDesc.renderParams),
182210
frameCount(0),
211+
accumulatedSampleCount(0),
183212
timestampBufferMapContext{&timestampBuffer, &drawDurationsNs, &renderPassDurationsNs}
184213
{
185214
{
@@ -420,12 +449,28 @@ Renderer::Renderer(
420449
const WGPUBindGroupLayout sceneBindGroupLayout =
421450
wgpuDeviceCreateBindGroupLayout(gpuContext.device, &sceneBindGroupLayoutDesc);
422451

452+
// image bind group layout
453+
454+
const WGPUBindGroupLayoutEntry imageBindGroupLayoutEntry =
455+
imageBuffer.bindGroupLayoutEntry(0, WGPUShaderStage_Fragment);
456+
457+
const WGPUBindGroupLayoutDescriptor imageBindGroupLayoutDesc{
458+
.nextInChain = nullptr,
459+
.label = "image bind group layout",
460+
.entryCount = 1,
461+
.entries = &imageBindGroupLayoutEntry,
462+
};
463+
464+
const WGPUBindGroupLayout imageBindGroupLayout =
465+
wgpuDeviceCreateBindGroupLayout(gpuContext.device, &imageBindGroupLayoutDesc);
466+
423467
// pipeline layout
424468

425-
std::array<WGPUBindGroupLayout, 3> bindGroupLayouts{
469+
std::array<WGPUBindGroupLayout, 4> bindGroupLayouts{
426470
uniformsBindGroupLayout,
427471
renderParamsBindGroupLayout,
428472
sceneBindGroupLayout,
473+
imageBindGroupLayout,
429474
};
430475

431476
const WGPUPipelineLayoutDescriptor pipelineLayoutDesc{
@@ -485,6 +530,22 @@ Renderer::Renderer(
485530
};
486531
sceneBindGroup = wgpuDeviceCreateBindGroup(gpuContext.device, &sceneBindGroupDesc);
487532

533+
// image bind group
534+
535+
const WGPUBindGroupEntry imageBindGroupEntry = imageBuffer.bindGroupEntry(0);
536+
537+
const WGPUBindGroupDescriptor imageBindGroupDesc{
538+
.nextInChain = nullptr,
539+
.label = "image bind group",
540+
.layout = imageBindGroupLayout,
541+
.entryCount = 1,
542+
.entries = &imageBindGroupEntry,
543+
};
544+
545+
imageBindGroup = wgpuDeviceCreateBindGroup(gpuContext.device, &imageBindGroupDesc);
546+
547+
// pipeline
548+
488549
const WGPURenderPipelineDescriptor pipelineDesc{
489550
.nextInChain = nullptr,
490551
.label = "Render pipeline",
@@ -544,6 +605,8 @@ Renderer::~Renderer()
544605
renderPipeline = nullptr;
545606
querySetSafeRelease(querySet);
546607
querySet = nullptr;
608+
bindGroupSafeRelease(imageBindGroup);
609+
imageBindGroup = nullptr;
547610
bindGroupSafeRelease(sceneBindGroup);
548611
sceneBindGroup = nullptr;
549612
bindGroupSafeRelease(renderParamsBindGroup);
@@ -554,7 +617,11 @@ Renderer::~Renderer()
554617

555618
void Renderer::setRenderParameters(const RenderParameters& renderParams)
556619
{
557-
currentRenderParams = renderParams;
620+
if (currentRenderParams != renderParams)
621+
{
622+
currentRenderParams = renderParams;
623+
accumulatedSampleCount = 0; // reset the temporal accumulation
624+
}
558625
}
559626

560627
void Renderer::render(const GpuContext& gpuContext, Gui& gui)
@@ -568,15 +635,20 @@ void Renderer::render(const GpuContext& gpuContext, Gui& gui)
568635
}
569636

570637
{
571-
// TODO: framebuffersize is now a part of render params struct, adjust constructor
572-
const RenderParamsLayout renderParamsLayout(
573-
currentRenderParams.framebufferSize, frameCount++, currentRenderParams);
638+
assert(accumulatedSampleCount <= currentRenderParams.samplingParams.numSamplesPerPixel);
639+
const RenderParamsLayout renderParamsLayout{
640+
currentRenderParams.framebufferSize,
641+
frameCount++,
642+
currentRenderParams,
643+
accumulatedSampleCount};
574644
wgpuQueueWriteBuffer(
575645
gpuContext.queue,
576646
renderParamsBuffer.handle(),
577647
0,
578648
&renderParamsLayout,
579649
sizeof(RenderParamsLayout));
650+
accumulatedSampleCount = std::min(
651+
accumulatedSampleCount + 1, currentRenderParams.samplingParams.numSamplesPerPixel);
580652
}
581653

582654
const WGPUCommandEncoder encoder = [&gpuContext]() {
@@ -620,6 +692,7 @@ void Renderer::render(const GpuContext& gpuContext, Gui& gui)
620692
wgpuRenderPassEncoderSetBindGroup(
621693
renderPassEncoder, 1, renderParamsBindGroup, 0, nullptr);
622694
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, sceneBindGroup, 0, nullptr);
695+
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, imageBindGroup, 0, nullptr);
623696
wgpuRenderPassEncoderSetVertexBuffer(
624697
renderPassEncoder, 0, vertexBuffer.handle(), 0, vertexBuffer.byteSize());
625698

@@ -731,4 +804,10 @@ float Renderer::averageRenderpassDurationMs() const
731804
renderPassDurationsNs.begin(), renderPassDurationsNs.end(), std::uint64_t(0));
732805
return 0.000001f * static_cast<float>(sum) / renderPassDurationsNs.size();
733806
}
807+
808+
float Renderer::renderProgressPercentage() const
809+
{
810+
return 100.0f * static_cast<float>(accumulatedSampleCount) /
811+
static_cast<float>(currentRenderParams.samplingParams.numSamplesPerPixel);
812+
}
734813
} // namespace nlrs

0 commit comments

Comments
 (0)