Commit f645ffaf authored by Johannes Braun's avatar Johannes Braun
Browse files

ITS not workinggit add -A!

parent f3962d9e
......@@ -17,6 +17,8 @@
#include "core/res/collada.h"
#include "components/Rotator.h"
#include <util/property.h>
using namespace glare;
enum class RenderMode
......@@ -469,16 +471,16 @@ void drawSettingsWindow()
case 0:
{
ImGui::TextWrapped("By default, a transformation or mesh update will trigger a pathtracer sample count reset. If you want to limit this to explicit settings changes and camera movements, uncheck the following option.");
bool collector_active = pathtracer->collector()->isActive();
if (ImGui::Checkbox("Reset on scene update", &collector_active))
pathtracer->collector()->setActive(collector_active);
ImGui::Spacing();
auto format = static_cast<int>(pathtracer->collector()->getSceneFormat());
if (ImGui::Combo("", &format, { "Default", "Global BVH" }))
if (ImGui::Combo("", &format, { "Default", "Global BVH" }) && format != static_cast<int>(pathtracer->collector()->getSceneFormat()))
{
pathtracer->collector()->setSceneFormat(static_cast<raytrace::SceneFormat>(format));
pathtracer->reset();
}
}
break;
......@@ -510,7 +512,7 @@ void drawSettingsWindow()
ImGui::Spacing();
int max_samples = pathtracer->getMaxSamples();
if (ImGui::DragInt("Maximum", &max_samples, 10, 1, 50000))
if (ImGui::DragInt("Maximum", &max_samples, 10, 1, 100000))
pathtracer->setMaxSamples(max_samples);
int spfr = pathtracer->getSamplesPerFrame();
......@@ -525,7 +527,7 @@ void drawSettingsWindow()
ImGui::TextWrapped("The global bounce limit clamps down all effect-dependant bounce limits to a unified maximum value.");
{
int bounces = pathtracer->getBounces();
if (ImGui::DragInt("Global Limit", &bounces, 0.1f, 0, 15))
if (ImGui::DragInt("Global Limit", &bounces, 0.1f, 0, 255))
pathtracer->setBounces(bounces);
ImGui::Spacing();
......
......@@ -5,17 +5,25 @@
#include <util/property.h>
class P
{
public:
void blub(unsigned bla)
{
}
unsigned blubber()
{
}
};
int main()
{
Property<unsigned> uiprop1([](const auto& val) {
std::cout << "Changed val to " << val << '\n';
});
Property<unsigned> uiprop2([]() {
std::cout << "Changed val!\n";
});
uiprop1 = 201;
uiprop2 = uiprop1 + 20;
P p;
//auto prop = make_property<int, P, unsigned>(&p, &P::blub, &P::blubber);
system("pause");
return 0;
......
......@@ -12,6 +12,7 @@ namespace tags
constexpr msg::id_type light = msg::make_id("LGHT");
constexpr msg::id_type scene = msg::make_id("SCEN");
constexpr msg::id_type material = msg::make_id("MATR");
constexpr msg::id_type skybox = msg::make_id("SKYB");
}
#endif // !INCLUDE_MESSAGE_TAGS_H
#include "skybox.h"
// --------------- STDLIB ---------------
// --------------- EXTERN ---------------
// --------------- INTERN ---------------
#include <raytrace/data/global_collector.h>
namespace glare::raytrace
{
SkyboxCollector::SkyboxCollector() {}
SkyboxCollector::~SkyboxCollector() {}
void SkyboxCollector::collect(const math::Flags &flags, const core::SceneNode &node)
{
if (flags & DirtyFlags::eSkybox)
{
if (auto&& skybox = node.getComponent<core::Skybox>()) {
m_skybox = skybox;
}
}
}
void SkyboxCollector::apply(core::Program &program)
{
if (m_skybox && m_skybox->isInitialized())
{
program.uniform("u_environment.cubemap", m_skybox->makeResident());
program.uniform("u_environment.has_cubemap", 1);
}
else
{
program.uniform("u_environment.color", glm::vec4(0.7f, 0.9f, 0.97f, 1));
program.uniform("u_environment.has_cubemap", 0);
}
}
}
\ No newline at end of file
#ifndef INCLUDE_SKYBOX_COLLECTOR_H
#define INCLUDE_SKYBOX_COLLECTOR_H
// --------------- STDLIB ---------------
#include <vector>
// --------------- EXTERN ---------------
#include <core/base/program.h>
#include <core/objects/skybox.h>
// --------------- INTERN ---------------
#include <raytrace/data/global_collector_unit.h>
#include <raytrace/structs.h>
namespace glare::raytrace
{
class SkyboxCollector : public CollectorUnit
{
public:
SkyboxCollector();
SkyboxCollector(SkyboxCollector& other) = default;
SkyboxCollector(SkyboxCollector&& other) = default;
SkyboxCollector& operator=(SkyboxCollector& other) = default;
SkyboxCollector& operator=(SkyboxCollector&& other) = default;
~SkyboxCollector() override;
void clear(const math::Flags &flags) override {}
void build(const math::Flags &flags) override {}
void collect(const math::Flags &flags, const core::SceneNode &node) override;
void apply(core::Program &program) override;
private:
std::shared_ptr<core::Skybox> m_skybox;
};
}
#endif //INCLUDE_SKYBOX_COLLECTOR_H
......@@ -3,6 +3,7 @@
#include "collector_units/mesh.h"
#include "collector_units/light.h"
#include "collector_units/camera.h"
#include "collector_units/skybox.h"
#include "core/state.h"
namespace glare::raytrace
......@@ -18,14 +19,15 @@ namespace glare::raytrace
m_units.emplace("mesh_collector", std::make_shared<MeshCollector>());
m_units.emplace("light_collector", std::make_shared<LightCollector>());
m_units.emplace("camera_collector", std::make_shared<CameraCollector>());
m_units.emplace("skybox_collector", std::make_shared<SkyboxCollector>());
// And register tags and evaluators for receiving and process scene changes
core::Context::current().messages()->addObserver(this, tags::camera, tags::light, tags::mesh_transform, tags::nodes, tags::material);
core::Context::current().messages()->addObserver(this, tags::camera, tags::light, tags::mesh_transform, tags::nodes, tags::material, tags::skybox);
m_evaluators.emplace(tags::camera, TagEvaluator(DirtyFlags::eCamera));
m_evaluators.emplace(tags::light, TagEvaluator(DirtyFlags::eLight));
m_evaluators.emplace(tags::material, TagEvaluator(DirtyFlags::eMaterial));
m_evaluators.emplace(tags::material, TagEvaluator(DirtyFlags::eMaterial));
// Only update on mesh transform changes if the collector is currently active.
m_evaluators.emplace(tags::mesh_transform, TagEvaluator(DirtyFlags::eMeshTransform, [&](const msg::message_type &message) { return m_is_active; }));
m_evaluators.emplace(tags::nodes, TagEvaluator(DirtyFlags::eAll, [&](const msg::message_type &message) { return m_is_active; }));
......@@ -37,9 +39,13 @@ namespace glare::raytrace
void SceneCollector::setSceneFormat(SceneFormat format)
{
m_format = format;
setDirty(DirtyFlags::eMeshTransform);
std::static_pointer_cast<MeshCollector>(m_units["mesh_collector"])->setUseBVH(m_format == SceneFormat::eBVH);
collect();
core::Context::current().messages().id(tags::mesh_transform).push();
}
SceneFormat SceneCollector::getSceneFormat() const
{
return m_format;
}
SceneCollector::~SceneCollector()
......
......@@ -22,6 +22,7 @@ namespace glare::raytrace
eMaterial = 1 << 2,
eLight = 1 << 3,
eCamera = 1 << 4,
eSkybox = 1 << 5,
eNone = 0,
eAll = ~0
};
......@@ -58,10 +59,7 @@ namespace glare::raytrace
void setSceneFormat(SceneFormat format);
SceneFormat getSceneFormat() const
{
return m_format;
}
SceneFormat getSceneFormat() const;
private:
struct TagEvaluator;
......
......@@ -6,6 +6,8 @@
namespace glare::raytrace
{
std::unique_ptr<core::Program> Pathtracer::m_render_shader = nullptr;
Pathtracer::Pathtracer()
: Pathtracer(RayGeneratorType::eTrace)
{
......@@ -49,9 +51,8 @@ namespace glare::raytrace
m_linespace_config.distance_threshold = 8.f;
m_render_config.bounces = 16;
Log_Hint << "Pathtracing Shader will now be compiled. This might take a couple of seconds.";
m_render_shader = std::make_unique<core::Program>(files::shader("/pathtracer/pathtracer.comp"));
if(!m_render_shader) m_render_shader = std::make_unique<core::Program>(files::shader("/pathtracer/pathtracer.comp"));
m_generator_type = ray_generator;
m_trace_buffer = std::make_unique<core::Buffer>();
core::Context::current().messages()->addObserver(this, tags::scene);
......@@ -75,24 +76,24 @@ namespace glare::raytrace
//bounces
const auto bounces_child = pt_child.find_child_by_attribute("group", "name", "bounces");
m_bounce_count_thresholds[static_cast<unsigned>(EffectType::eDiffusion)] = static_cast<uint8_t>(bounces_child.find_child_by_attribute("item", "name", "diffusion").attribute("value").as_uint(2));
m_bounce_count_thresholds[static_cast<unsigned>(EffectType::eReflection)] = static_cast<uint8_t>(bounces_child.find_child_by_attribute("item", "name", "reflection").attribute("value").as_uint(4));
m_bounce_count_thresholds[static_cast<unsigned>(EffectType::eTransmission)] = static_cast<uint8_t>(bounces_child.find_child_by_attribute("item", "name", "transmission").attribute("value").as_uint(4));
m_bounce_count_thresholds[static_cast<unsigned>(EffectType::eEmission)] = static_cast<uint8_t>(bounces_child.find_child_by_attribute("item", "name", "emission").attribute("value").as_uint(8));
dynamic_config.bounces.thresholds[static_cast<unsigned>(EffectType::eDiffusion)] = static_cast<uint8_t>(bounces_child.find_child_by_attribute("item", "name", "diffusion").attribute("value").as_uint(2));
dynamic_config.bounces.thresholds[static_cast<unsigned>(EffectType::eReflection)] = static_cast<uint8_t>(bounces_child.find_child_by_attribute("item", "name", "reflection").attribute("value").as_uint(4));
dynamic_config.bounces.thresholds[static_cast<unsigned>(EffectType::eTransmission)] = static_cast<uint8_t>(bounces_child.find_child_by_attribute("item", "name", "transmission").attribute("value").as_uint(4));
dynamic_config.bounces.thresholds[static_cast<unsigned>(EffectType::eEmission)] = static_cast<uint8_t>(bounces_child.find_child_by_attribute("item", "name", "emission").attribute("value").as_uint(8));
//ls thresholds
const auto ls_thresholds_child = pt_child.find_child_by_attribute("group", "name", "ls-thresholds");
m_ls_bounce_count_thresholds[static_cast<unsigned>(EffectType::eDiffusion)] = static_cast<uint8_t>(ls_thresholds_child.find_child_by_attribute("item", "name", "diffusion").attribute("value").as_uint(2));
m_ls_bounce_count_thresholds[static_cast<unsigned>(EffectType::eReflection)] = static_cast<uint8_t>(ls_thresholds_child.find_child_by_attribute("item", "name", "reflection").attribute("value").as_uint(4));
m_ls_bounce_count_thresholds[static_cast<unsigned>(EffectType::eTransmission)] = static_cast<uint8_t>(ls_thresholds_child.find_child_by_attribute("item", "name", "transmission").attribute("value").as_uint(4));
m_ls_bounce_count_thresholds[static_cast<unsigned>(EffectType::eEmission)] = static_cast<uint8_t>(ls_thresholds_child.find_child_by_attribute("item", "name", "emission").attribute("value").as_uint(8));
dynamic_config.linespace.thresholds.bounces[static_cast<unsigned>(EffectType::eDiffusion)] = static_cast<uint8_t>(ls_thresholds_child.find_child_by_attribute("item", "name", "diffusion").attribute("value").as_uint(2));
dynamic_config.linespace.thresholds.bounces[static_cast<unsigned>(EffectType::eReflection)] = static_cast<uint8_t>(ls_thresholds_child.find_child_by_attribute("item", "name", "reflection").attribute("value").as_uint(4));
dynamic_config.linespace.thresholds.bounces[static_cast<unsigned>(EffectType::eTransmission)] = static_cast<uint8_t>(ls_thresholds_child.find_child_by_attribute("item", "name", "transmission").attribute("value").as_uint(4));
dynamic_config.linespace.thresholds.bounces[static_cast<unsigned>(EffectType::eEmission)] = static_cast<uint8_t>(ls_thresholds_child.find_child_by_attribute("item", "name", "emission").attribute("value").as_uint(8));
dynamic_config.linespace.thresholds.distance = ls_thresholds_child.find_child_by_attribute("item", "name", "distance").attribute("value").as_float(10.f);
m_linespace_config.accuracy_threshold = ls_thresholds_child.find_child_by_attribute("item", "name", "accuracy").attribute("value").as_float(0.5f);
m_linespace_config.shadow_threshold = ls_thresholds_child.find_child_by_attribute("item", "name", "shadow").attribute("value").as_float(0.5f);
m_linespace_config.distance_threshold = ls_thresholds_child.find_child_by_attribute("item", "name", "distance").attribute("value").as_float(10.f);
dynamic_config.linespace.quality.accuracy = ls_thresholds_child.find_child_by_attribute("item", "name", "accuracy").attribute("value").as_float(0.5f);
dynamic_config.linespace.quality.shadow = ls_thresholds_child.find_child_by_attribute("item", "name", "shadow").attribute("value").as_float(0.5f);
m_render_config.clamp_direct = pt_child.find_child_by_attribute("item", "name", "clamp-direct").attribute("value").as_float(8);
m_render_config.clamp_indirect = pt_child.find_child_by_attribute("item", "name", "clamp-indirect").attribute("value").as_float(8);
dynamic_config.clamp.direct = pt_child.find_child_by_attribute("item", "name", "clamp-direct").attribute("value").as_float(8);
dynamic_config.clamp.indirect = pt_child.find_child_by_attribute("item", "name", "clamp-indirect").attribute("value").as_float(8);
}
void Pathtracer::reset()
......@@ -101,27 +102,27 @@ namespace glare::raytrace
m_collector->apply(*m_render_shader);
m_render_shader->uniform("u_render_config.num_bounces", m_render_config.bounces);
m_render_shader->uniform("u_render_config.clamp_direct", m_render_config.clamp_direct);
m_render_shader->uniform("u_render_config.clamp_indirect", m_render_config.clamp_indirect);
m_render_shader->uniform("u_render_config.num_bounces", dynamic_config.bounces.count);
m_render_shader->uniform("u_render_config.clamp_direct", dynamic_config.clamp.direct);
m_render_shader->uniform("u_render_config.clamp_indirect", dynamic_config.clamp.indirect);
m_render_shader->uniform("u_linespace_properties.accuracy_quality", m_linespace_config.accuracy_threshold);
m_render_shader->uniform("u_linespace_properties.shadow_quality", m_linespace_config.shadow_threshold);
m_render_shader->uniform("u_linespace_properties.distance_threshold", m_linespace_config.distance_threshold);
m_render_shader->uniform("u_linespace_properties.accuracy_quality", dynamic_config.linespace.quality.accuracy);
m_render_shader->uniform("u_linespace_properties.shadow_quality", dynamic_config.linespace.quality.shadow);
m_render_shader->uniform("u_linespace_properties.distance_threshold", dynamic_config.linespace.thresholds.distance);
unsigned bounce_thresholds = 0;
for (int i = 0; i < 4; ++i)
{
bounce_thresholds |= (m_bounce_count_thresholds[i] & 0xff) << (8 * i);
bounce_thresholds |= (dynamic_config.bounces.thresholds[i] & 0xff) << (8 * i);
}
m_render_shader->uniform("u_render_config.bounce_thresholds", bounce_thresholds);
m_linespace_config.bounce_thresholds = 0;
unsigned bounce_thresholds = 0;
for (int i = 0; i < 4; ++i)
{
m_linespace_config.bounce_thresholds |= (m_ls_bounce_count_thresholds[i] & 0xff) << (8 * i);
bounce_thresholds |= (dynamic_config.linespace.thresholds.bounces[i] & 0xff) << (8 * i);
}
m_render_shader->uniform("u_linespace_properties.bounce_thresholds", m_linespace_config.bounce_thresholds);
m_render_shader->uniform("u_linespace_properties.bounce_thresholds", bounce_thresholds);
m_render_shader->bindSubroutine(gl::ShaderType::eCompute, "u_light_sample[0]", "samplePoint");
m_render_shader->bindSubroutine(gl::ShaderType::eCompute, "u_light_sample[1]", "sampleSpot");
......@@ -131,41 +132,29 @@ namespace glare::raytrace
const core::TextureRGBA_32F &Pathtracer::render(uint32_t width, uint32_t height)
{
static auto color_storage = std::make_unique<core::TextureRGBA_32F>(width, height);
if (width != m_last_width || height != m_last_height) {
m_last_width = width;
m_last_height = height;
m_trace_buffer->reserve<Trace>(width * height, gl::BufferUsage::eDynamicCopy);
m_trace_buffer.reserve<Trace>(width * height, gl::BufferUsage::eDynamicCopy);
m_render_target = std::make_unique<core::TextureRGBA_32F>(width, height);
color_storage = std::make_unique<core::TextureRGBA_32F>(width, height);
m_color_storage = std::make_unique<core::TextureRGBA_32F>(width, height);
m_render_shader->uniform("u_render_target", m_render_target->makeImageResident(gl::Access::eReadWrite));
m_render_shader->uniform("u_color_store", color_storage->makeImageResident(gl::Access::eReadWrite));
m_render_shader->storageBuffer("trace_buffer", *m_trace_buffer);
m_render_config.current_sample = 0;
m_render_shader->uniform("u_color_store", m_color_storage->makeImageResident(gl::Access::eReadWrite));
m_render_shader->storageBuffer("trace_buffer", m_trace_buffer);
samples.current = 0;
}
if (m_render_config.current_sample == 0)
if (samples.current == 0)
{
m_collector->collect();
gl::clearTextureImage(color_storage->id(), 0, gl::TextureFormat::eRGBA, gl::Type::eFloat, glm::value_ptr(glm::vec4(0)));
}
if (m_skybox && m_skybox->isInitialized())
{
m_render_shader->uniform("u_environment.cubemap", m_skybox->makeResident());
m_render_shader->uniform("u_environment.has_cubemap", 1);
}
else
{
m_render_shader->uniform("u_environment.color", glm::vec4(0.7f, 0.9f, 0.97f, 1));
m_render_shader->uniform("u_environment.has_cubemap", 0);
gl::clearTextureImage(m_color_storage->id(), 0, gl::TextureFormat::eRGBA, gl::Type::eFloat, glm::value_ptr(glm::vec4(0)));
}
for (unsigned sample = 0; sample < m_render_config.samples_per_frame; sample++) {
if (m_render_config.current_sample >= m_render_config.max_samples)
if (samples.current >= samples.max)
return *m_render_target;
if (debug.print_render_times) core::ClockGL::instance().start();
......@@ -175,12 +164,12 @@ namespace glare::raytrace
if (debug.print_render_times) core::ClockGL::instance().start();
m_render_shader->use();
m_render_shader->uniform("u_render_config.current_sample", m_render_config.current_sample);
m_render_shader->uniform("u_render_config.current_sample", samples.current);
m_render_shader->uniform("random_seed", ray_generator->randomSeed());
m_render_shader->dispatch2d(width, 16, height, 16);
if (debug.print_render_times) Log_Hint << "RENDER TIME [Trace and Bounce]: " << core::ClockGL::instance().end() << " ns";
++m_render_config.current_sample;
++samples.current;
}
return *m_render_target;
......@@ -216,7 +205,6 @@ namespace glare::raytrace
{
m_collector = collector;
m_skybox = core::Context::current().skybox();
reset();
}
......@@ -232,7 +220,6 @@ namespace glare::raytrace
m_collector->collect(graph_root);
}
m_skybox = core::Context::current().skybox();
reset();
}
......@@ -262,9 +249,9 @@ namespace glare::raytrace
return m_last_height;
}
core::Buffer& Pathtracer::traceBuffer() const
core::Buffer& Pathtracer::traceBuffer()
{
return *m_trace_buffer;
return m_trace_buffer;
}
const core::TextureRGBA_32F& Pathtracer::renderTarget() const
......@@ -279,19 +266,13 @@ namespace glare::raytrace
unsigned Pathtracer::currentSample() const
{
return m_render_config.current_sample;
return samples.current;
}
/*
void Pathtracer::setEffectBounces(EffectType type, uint8_t bounces)
{
uint8_t clamped = bounces & 0xff;
if (clamped != bounces)
{
Log_Warn << bounces << " - value larger than 15 (0xf). Result of " << clamped << " may be unintended.";
}
m_bounce_count_thresholds[static_cast<unsigned>(type)] = clamped;
m_bounce_count_thresholds[static_cast<unsigned>(type)] = bounces;
reset();
}
......@@ -302,14 +283,7 @@ namespace glare::raytrace
void Pathtracer::setLinespaceBounceThresholds(EffectType type, uint8_t bounces)
{
uint8_t clamped = bounces & 0xff;
if (clamped != bounces)
{
Log_Warn << bounces << " - value larger than 15 (0xf). Result of " << clamped << " may be unintended.";
}
m_ls_bounce_count_thresholds[unsigned(type)] = clamped;
m_ls_bounce_count_thresholds[unsigned(type)] = bounces;
reset();
}
......@@ -318,17 +292,6 @@ namespace glare::raytrace
return m_ls_bounce_count_thresholds[static_cast<unsigned>(type)];
}
void Pathtracer::setSkybox(std::shared_ptr<core::Skybox> skybox)
{
m_skybox = skybox;
reset();
}
std::shared_ptr<core::Skybox> Pathtracer::getSkybox() const
{
return m_skybox;
}
void Pathtracer::setBounces(unsigned count)
{
reset();
......@@ -408,5 +371,5 @@ namespace glare::raytrace
float Pathtracer::getDistanceThreshold() const
{
return m_linespace_config.distance_threshold;
}
}*/
}
......@@ -56,38 +56,42 @@ namespace glare::raytrace
uint32_t height() const;
const core::Program& shader() const;
core::Buffer& traceBuffer() const;
core::Buffer& traceBuffer();
const core::TextureRGBA_32F& renderTarget() const;
std::shared_ptr<SceneCollector> collector() const;
unsigned currentSample() const;
void setEffectBounces(EffectType type, uint8_t bounces);
/* void setEffectBounces(EffectType type, uint8_t bounces);
uint8_t getEffectBounces(EffectType type);
void setLinespaceBounceThresholds(EffectType type, uint8_t bounces);
uint8_t getLinespaceBounceThreshold(EffectType type);
void setSkybox(std::shared_ptr<core::Skybox> skybox);
std::shared_ptr<core::Skybox> Pathtracer::getSkybox() const;
void setBounces(unsigned count);
unsigned getBounces() const;
void setMaxSamples(unsigned count);
unsigned getMaxSamples() const;
void setSamplesPerFrame(unsigned count);
unsigned getSamplesPerFrame() const;
float getClampDirect() const;
void setClampDirect(float clamp_direct);
float getClampIndirect() const;
void setClampIndirect(float clamp_indirect);
float getAccuracyThreshold() const;
void setAccuracyThreshold(float accuracy_threshold);
float getShadowThreshold() const;
void setShadowThreshold(float shadow_threshold);
float getDistanceThreshold() const;
void setDistanceThreshold(float distance_threshold);
void setDistanceThreshold(float distance_threshold);*/
struct
/* struct
{
unsigned current_sample = 0;
unsigned max_samples = 80000;
......@@ -100,13 +104,63 @@ namespace glare::raytrace
struct
{
bool print_render_times = false;
} debug;
} debug;*/
private:
struct Configuration {
struct {
unsigned count = 16;
std::array<uint8_t, 4> thresholds;
} bounces;
struct {
float direct = 10.f;
float indirect = 10.f;
} clamp;
struct {
bool show_times = false;
} debug;
struct {
struct
{
float accuracy = 0.5f;
float shadow = 0.5f;
} quality;
struct
{
float distance = 10.f;
std::array<uint8_t, 4> bounces;
} thresholds;
} linespace;
bool operator!=(const Configuration& other) {
return bounces.count != other.bounces.count &&
bounces.thresholds == other.bounces.thresholds &&
clamp.direct == other.clamp.direct &&
clamp.indirect == other.clamp.indirect &&
linespace.quality.accuracy == other.linespace.quality.accuracy &&
linespace.quality.shadow == other.linespace.quality.shadow &&
linespace.thresholds.distance = other.linespace.thresholds.distance &&
linespace.thresholds.bounces = other.linespace.thresholds.bounces;
}
} dynamic_config;
struct {
unsigned current = 0;
unsigned max = 80'000;
unsigned per_frame = 1;
} samples;
static std::unique_ptr<core::Program> m_render_shader;
uint32_t m_last_width = 0;
uint32_t m_last_height = 0;
struct {
/*struct {
float accuracy_threshold;