Skip to content

Commit

Permalink
dynamic shadowmap visualization (#7274)
Browse files Browse the repository at this point in the history
* debugging PCF mode

This mode always uses a hard PCF and takes a 
slightly slower code path.

* dynamic shadowmap visualization

The directional shadowmap visualizer is implemented behind a 
specialization constant. Add the DebugRegistry infrastructure to be
able to update the spec-constant at runtime and have a subset of 
all materials invalidated.

This allows to toggle the visualization at runtime using a debug
property.

This is also a proof of concept that we can update spec-constants
at runtime; we could probably leverage this work for engine-wide
shader configurations.

* Update main.fs

* Update filament/src/details/Material.cpp

Co-authored-by: Powei Feng <[email protected]>

---------

Co-authored-by: Powei Feng <[email protected]>
  • Loading branch information
pixelflinger and poweifeng authored Oct 19, 2023
1 parent 3c77d2c commit 6498cf5
Show file tree
Hide file tree
Showing 22 changed files with 247 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1930,6 +1930,7 @@ public enum ShadowType {
* PCF with soft shadows and contact hardening
*/
PCSS,
PCFd,
}

/**
Expand Down
19 changes: 16 additions & 3 deletions filament/include/filament/DebugRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,28 @@ class UTILS_PUBLIC DebugRegistry : public FilamentAPI {
* @return Address of the data of the \p name property
* @{
*/
void* getPropertyAddress(const char* name) noexcept;
void* getPropertyAddress(const char* name);

void const* getPropertyAddress(const char* name) const noexcept;

template<typename T>
inline T* getPropertyAddress(const char* name) {
return static_cast<T*>(getPropertyAddress(name));
}

template<typename T>
inline T* getPropertyAddress(const char* name) noexcept {
inline T const* getPropertyAddress(const char* name) const noexcept {
return static_cast<T*>(getPropertyAddress(name));
}

template<typename T>
inline bool getPropertyAddress(const char* name, T** p) noexcept {
inline bool getPropertyAddress(const char* name, T** p) {
*p = getPropertyAddress<T>(name);
return *p != nullptr;
}

template<typename T>
inline bool getPropertyAddress(const char* name, T* const* p) const noexcept {
*p = getPropertyAddress<T>(name);
return *p != nullptr;
}
Expand Down
3 changes: 2 additions & 1 deletion filament/include/filament/Options.h
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,8 @@ enum class ShadowType : uint8_t {
PCF, //!< percentage-closer filtered shadows (default)
VSM, //!< variance shadows
DPCF, //!< PCF with contact hardening simulation
PCSS //!< PCF with soft shadows and contact hardening
PCSS, //!< PCF with soft shadows and contact hardening
PCFd, // for debugging only, don't use.
};

/**
Expand Down
6 changes: 5 additions & 1 deletion filament/src/DebugRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ bool DebugRegistry::getProperty(const char* name, float4* v) const noexcept {
return downcast(this)->getProperty(name, v);
}

void *DebugRegistry::getPropertyAddress(const char *name) noexcept {
void *DebugRegistry::getPropertyAddress(const char *name) {
return downcast(this)->getPropertyAddress(name);
}

void const *DebugRegistry::getPropertyAddress(const char *name) const noexcept {
return downcast(this)->getPropertyAddress(name);
}

Expand Down
11 changes: 11 additions & 0 deletions filament/src/PerViewUniforms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,17 @@ void PerViewUniforms::prepareShadowPCSS(Handle<HwTexture> texture,
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
}

void PerViewUniforms::prepareShadowPCFDebug(Handle<HwTexture> texture,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept {
mSamplers.setSampler(PerViewSib::SHADOW_MAP, { texture, {
.filterMag = SamplerMagFilter::NEAREST,
.filterMin = SamplerMinFilter::NEAREST
}});
auto& s = mUniforms.edit();
s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCF;
PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms);
}

void PerViewUniforms::commit(backend::DriverApi& driver) noexcept {
if (mUniforms.isDirty()) {
driver.updateBufferObject(mUniformBufferHandle, mUniforms.toBufferDescriptor(driver), 0);
Expand Down
3 changes: 3 additions & 0 deletions filament/src/PerViewUniforms.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ class PerViewUniforms {
ShadowMappingUniforms const& shadowMappingUniforms,
SoftShadowOptions const& options) noexcept;

void prepareShadowPCFDebug(TextureHandle texture,
ShadowMappingUniforms const& shadowMappingUniforms) noexcept;

// update local data into GPU UBO
void commit(backend::DriverApi& driver) noexcept;

Expand Down
43 changes: 32 additions & 11 deletions filament/src/details/DebugRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include "details/DebugRegistry.h"

#include <utils/Panic.h>

#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
Expand All @@ -33,33 +35,53 @@ namespace filament {

FDebugRegistry::FDebugRegistry() noexcept = default;

UTILS_NOINLINE
void* FDebugRegistry::getPropertyAddress(const char* name) noexcept {
std::string_view key{ name };
auto FDebugRegistry::getPropertyInfo(const char* name) noexcept -> PropertyInfo {
std::string_view const key{ name };
auto& propertyMap = mPropertyMap;
if (propertyMap.find(key) == propertyMap.end()) {
return nullptr;
return { nullptr, {} };
}
return propertyMap[key];
}

void FDebugRegistry::registerProperty(std::string_view name, void* p, Type type) noexcept {
UTILS_NOINLINE
void* FDebugRegistry::getPropertyAddress(const char* name) {
auto info = getPropertyInfo(name);
ASSERT_PRECONDITION_NON_FATAL(!info.second,
"don't use DebugRegistry::getPropertyAddress() when a callback is set. "
"Use setProperty() instead.");
return info.first;
}

UTILS_NOINLINE
void const* FDebugRegistry::getPropertyAddress(const char* name) const noexcept {
auto info = const_cast<FDebugRegistry*>(this)->getPropertyInfo(name);
return info.first;
}

void FDebugRegistry::registerProperty(std::string_view name, void* p, Type,
std::function<void()> fn) noexcept {
auto& propertyMap = mPropertyMap;
if (propertyMap.find(name) == propertyMap.end()) {
propertyMap[name] = p;
propertyMap[name] = { p, std::move(fn) };
}
}

bool FDebugRegistry::hasProperty(const char* name) const noexcept {
return const_cast<FDebugRegistry*>(this)->getPropertyAddress(name) != nullptr;
return getPropertyAddress(name) != nullptr;
}

template<typename T>
bool FDebugRegistry::setProperty(const char* name, T v) noexcept {
if constexpr (DEBUG_PROPERTIES_WRITABLE) {
T* const addr = static_cast<T*>(getPropertyAddress(name));
auto info = getPropertyInfo(name);
T* const addr = static_cast<T*>(info.first);
if (addr) {
auto old = *addr;
*addr = v;
if (info.second && old != v) {
info.second();
}
return true;
}
}
Expand All @@ -75,8 +97,7 @@ template bool FDebugRegistry::setProperty<float4>(const char* name, float4 v) no

template<typename T>
bool FDebugRegistry::getProperty(const char* name, T* p) const noexcept {
FDebugRegistry* const pRegistry = const_cast<FDebugRegistry*>(this);
T const* const addr = static_cast<T*>(pRegistry->getPropertyAddress(name));
T const* const addr = static_cast<T const*>(getPropertyAddress(name));
if (addr) {
*p = *addr;
return true;
Expand All @@ -100,7 +121,7 @@ void FDebugRegistry::registerDataSource(std::string_view name,
}

DebugRegistry::DataSource FDebugRegistry::getDataSource(const char* name) const noexcept {
std::string_view key{ name };
std::string_view const key{ name };
auto& dataSourceMap = mDataSourceMap;
auto const& it = dataSourceMap.find(key);
if (it == dataSourceMap.end()) {
Expand Down
42 changes: 39 additions & 3 deletions filament/src/details/DebugRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@

#include <utils/compiler.h>

#include <functional>
#include <string_view>
#include <unordered_map>
#include <utility>

namespace filament {

Expand Down Expand Up @@ -58,6 +60,37 @@ class FDebugRegistry : public DebugRegistry {
registerProperty(name, p, FLOAT4);
}


void registerProperty(std::string_view name, bool* p,
std::function<void()> fn) noexcept {
registerProperty(name, p, BOOL, std::move(fn));
}

void registerProperty(std::string_view name, int* p,
std::function<void()> fn) noexcept {
registerProperty(name, p, INT, std::move(fn));
}

void registerProperty(std::string_view name, float* p,
std::function<void()> fn) noexcept {
registerProperty(name, p, FLOAT, std::move(fn));
}

void registerProperty(std::string_view name, math::float2* p,
std::function<void()> fn) noexcept {
registerProperty(name, p, FLOAT2, std::move(fn));
}

void registerProperty(std::string_view name, math::float3* p,
std::function<void()> fn) noexcept {
registerProperty(name, p, FLOAT3, std::move(fn));
}

void registerProperty(std::string_view name, math::float4* p,
std::function<void()> fn) noexcept {
registerProperty(name, p, FLOAT4, std::move(fn));
}

void registerDataSource(std::string_view name, void const* data, size_t count) noexcept;

#if !defined(_MSC_VER)
Expand All @@ -67,12 +100,15 @@ class FDebugRegistry : public DebugRegistry {
template<typename T> bool setProperty(const char* name, T v) noexcept;

private:
using PropertyInfo = std::pair<void*, std::function<void()>>;
friend class DebugRegistry;
void registerProperty(std::string_view name, void* p, Type type) noexcept;
void registerProperty(std::string_view name, void* p, Type type, std::function<void()> fn = {}) noexcept;
bool hasProperty(const char* name) const noexcept;
void* getPropertyAddress(const char* name) noexcept;
PropertyInfo getPropertyInfo(const char* name) noexcept;
void* getPropertyAddress(const char* name);
void const* getPropertyAddress(const char* name) const noexcept;
DataSource getDataSource(const char* name) const noexcept;
std::unordered_map<std::string_view, void*> mPropertyMap;
std::unordered_map<std::string_view, PropertyInfo> mPropertyMap;
std::unordered_map<std::string_view, DataSource> mDataSourceMap;
};

Expand Down
11 changes: 11 additions & 0 deletions filament/src/details/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,17 @@ void FEngine::init() {
mLightManager.init(*this);
mDFG.init(*this);
}

mDebugRegistry.registerProperty("d.shadowmap.debug_directional_shadowmap",
&debug.shadowmap.debug_directional_shadowmap, [this]() {
mMaterials.forEach([](FMaterial* material) {
if (material->getMaterialDomain() == MaterialDomain::SURFACE) {
material->invalidate(
Variant::DIR | Variant::SRE | Variant::DEP,
Variant::DIR | Variant::SRE);
}
});
});
}

FEngine::~FEngine() noexcept {
Expand Down
1 change: 1 addition & 0 deletions filament/src/details/Engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ class FEngine : public Engine {
// these are the debug properties used by FDebug. They're accessed directly by modules who need them.
struct {
struct {
bool debug_directional_shadowmap = false;
bool far_uses_shadowcasters = true;
bool focus_shadowcasters = true;
bool visualize_cascades = false;
Expand Down
51 changes: 47 additions & 4 deletions filament/src/details/Material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT,
(int)maxFroxelBufferHeight });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP,
(bool)engine.debug.shadowmap.debug_directional_shadowmap });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_STATIC_TEXTURE_TARGET_WORKAROUND,
(bool)staticTextureWorkaround });
Expand Down Expand Up @@ -447,6 +450,48 @@ FMaterial::~FMaterial() noexcept {
delete mMaterialParser;
}

void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
// update the spec constants that can change
// TODO: should we just always update all of them?
auto pos = std::find_if(mSpecializationConstants.begin(), mSpecializationConstants.end(),
[&](const auto& item) {
return item.id == ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP;
});
if (pos != mSpecializationConstants.end()) {
pos->value = mEngine.debug.shadowmap.debug_directional_shadowmap;
}

DriverApi& driverApi = mEngine.getDriverApi();
auto& cachedPrograms = mCachedPrograms;
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
Variant const variant(k);
if ((k & variantMask) == variantValue) {
if (UTILS_LIKELY(!mIsDefaultMaterial)) {
// The depth variants may be shared with the default material, in which case
// we should not free it now.
bool const isSharedVariant =
Variant::isValidDepthVariant(variant) && !mHasCustomDepthShader;
if (isSharedVariant) {
// we don't own this variant, skip.
continue;
}
}
driverApi.destroyProgram(cachedPrograms[k]);
cachedPrograms[k].clear();
}
}

if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
for (Variant const variant: pDefaultMaterial->mDepthVariants) {
pDefaultMaterial->prepareProgram(variant);
if (!cachedPrograms[variant.key]) {
cachedPrograms[variant.key] = pDefaultMaterial->getProgram(variant);
}
}
}
}

void FMaterial::terminate(FEngine& engine) {

#if FILAMENT_ENABLE_MATDBG
Expand Down Expand Up @@ -719,10 +764,7 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
void FMaterial::applyPendingEdits() noexcept {
const char* name = mName.c_str();
slog.d << "Applying edits to " << (name ? name : "(untitled)") << io::endl;
destroyPrograms(mEngine);
for (auto& program : mCachedPrograms) {
program.clear();
}
destroyPrograms(mEngine); // FIXME: this will not destroy the shared variants
delete mMaterialParser;
mMaterialParser = mPendingEdits;
mPendingEdits = nullptr;
Expand Down Expand Up @@ -772,6 +814,7 @@ void FMaterial::destroyPrograms(FEngine& engine) {
}
}
driverApi.destroyProgram(cachedPrograms[k]);
cachedPrograms[k].clear();
}
}

Expand Down
2 changes: 2 additions & 0 deletions filament/src/details/Material.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ class FMaterial : public Material {
return bool(mCachedPrograms[variant.key]);
}

void invalidate(Variant::type_t variantMask = 0, Variant::type_t variantValue = 0) noexcept;

// prepareProgram creates the program for the material's given variant at the backend level.
// Must be called outside of backend render pass.
// Must be called before getProgram() below.
Expand Down
3 changes: 3 additions & 0 deletions filament/src/details/View.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,9 @@ void FView::prepareShadow(Handle<HwTexture> texture) const noexcept {
case filament::ShadowType::PCSS:
mPerViewUniforms.prepareShadowPCSS(texture, uniforms, mSoftShadowOptions);
break;
case filament::ShadowType::PCFd:
mPerViewUniforms.prepareShadowPCFDebug(texture, uniforms);
break;
}
}

Expand Down
1 change: 1 addition & 0 deletions libs/filabridge/include/private/filament/EngineEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ enum class ReservedSpecializationConstants : uint8_t {
CONFIG_SRGB_SWAPCHAIN_EMULATION = 3, // don't change (hardcoded in OpenGLDriver.cpp)
CONFIG_FROXEL_BUFFER_HEIGHT = 4,
CONFIG_POWER_VR_SHADER_WORKAROUNDS = 5,
CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP = 6,
};

// This value is limited by UBO size, ES3.0 only guarantees 16 KiB.
Expand Down
4 changes: 4 additions & 0 deletions libs/filamat/src/shaders/CodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT, 1024);
}

// directional shadowmap visualization
generateSpecializationConstant(out, "CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP",
+ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP, false);

// Workaround a Metal pipeline compilation error with the message:
// "Could not statically determine the target of a texture". See light_indirect.fs
generateSpecializationConstant(out, "CONFIG_STATIC_TEXTURE_TARGET_WORKAROUND",
Expand Down
Loading

0 comments on commit 6498cf5

Please sign in to comment.