Dynamic "Material constants" support

Material constants (a.k.a: specialization constants) can only be set
during Material creation through Material::Builder.
This change somewhat relaxes that limitation by allowing constants to
be set at runtime on Material directly.

Currently this new API is still private and only supported on FMaterial.

This feature works by invalidating the HwProgram cache of the concerned
Material, causing a shader recompile per variant; so this API is costly
and should be used only for debugging or during app/game configuration.

The TAA material is modified to use constants instead of #defines for
various settings and those are exposed in TaaOptions as well is in 
ViewerGui. So with this change all aspects of the TAA material can
be changed at runtime.
This commit is contained in:
Mathias Agopian
2023-12-11 16:20:47 -08:00
committed by Mathias Agopian
parent 2f62978e4c
commit 2a67152dda
29 changed files with 545 additions and 140 deletions
@@ -1876,11 +1876,44 @@ public class View {
/**
* Options for Temporal Anti-aliasing (TAA)
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
* shaders to be recompiled. These options should be changed or set during initialization.
* `filterWidth` and `feedback`, however, could be changed an any time.
* @see setTemporalAntiAliasingOptions()
*/
public static class TemporalAntiAliasingOptions {
public enum BoxType {
/**
* use an AABB neighborhood
*/
AABB,
/**
* use the variance of the neighborhood
*/
VARIANCE,
/**
* use both AABB and variance
*/
AABB_VARIANCE,
}
public enum BoxClipping {
/**
* Accurate box clipping
*/
ACCURATE,
/**
* clamping
*/
CLAMP,
/**
* no rejections (use for debugging)
*/
NONE,
}
/**
* reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
* reconstruction filter width typically between 0.2 (sharper, aliased) and 1 (smoother)
*/
public float filterWidth = 1.0f;
/**
@@ -1891,6 +1924,36 @@ public class View {
* enables or disables temporal anti-aliasing
*/
public boolean enabled = false;
/**
* whether to filter the history buffer
*/
public boolean filterHistory = true;
/**
* whether to apply the reconstruction filter to the input
*/
public boolean filterInput = true;
/**
* whether to use the YcoCg color-space for history rejection
*/
public boolean useYCoCg = false;
/**
* type of color gamut box
*/
@NonNull
public TemporalAntiAliasingOptions.BoxType boxType = TemporalAntiAliasingOptions.BoxType.VARIANCE;
/**
* clipping algorithm
*/
@NonNull
public TemporalAntiAliasingOptions.BoxClipping boxClipping = TemporalAntiAliasingOptions.BoxClipping.ACCURATE;
/**
* adjust the feedback dynamically to reduce flickering
*/
public boolean preventFlickering = false;
/**
* whether to apply history reprojection (debug option)
*/
public boolean historyReprojection = true;
}
/**
File diff suppressed because one or more lines are too long
Binary file not shown.
+14 -1
View File
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3
View File
@@ -587,6 +587,7 @@
<li><em>overrides</em> Dictionary with one or more of the following properties: mapSize, shadowCascades, constantBias, normalBias, shadowFar, shadowNearHint, shadowFarHint, stable, polygonOffsetConstant, polygonOffsetSlope, \</li>
</ul>
</li>
<li><strong>engine.setStereoscopicOptions()</strong></li>
<li><strong>engine.setTemporalAntiAliasingOptions()</strong></li>
<li><strong>engine.setVignetteOptions()</strong></li>
</ul>
@@ -1462,6 +1463,8 @@ This defines the following functions:</p>
<li>STENCIL_ATTACHMENT</li>
<li>UPLOADABLE</li>
<li>SAMPLEABLE</li>
<li>BLIT_SRC</li>
<li>BLIT_DST</li>
<li>SUBPASS_INPUT</li>
</ul>
</div>
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -537,7 +537,7 @@ void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
if (UTILS_UNLIKELY(mContext.isES2())) {
// Here we patch the specification constants to enable or not the rec709 output
// Here we patch the specialization constants to enable or not the rec709 output
// color space emulation in this program. Obviously, the backend shouldn't know about
// specific spec-constants, so we need to handle failures gracefully. This cannot be
// done at Material creation time because only the backend has access to
@@ -547,7 +547,8 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
// add support for ARB_shading_language_packing if needed
auto const packingFunctions = process_ARB_shading_language_packing(context);
// split shader source, so we can insert the specification constants and the packing functions
// split shader source, so we can insert the specialization constants and the packing
// functions
auto const [prolog, body] = splitShaderSource(source);
const std::array<const char*, 4> sources = {
+25 -1
View File
@@ -424,12 +424,36 @@ struct MultiSampleAntiAliasingOptions {
/**
* Options for Temporal Anti-aliasing (TAA)
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
* shaders to be recompiled. These options should be changed or set during initialization.
* `filterWidth` and `feedback`, however, could be changed an any time.
* @see setTemporalAntiAliasingOptions()
*/
struct TemporalAntiAliasingOptions {
float filterWidth = 1.0f; //!< reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
float filterWidth = 1.0f; //!< reconstruction filter width typically between 0.2 (sharper, aliased) and 1 (smoother)
float feedback = 0.04f; //!< history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
bool enabled = false; //!< enables or disables temporal anti-aliasing
enum class BoxType : uint8_t {
AABB, //!< use an AABB neighborhood
VARIANCE, //!< use the variance of the neighborhood
AABB_VARIANCE //!< use both AABB and variance
};
enum class BoxClipping : uint8_t {
ACCURATE, //!< Accurate box clipping
CLAMP, //!< clamping
NONE //!< no rejections (use for debugging)
};
bool filterHistory = true; //!< whether to filter the history buffer
bool filterInput = true; //!< whether to apply the reconstruction filter to the input
bool useYCoCg = false; //!< whether to use the YcoCg color-space for history rejection
BoxType boxType = BoxType::VARIANCE; //!< type of color gamut box
BoxClipping boxClipping = BoxClipping::ACCURATE; //!< clipping algorithm
bool preventFlickering = false; //!< adjust the feedback dynamically to reduce flickering
bool historyReprojection = true; //!< whether to apply history reprojection (debug option)
};
/**
+35 -2
View File
@@ -46,6 +46,8 @@
#include <math/half.h>
#include <math/mat2.h>
#include <utils/BitmaskEnum.h>
#include <algorithm>
#include <limits>
@@ -200,7 +202,8 @@ void PostProcessManager::registerPostProcessMaterial(std::string_view name, Mate
}
UTILS_NOINLINE
PostProcessManager::PostProcessMaterial& PostProcessManager::getPostProcessMaterial(std::string_view name) noexcept {
PostProcessManager::PostProcessMaterial& PostProcessManager::getPostProcessMaterial(
std::string_view name) noexcept {
assert_invariant(mMaterialRegistry.find(name) != mMaterialRegistry.end());
return mMaterialRegistry[name];
}
@@ -2528,6 +2531,36 @@ void PostProcessManager::prepareTaa(FrameGraph& fg, filament::Viewport const& sv
});
}
void PostProcessManager::configureTemporalAntiAliasingMaterial(
TemporalAntiAliasingOptions const& taaOptions) noexcept {
FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine);
bool dirty = false;
auto setConstantParameter =
[&dirty](FMaterial* const ma, std::string_view name, auto value) noexcept {
auto id = ma->getSpecializationConstantId(name);
if (id.has_value()) {
if (ma->setConstant(id.value(), value)) {
dirty = true;
}
}
};
setConstantParameter(ma, "historyReprojection", taaOptions.historyReprojection);
setConstantParameter(ma, "filterHistory", taaOptions.filterHistory);
setConstantParameter(ma, "filterInput", taaOptions.filterInput);
setConstantParameter(ma, "useYCoCg", taaOptions.useYCoCg);
setConstantParameter(ma, "preventFlickering", taaOptions.preventFlickering);
setConstantParameter(ma, "boxType", (int32_t)taaOptions.boxType);
setConstantParameter(ma, "boxClipping", (int32_t)taaOptions.boxClipping);
if (dirty) {
ma->invalidate();
// TODO: call Material::compile(), we can't si that now because it works only
// with surface materials
}
}
FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
FrameGraphId<FrameGraphTexture> depth,
@@ -2616,8 +2649,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
auto color = resources.getTexture(data.color);
auto depth = resources.getTexture(data.depth);
auto history = resources.getTexture(data.history);
auto const& material = getPostProcessMaterial("taa");
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", color, {}); // nearest
mi->setParameter("depth", depth, {}); // nearest
+4
View File
@@ -86,6 +86,10 @@ public:
void init() noexcept;
void terminate(backend::DriverApi& driver) noexcept;
void configureTemporalAntiAliasingMaterial(
TemporalAntiAliasingOptions const& taaOptions) noexcept;
// methods below are ordered relative to their position in the pipeline (as much as possible)
// structure (depth) pass
+12 -2
View File
@@ -373,8 +373,13 @@ void FEngine::init() {
mDebugRegistry.registerProperty("d.shadowmap.debug_directional_shadowmap",
&debug.shadowmap.debug_directional_shadowmap, [this]() {
mMaterials.forEach([](FMaterial* material) {
mMaterials.forEach([this](FMaterial* material) {
if (material->getMaterialDomain() == MaterialDomain::SURFACE) {
material->setConstant(
+ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP,
debug.shadowmap.debug_directional_shadowmap);
material->invalidate(
Variant::DIR | Variant::SRE | Variant::DEP,
Variant::DIR | Variant::SRE);
@@ -384,8 +389,13 @@ void FEngine::init() {
mDebugRegistry.registerProperty("d.lighting.debug_froxel_visualization",
&debug.lighting.debug_froxel_visualization, [this]() {
mMaterials.forEach([](FMaterial* material) {
mMaterials.forEach([this](FMaterial* material) {
if (material->getMaterialDomain() == MaterialDomain::SURFACE) {
material->setConstant(
+ReservedSpecializationConstants::CONFIG_DEBUG_FROXEL_VISUALIZATION,
debug.lighting.debug_froxel_visualization);
material->invalidate(
Variant::DYN | Variant::DEP,
Variant::DYN);
+74 -27
View File
@@ -79,7 +79,10 @@ struct Material::BuilderDetails {
size_t mSize = 0;
MaterialParser* mMaterialParser = nullptr;
bool mDefaultMaterial = false;
std::unordered_map<std::string, std::variant<int32_t, float, bool>> mConstantSpecializations;
std::unordered_map<
utils::CString,
std::variant<int32_t, float, bool>,
CString::Hasher> mConstantSpecializations;
};
FMaterial::DefaultMaterialBuilder::DefaultMaterialBuilder() : Material::Builder() {
@@ -216,9 +219,14 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
mSubpassInfo.isValid = false;
}
utils::FixedCapacityVector<MaterialConstant> constants;
// Older materials won't have a constants chunk, but that's okay.
parser->getConstants(&constants);
parser->getConstants(&mMaterialConstants);
for (size_t i = 0, c = mMaterialConstants.size(); i < c; i++) {
auto& item = mMaterialConstants[i];
// the key can be a string_view because mMaterialConstant owns the CString
std::string_view const key{ item.name.data(), item.name.size() };
mSpecializationConstantsNameToIndex[key] = i;
}
// Verify that all the constant specializations exist in the material and that their types match.
// The first specialization constants are defined internally by Filament.
@@ -238,7 +246,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
bool const powerVrShaderWorkarounds =
engine.getDriverApi().isWorkaroundNeeded(Workaround::POWER_VR_SHADER_WORKAROUNDS);
mSpecializationConstants.reserve(constants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
mSpecializationConstants.reserve(mMaterialConstants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::BACKEND_FEATURE_LEVEL,
(int)engine.getSupportedFeatureLevel() });
@@ -250,16 +258,16 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
(int)maxFroxelBufferHeight });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP,
(bool)engine.debug.shadowmap.debug_directional_shadowmap });
engine.debug.shadowmap.debug_directional_shadowmap });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_DEBUG_FROXEL_VISUALIZATION,
(bool)engine.debug.lighting.debug_froxel_visualization });
engine.debug.lighting.debug_froxel_visualization });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_STATIC_TEXTURE_TARGET_WORKAROUND,
(bool)staticTextureWorkaround });
staticTextureWorkaround });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_POWER_VR_SHADER_WORKAROUNDS,
(bool)powerVrShaderWorkarounds });
powerVrShaderWorkarounds });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT,
(int)engine.getConfig().stereoscopicEyeCount });
@@ -270,19 +278,18 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
false});
}
for (const auto& [name, value] : builder->mConstantSpecializations) {
auto found = std::find_if(
constants.begin(), constants.end(), [name = name](const auto& constant) {
return strncmp(constant.name.data(), name.data(), name.length()) == 0;
});
ASSERT_PRECONDITION(found != constants.end(),
for (auto const& [name, value] : builder->mConstantSpecializations) {
std::string_view const key{ name.data(), name.size() };
auto pos = mSpecializationConstantsNameToIndex.find(key);
ASSERT_PRECONDITION(pos != mSpecializationConstantsNameToIndex.end(),
"The material %s does not have a constant parameter named %s.",
mName.c_str_safe(), name.c_str());
const char* const types[3] = {"an int", "a float", "a bool"};
const char* const errorMessage =
"The constant parameter %s on material %s is of type %s, but %s was "
"provided.";
switch (found->type) {
auto& constant = mMaterialConstants[pos->second];
switch (constant.type) {
case ConstantType::INT:
ASSERT_PRECONDITION(std::holds_alternative<int32_t>(value), errorMessage,
name.c_str(), mName.c_str_safe(), "int", types[value.index()]);
@@ -296,8 +303,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder)
name.c_str(), mName.c_str_safe(), "bool", types[value.index()]);
break;
}
uint32_t const index =
std::distance(constants.begin(), found) + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
uint32_t const index = pos->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
mSpecializationConstants.push_back({ index, value });
}
@@ -462,16 +468,6 @@ FMaterial::~FMaterial() noexcept {
}
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?
for (auto& item : mSpecializationConstants) {
if (item.id == ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP) {
item.value = mEngine.debug.shadowmap.debug_directional_shadowmap;
} else if (item.id == ReservedSpecializationConstants::CONFIG_DEBUG_FROXEL_VISUALIZATION) {
item.value = mEngine.debug.lighting.debug_froxel_visualization;
}
}
if (mMaterialDomain == MaterialDomain::SURFACE) {
DriverApi& driverApi = mEngine.getDriverApi();
auto& cachedPrograms = mCachedPrograms;
@@ -841,4 +837,55 @@ void FMaterial::destroyPrograms(FEngine& engine) {
}
}
std::optional<uint32_t> FMaterial::getSpecializationConstantId(std::string_view name) const noexcept {
auto pos = mSpecializationConstantsNameToIndex.find(name);
if (pos != mSpecializationConstantsNameToIndex.end()) {
return pos->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
}
return std::nullopt;
}
template<typename T, typename>
bool FMaterial::setConstant(uint32_t id, T value) noexcept {
size_t const maxId = mMaterialConstants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
if (UTILS_LIKELY(id < maxId)) {
if (id >= CONFIG_MAX_RESERVED_SPEC_CONSTANTS) {
// Constant from the material itself (as opposed to the reserved ones)
auto& constant = mMaterialConstants[id - CONFIG_MAX_RESERVED_SPEC_CONSTANTS];
using ConstantType = backend::ConstantType;
switch (constant.type) {
case ConstantType::INT:
if (!std::is_same_v<T, int32_t>) return false;
break;
case ConstantType::FLOAT:
if (!std::is_same_v<T, float>) return false;
break;
case ConstantType::BOOL:
if (!std::is_same_v<T, bool>) return false;
break;
}
}
auto pos = std::find_if(
mSpecializationConstants.begin(), mSpecializationConstants.end(),
[id](backend::Program::SpecializationConstant const& specializationConstant) {
return specializationConstant.id == id;
});
if (pos != mSpecializationConstants.end()) {
if (std::get<T>(pos->value) != value) {
pos->value = value;
return true;
}
} else {
mSpecializationConstants.push_back({ id, value });
return true;
}
}
return false;
}
template bool FMaterial::setConstant<int32_t>(uint32_t id, int32_t value) noexcept;
template bool FMaterial::setConstant<float>(uint32_t id, float value) noexcept;
template bool FMaterial::setConstant<bool>(uint32_t id, bool value) noexcept;
} // namespace filament
+13
View File
@@ -165,6 +165,14 @@ public:
void destroyPrograms(FEngine& engine);
// return the id of a specialization constant specified by name for this material
std::optional<uint32_t> getSpecializationConstantId(std::string_view name) const noexcept ;
// Sets a specialization constant by id. call is no-op if the id is invalid.
// Return true is the value was changed.
template<typename T, typename = Builder::is_supported_constant_parameter_t<T>>
bool setConstant(uint32_t id, T value) noexcept;
#if FILAMENT_ENABLE_MATDBG
void applyPendingEdits() noexcept;
@@ -261,6 +269,11 @@ private:
SamplerGroupBindingInfoList mSamplerGroupBindingInfoList;
SamplerBindingToNameMap mSamplerBindingToNameMap;
// Constants defined by this Material
utils::FixedCapacityVector<MaterialConstant> mMaterialConstants;
// A map from the Constant name to the mMaterialConstant index
std::unordered_map<std::string_view, uint32_t> mSpecializationConstantsNameToIndex;
// current specialization constants for the HwProgram
utils::FixedCapacityVector<backend::Program::SpecializationConstant> mSpecializationConstants;
#if FILAMENT_ENABLE_MATDBG
+5
View File
@@ -491,6 +491,11 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
hasDithering = false;
hasFXAA = false;
scale = 1.0f;
} else {
// This configures post-process materials by setting constant parameters
if (taaOptions.enabled) {
ppm.configureTemporalAntiAliasingMaterial(taaOptions);
}
}
const bool blendModeTranslucent = view.getBlendMode() == BlendMode::TRANSLUCENT;
+115 -93
View File
@@ -28,6 +28,43 @@ material {
name : filterWeights
}
],
constants : [
{
type : bool,
name : historyReprojection,
default : true
},
{
type : bool,
name : filterHistory,
default : true
},
{
type : bool,
name : filterInput,
default : true
},
{
type : int,
name : boxClipping,
default : 0
},
{
type : int,
name : boxType,
default : 1
},
{
type : bool,
name : useYCoCg,
default : false
},
{
type : bool,
name : preventFlickering,
default : false
}
],
variables : [
vertex
],
@@ -73,34 +110,12 @@ fragment {
// no clipping (for debugging only)
#define BOX_CLIPPING_NONE 2
/* Configuration mobile/desktop */
#if FILAMENT_QUALITY < FILAMENT_QUALITY_HIGH
#define BOX_CLIPPING BOX_CLIPPING_ACCURATE
#define BOX_TYPE BOX_TYPE_VARIANCE
#define USE_YCoCg 0
#define FILTER_INPUT 1
#define FILTER_HISTORY 0
#else
#define BOX_CLIPPING BOX_CLIPPING_ACCURATE
#define BOX_TYPE BOX_TYPE_AABB_VARIANCE
#define USE_YCoCg 1
#define FILTER_INPUT 1
#define FILTER_HISTORY 1
#endif
/* debugging helper */
#define HISTORY_REPROJECTION 1
#define PREVENT_FLICKERING 0 // FIXME: thin lines disapear
float luma(const vec3 color) {
#if USE_YCoCg
return color.x;
#else
return luminance(color);
#endif
if (materialConstants_useYCoCg) {
return color.x;
} else {
return luminance(color);
}
}
vec3 RGB_YCoCg(const vec3 c) {
@@ -203,26 +218,27 @@ void postProcess(inout PostProcessInputs postProcess) {
// read the depth buffer center sample for reprojection
float depth = textureLod(materialParams_depth, uv.xy, 0.0).r;
#if HISTORY_REPROJECTION
uv.zw = uvToRenderTargetUV(uv.zw);
// reproject history to current frame
highp vec4 q = materialParams.reprojection * vec4(uv.zw, depth, 1.0);
uv.zw = (q.xy * (1.0 / q.w)) * 0.5 + 0.5;
uv.zw = uvToRenderTargetUV(uv.zw);
#endif
if (materialConstants_historyReprojection) {
uv.zw = uvToRenderTargetUV(uv.zw);
// reproject history to current frame
highp vec4 q = materialParams.reprojection * vec4(uv.zw, depth, 1.0);
uv.zw = (q.xy * (1.0 / q.w)) * 0.5 + 0.5;
uv.zw = uvToRenderTargetUV(uv.zw);
}
// read center color and history samples
vec4 color = textureLod(materialParams_color, uv.xy, 0.0);
#if FILTER_HISTORY
vec4 history = sampleTextureCatmullRom(materialParams_history, uv.zw,
vec2(textureSize(materialParams_history, 0)));
#else
vec4 history = textureLod(materialParams_history, uv.zw, 0.0);
#endif
vec4 history;
if (materialConstants_filterHistory) {
history = sampleTextureCatmullRom(materialParams_history, uv.zw,
vec2(textureSize(materialParams_history, 0)));
} else {
history = textureLod(materialParams_history, uv.zw, 0.0);
}
#if USE_YCoCg
history.rgb = RGB_YCoCg(history.rgb);
#endif
if (materialConstants_useYCoCg) {
history.rgb = RGB_YCoCg(history.rgb);
}
// build the history clamping box
vec3 s[9];
@@ -236,66 +252,72 @@ void postProcess(inout PostProcessInputs postProcess) {
s[7] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2( 0, TEXTURE_SPACE_UP)).rgb;
s[8] = textureLodOffset(materialParams_color, uv.xy, 0.0, ivec2( 1, TEXTURE_SPACE_UP)).rgb;
#if USE_YCoCg
for (int i = 0 ; i < 9 ; i++) {
s[i] = RGB_YCoCg(s[i]);
if (materialConstants_useYCoCg) {
for (int i = 0; i < 9; i++) {
s[i] = RGB_YCoCg(s[i]);
}
color.rgb = s[4].rgb;
}
color.rgb = s[4].rgb;
#endif
#if FILTER_INPUT
// unjitter/filter input
vec4 filtered = vec4(0, 0, 0, color.a);
for (int i = 0 ; i < 9 ; i++) {
filtered.rgb += s[i] * materialParams.filterWeights[i];
vec4 filtered;
if (materialConstants_filterInput) {
// unjitter/filter input
filtered = vec4(0, 0, 0, color.a);
for (int i = 0; i < 9; i++) {
filtered.rgb += s[i] * materialParams.filterWeights[i];
}
} else {
filtered = color;
}
#else
vec4 filtered = color;
#endif
#if BOX_TYPE == BOX_TYPE_AABB || BOX_TYPE == BOX_TYPE_AABB_VARIANCE
vec3 boxmin = min(s[4], min(min(s[1], s[3]), min(s[5], s[7])));
vec3 boxmax = max(s[4], max(max(s[1], s[3]), max(s[5], s[7])));
vec3 box9min = min(boxmin, min(min(s[0], s[2]), min(s[6], s[8])));
vec3 box9max = max(boxmax, max(max(s[0], s[2]), max(s[6], s[8])));
// round the corners of the 3x3 box
boxmin = (boxmin + box9min) * 0.5;
boxmax = (boxmax + box9max) * 0.5;
#endif
#if BOX_TYPE == BOX_TYPE_VARIANCE || BOX_TYPE == BOX_TYPE_AABB_VARIANCE
// "An Excursion in Temporal Supersampling" by Marco Salvi
vec3 m0 = s[4];
vec3 m1 = s[4] * s[4];
// we use only 5 samples instead of all 9
for (int i = 1 ; i < 9 ; i+=2) {
m0 += s[i];
m1 += s[i] * s[i];
vec3 boxmin;
vec3 boxmax;
if (materialConstants_boxType == BOX_TYPE_AABB ||
materialConstants_boxType == BOX_TYPE_AABB_VARIANCE) {
boxmin = min(s[4], min(min(s[1], s[3]), min(s[5], s[7])));
boxmax = max(s[4], max(max(s[1], s[3]), max(s[5], s[7])));
vec3 box9min = min(boxmin, min(min(s[0], s[2]), min(s[6], s[8])));
vec3 box9max = max(boxmax, max(max(s[0], s[2]), max(s[6], s[8])));
// round the corners of the 3x3 box
boxmin = (boxmin + box9min) * 0.5;
boxmax = (boxmax + box9max) * 0.5;
}
if (materialConstants_boxType == BOX_TYPE_VARIANCE ||
materialConstants_boxType == BOX_TYPE_AABB_VARIANCE) {
// "An Excursion in Temporal Supersampling" by Marco Salvi
vec3 m0 = s[4];
vec3 m1 = s[4] * s[4];
// we use only 5 samples instead of all 9
for (int i = 1; i < 9; i+=2) {
m0 += s[i];
m1 += s[i] * s[i];
}
vec3 a0 = m0 * (1.0 / 5.0);
vec3 a1 = m1 * (1.0 / 5.0);
vec3 sigma = sqrt(a1 - a0 * a0);
if (materialConstants_boxType == BOX_TYPE_VARIANCE) {
boxmin = a0 - VARIANCE_GAMMA * sigma;
boxmax = a0 + VARIANCE_GAMMA * sigma;
} else {
boxmin = min(boxmin, a0 - VARIANCE_GAMMA * sigma);
boxmax = max(boxmax, a0 + VARIANCE_GAMMA * sigma);
}
}
vec3 a0 = m0 * (1.0 / 5.0);
vec3 a1 = m1 * (1.0 / 5.0);
vec3 sigma = sqrt(a1 - a0 * a0);
#if BOX_TYPE == BOX_TYPE_VARIANCE
vec3 boxmin = a0 - VARIANCE_GAMMA * sigma;
vec3 boxmax = a0 + VARIANCE_GAMMA * sigma;
#else
boxmin = min(boxmin, a0 - VARIANCE_GAMMA * sigma);
boxmax = max(boxmax, a0 + VARIANCE_GAMMA * sigma);
#endif
#endif
// history clamping
history = clipToBox(BOX_CLIPPING, boxmin, boxmax, filtered, history);
history = clipToBox(materialConstants_boxClipping, boxmin, boxmax, filtered, history);
float lumaColor = luma(filtered.rgb);
float lumaHistory = luma(history.rgb);
float alpha = materialParams.alpha;
#if PREVENT_FLICKERING
// [Lottes] prevents flickering by modulating the blend weight by the difference in luma
float diff = 1.0 - abs(lumaColor - lumaHistory) / (0.001 + max(lumaColor, lumaHistory));
alpha *= diff * diff;
#endif
if (materialConstants_preventFlickering) {
// [Lottes] prevents flickering by modulating the blend weight by the difference in luma
float diff = 1.0 - abs(lumaColor - lumaHistory) / (0.001 + max(lumaColor, lumaHistory));
alpha *= diff * diff;
}
// tonemapping for handling HDR
filtered.rgb *= 1.0 / (1.0 + lumaColor);
@@ -307,9 +329,9 @@ void postProcess(inout PostProcessInputs postProcess) {
// untonemap result
result.rgb *= 1.0 / (1.0 - luma(result.rgb));
#if USE_YCoCg
result.rgb = YCoCg_RGB(result.rgb);
#endif
if (materialConstants_useYCoCg) {
result.rgb = YCoCg_RGB(result.rgb);
}
// store result (which will becomes new history)
// we could end-up with negative values due to the bicubic filter, which never recover on
@@ -26,7 +26,9 @@
namespace filament {
static constexpr size_t POST_PROCESS_VARIANT_COUNT = 2;
static constexpr size_t POST_PROCESS_VARIANT_BITS = 1;
static constexpr size_t POST_PROCESS_VARIANT_COUNT = (1u << POST_PROCESS_VARIANT_BITS);
static constexpr size_t POST_PROCESS_VARIANT_MASK = POST_PROCESS_VARIANT_COUNT - 1;
enum class PostProcessVariant : uint8_t {
OPAQUE,
TRANSLUCENT
@@ -135,11 +137,15 @@ template<>
struct utils::EnableIntegerOperators<filament::SamplerBindingPoints> : public std::true_type {};
template<>
struct utils::EnableIntegerOperators<filament::ReservedSpecializationConstants> : public std::true_type {};
template<>
struct utils::EnableIntegerOperators<filament::PostProcessVariant> : public std::true_type {};
template<>
inline constexpr size_t utils::Enum::count<filament::UniformBindingPoints>() { return 9; }
template<>
inline constexpr size_t utils::Enum::count<filament::SamplerBindingPoints>() { return 4; }
template<>
inline constexpr size_t utils::Enum::count<filament::PostProcessVariant>() { return filament::POST_PROCESS_VARIANT_COUNT; }
static_assert(utils::Enum::count<filament::UniformBindingPoints>() <= filament::backend::CONFIG_UNIFORM_BINDING_COUNT);
static_assert(utils::Enum::count<filament::SamplerBindingPoints>() <= filament::backend::CONFIG_SAMPLER_BINDING_COUNT);
+1 -1
View File
@@ -35,7 +35,7 @@ struct hashCStrings {
typedef size_t result_type;
result_type operator()(argument_type cstr) const noexcept {
size_t hash = 5381;
while (int c = *cstr++) {
while (int const c = *cstr++) {
hash = (hash * 33u) ^ size_t(c);
}
return hash;
+60 -1
View File
@@ -647,6 +647,44 @@ std::ostream& operator<<(std::ostream& out, const MultiSampleAntiAliasingOptions
<< "}";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::BoxType* out) {
if (0 == compare(tokens[i], jsonChunk, "AABB")) { *out = TemporalAntiAliasingOptions::BoxType::AABB; }
else if (0 == compare(tokens[i], jsonChunk, "VARIANCE")) { *out = TemporalAntiAliasingOptions::BoxType::VARIANCE; }
else if (0 == compare(tokens[i], jsonChunk, "AABB_VARIANCE")) { *out = TemporalAntiAliasingOptions::BoxType::AABB_VARIANCE; }
else {
slog.w << "Invalid TemporalAntiAliasingOptions::BoxType: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxType in) {
switch (in) {
case TemporalAntiAliasingOptions::BoxType::AABB: return out << "\"AABB\"";
case TemporalAntiAliasingOptions::BoxType::VARIANCE: return out << "\"VARIANCE\"";
case TemporalAntiAliasingOptions::BoxType::AABB_VARIANCE: return out << "\"AABB_VARIANCE\"";
}
return out << "\"INVALID\"";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::BoxClipping* out) {
if (0 == compare(tokens[i], jsonChunk, "ACCURATE")) { *out = TemporalAntiAliasingOptions::BoxClipping::ACCURATE; }
else if (0 == compare(tokens[i], jsonChunk, "CLAMP")) { *out = TemporalAntiAliasingOptions::BoxClipping::CLAMP; }
else if (0 == compare(tokens[i], jsonChunk, "NONE")) { *out = TemporalAntiAliasingOptions::BoxClipping::NONE; }
else {
slog.w << "Invalid TemporalAntiAliasingOptions::BoxClipping: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxClipping in) {
switch (in) {
case TemporalAntiAliasingOptions::BoxClipping::ACCURATE: return out << "\"ACCURATE\"";
case TemporalAntiAliasingOptions::BoxClipping::CLAMP: return out << "\"CLAMP\"";
case TemporalAntiAliasingOptions::BoxClipping::NONE: return out << "\"NONE\"";
}
return out << "\"INVALID\"";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
@@ -659,6 +697,20 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAli
i = parse(tokens, i + 1, jsonChunk, &out->feedback);
} else if (compare(tok, jsonChunk, "enabled") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
} else if (compare(tok, jsonChunk, "filterHistory") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->filterHistory);
} else if (compare(tok, jsonChunk, "filterInput") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->filterInput);
} else if (compare(tok, jsonChunk, "useYCoCg") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->useYCoCg);
} else if (compare(tok, jsonChunk, "boxType") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->boxType);
} else if (compare(tok, jsonChunk, "boxClipping") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->boxClipping);
} else if (compare(tok, jsonChunk, "preventFlickering") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->preventFlickering);
} else if (compare(tok, jsonChunk, "historyReprojection") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->historyReprojection);
} else {
slog.w << "Invalid TemporalAntiAliasingOptions key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
@@ -675,7 +727,14 @@ std::ostream& operator<<(std::ostream& out, const TemporalAntiAliasingOptions& i
return out << "{\n"
<< "\"filterWidth\": " << (in.filterWidth) << ",\n"
<< "\"feedback\": " << (in.feedback) << ",\n"
<< "\"enabled\": " << to_string(in.enabled) << "\n"
<< "\"enabled\": " << to_string(in.enabled) << ",\n"
<< "\"filterHistory\": " << to_string(in.filterHistory) << ",\n"
<< "\"filterInput\": " << to_string(in.filterInput) << ",\n"
<< "\"useYCoCg\": " << to_string(in.useYCoCg) << ",\n"
<< "\"boxType\": " << (in.boxType) << ",\n"
<< "\"boxClipping\": " << (in.boxClipping) << ",\n"
<< "\"preventFlickering\": " << to_string(in.preventFlickering) << ",\n"
<< "\"historyReprojection\": " << to_string(in.historyReprojection) << "\n"
<< "}";
}
+6
View File
@@ -69,6 +69,12 @@ std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, MultiSampleAntiAliasingOptions* out);
std::ostream& operator<<(std::ostream& out, const MultiSampleAntiAliasingOptions& in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::BoxType* out);
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxType in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions::BoxClipping* out);
std::ostream& operator<<(std::ostream& out, TemporalAntiAliasingOptions::BoxClipping in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, TemporalAntiAliasingOptions* out);
std::ostream& operator<<(std::ostream& out, const TemporalAntiAliasingOptions& in);
+16
View File
@@ -792,6 +792,22 @@ void ViewerGui::updateUserInterface() {
ImGui::Checkbox("Lens Flare", &mSettings.view.bloom.lensFlare);
}
if (ImGui::CollapsingHeader("TAA Options")) {
ImGui::Checkbox("History Reprojection", &mSettings.view.taa.historyReprojection);
ImGui::SliderFloat("Feedback", &mSettings.view.taa.feedback, 0.0f, 1.0f);
ImGui::Checkbox("Filter History", &mSettings.view.taa.filterHistory);
ImGui::Checkbox("Filter Input", &mSettings.view.taa.filterInput);
ImGui::SliderFloat("FilterWidth", &mSettings.view.taa.filterWidth, 0.0f, 1.0f);
ImGui::Checkbox("Use YCoCg", &mSettings.view.taa.useYCoCg);
ImGui::Checkbox("Prevent Flickering", &mSettings.view.taa.preventFlickering);
int boxClipping = (int)mSettings.view.taa.boxClipping;
int boxType = (int)mSettings.view.taa.boxType;
ImGui::Combo("Box Clipping", &boxClipping, "Accurate\0Clamp\0None\0\0");
ImGui::Combo("Box Type", &boxType, "AABB\0Variance\0Both\0\0");
mSettings.view.taa.boxClipping = (TemporalAntiAliasingOptions::BoxClipping)boxClipping;
mSettings.view.taa.boxType = (TemporalAntiAliasingOptions::BoxType)boxType;
}
if (ImGui::CollapsingHeader("SSAO Options")) {
auto& ssao = mSettings.view.ssao;
+7
View File
@@ -140,6 +140,13 @@ Filament.loadGeneratedExtensions = function() {
filterWidth: 1.0,
feedback: 0.04,
enabled: false,
filterHistory: true,
filterInput: true,
useYCoCg: false,
boxType: Filament.View$TemporalAntiAliasingOptions$BoxType.VARIANCE,
boxClipping: Filament.View$TemporalAntiAliasingOptions$BoxClipping.ACCURATE,
preventFlickering: false,
historyReprojection: true,
};
return Object.assign(options, overrides);
};
+44 -1
View File
@@ -1636,13 +1636,28 @@ export interface View$MultiSampleAntiAliasingOptions {
customResolve?: boolean;
}
export enum View$TemporalAntiAliasingOptions$BoxType {
AABB, // use an AABB neighborhood
VARIANCE, // use the variance of the neighborhood
AABB_VARIANCE, // use both AABB and variance
}
export enum View$TemporalAntiAliasingOptions$BoxClipping {
ACCURATE, // Accurate box clipping
CLAMP, // clamping
NONE, // no rejections (use for debugging)
}
/**
* Options for Temporal Anti-aliasing (TAA)
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
* shaders to be recompiled. These options should be changed or set during initialization.
* `filterWidth` and `feedback`, however, could be changed an any time.
* @see setTemporalAntiAliasingOptions()
*/
export interface View$TemporalAntiAliasingOptions {
/**
* reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
* reconstruction filter width typically between 0.2 (sharper, aliased) and 1 (smoother)
*/
filterWidth?: number;
/**
@@ -1653,6 +1668,34 @@ export interface View$TemporalAntiAliasingOptions {
* enables or disables temporal anti-aliasing
*/
enabled?: boolean;
/**
* whether to filter the history buffer
*/
filterHistory?: boolean;
/**
* whether to apply the reconstruction filter to the input
*/
filterInput?: boolean;
/**
* whether to use the YcoCg color-space for history rejection
*/
useYCoCg?: boolean;
/**
* type of color gamut box
*/
boxType?: View$TemporalAntiAliasingOptions$BoxType;
/**
* clipping algorithm
*/
boxClipping?: View$TemporalAntiAliasingOptions$BoxClipping;
/**
* adjust the feedback dynamically to reduce flickering
*/
preventFlickering?: boolean;
/**
* whether to apply history reprojection (debug option)
*/
historyReprojection?: boolean;
}
/**
+7
View File
@@ -120,6 +120,13 @@ value_object<View::TemporalAntiAliasingOptions>("View$TemporalAntiAliasingOption
.field("filterWidth", &View::TemporalAntiAliasingOptions::filterWidth)
.field("feedback", &View::TemporalAntiAliasingOptions::feedback)
.field("enabled", &View::TemporalAntiAliasingOptions::enabled)
.field("filterHistory", &View::TemporalAntiAliasingOptions::filterHistory)
.field("filterInput", &View::TemporalAntiAliasingOptions::filterInput)
.field("useYCoCg", &View::TemporalAntiAliasingOptions::useYCoCg)
.field("boxType", &View::TemporalAntiAliasingOptions::boxType)
.field("boxClipping", &View::TemporalAntiAliasingOptions::boxClipping)
.field("preventFlickering", &View::TemporalAntiAliasingOptions::preventFlickering)
.field("historyReprojection", &View::TemporalAntiAliasingOptions::historyReprojection)
;
value_object<View::ScreenSpaceReflectionsOptions>("View$ScreenSpaceReflectionsOptions")
+12
View File
@@ -33,6 +33,18 @@ enum_<View::DepthOfFieldOptions::Filter>("View$DepthOfFieldOptions$Filter")
.value("MEDIAN", View::DepthOfFieldOptions::Filter::MEDIAN)
;
enum_<View::TemporalAntiAliasingOptions::BoxType>("View$TemporalAntiAliasingOptions$BoxType")
.value("AABB", View::TemporalAntiAliasingOptions::BoxType::AABB)
.value("VARIANCE", View::TemporalAntiAliasingOptions::BoxType::VARIANCE)
.value("AABB_VARIANCE", View::TemporalAntiAliasingOptions::BoxType::AABB_VARIANCE)
;
enum_<View::TemporalAntiAliasingOptions::BoxClipping>("View$TemporalAntiAliasingOptions$BoxClipping")
.value("ACCURATE", View::TemporalAntiAliasingOptions::BoxClipping::ACCURATE)
.value("CLAMP", View::TemporalAntiAliasingOptions::BoxClipping::CLAMP)
.value("NONE", View::TemporalAntiAliasingOptions::BoxClipping::NONE)
;
enum_<View::AntiAliasing>("View$AntiAliasing")
.value("NONE", View::AntiAliasing::NONE)
.value("FXAA", View::AntiAliasing::FXAA)