diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 98c49ea094..dd02dcf8e2 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -13,6 +13,7 @@ A new header is inserted each time a *tag* is created.
- WebGL: add missing `ColorGrading` JS bindings
- engine: improvements/cleanup of Shadow mapping code [⚠️ **Recompile Materials**]
- engine: fix potential memory corruption with larger scenes
+- engine: VSM's MSAA setting is now per-view instead of per-light [⚠️ **API Change**]
## v1.28.3
diff --git a/android/filament-android/src/main/cpp/LightManager.cpp b/android/filament-android/src/main/cpp/LightManager.cpp
index dc81f64395..c81ab547e3 100644
--- a/android/filament-android/src/main/cpp/LightManager.cpp
+++ b/android/filament-android/src/main/cpp/LightManager.cpp
@@ -79,7 +79,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
jfloat shadowFarHint, jboolean stable, jboolean lispsm,
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
jboolean screenSpaceContactShadows, jint stepCount,
- jfloat maxShadowDistance, jint vsmMsaaSamples, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
+ jfloat maxShadowDistance, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
LightManager::ShadowOptions shadowOptions {
.mapSize = (uint32_t)mapSize,
@@ -97,7 +97,6 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
.stepCount = uint8_t(stepCount),
.maxShadowDistance = maxShadowDistance,
.vsm = {
- .msaaSamples = (uint8_t) vsmMsaaSamples,
.elvsm = (bool)elvsm,
.blurWidth = blurWidth
},
diff --git a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java
index b6f9a1989e..83c4aad3ee 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java
@@ -349,20 +349,6 @@ public class LightManager {
* @see View#setShadowType
*/
- /**
- * The number of MSAA samples to use when rendering VSM shadow maps.
- * Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
- * off MSAA.
- * Higher values may not be available depending on the underlying hardware.
- *
- *
- * Warning: This API is still experimental and subject to change.
- *
- */
- @IntRange(from = 1)
- public int vsmMsaaSamples = 1;
-
-
/**
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
* an improvement to the default EVSM which suffers important light leaks. Enabling
@@ -519,7 +505,7 @@ public class LightManager {
options.shadowFarHint, options.stable, options.lispsm,
options.polygonOffsetConstant, options.polygonOffsetSlope,
options.screenSpaceContactShadows,
- options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
+ options.stepCount, options.maxShadowDistance,
options.elvsm, options.blurWidth, options.shadowBulbRadius);
return this;
}
@@ -1185,7 +1171,7 @@ public class LightManager {
boolean stable, boolean lispsm,
float polygonOffsetConstant, float polygonOffsetSlope,
boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance,
- int vsmMsaaSamples, boolean elvsm, float blurWidth, float shadowBulbRadius);
+ boolean elvsm, float blurWidth, float shadowBulbRadius);
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
diff --git a/android/filament-android/src/main/java/com/google/android/filament/View.java b/android/filament-android/src/main/java/com/google/android/filament/View.java
index 4533488777..4cbdea625f 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/View.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/View.java
@@ -1817,6 +1817,13 @@ public class View {
* Whether to generate mipmaps for all VSM shadow maps.
*/
public boolean mipmapping = false;
+ /**
+ * The number of MSAA samples to use when rendering VSM shadow maps.
+ * Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
+ * off MSAA.
+ * Higher values may not be available depending on the underlying hardware.
+ */
+ public int msaaSamples = 1;
/**
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
* precision is rarely needed, but it does reduces light leaks as well as "fading"
diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h
index dfe20fb3fa..b764fb6bb2 100644
--- a/filament/include/filament/LightManager.h
+++ b/filament/include/filament/LightManager.h
@@ -340,14 +340,6 @@ public:
* @see View::setShadowType
*/
struct Vsm {
- /**
- * The number of MSAA samples to use when rendering VSM shadow maps.
- * Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
- * off MSAA.
- * Higher values may not be available depending on the underlying hardware.
- */
- uint8_t msaaSamples = 1;
-
/**
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
* an improvement to the default EVSM which suffers important light leaks. Enabling
diff --git a/filament/include/filament/Options.h b/filament/include/filament/Options.h
index c8a938101f..224f4dfb6b 100644
--- a/filament/include/filament/Options.h
+++ b/filament/include/filament/Options.h
@@ -390,6 +390,14 @@ struct VsmShadowOptions {
*/
bool mipmapping = false;
+ /**
+ * The number of MSAA samples to use when rendering VSM shadow maps.
+ * Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
+ * off MSAA.
+ * Higher values may not be available depending on the underlying hardware.
+ */
+ uint8_t msaaSamples = 1;
+
/**
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
* precision is rarely needed, but it does reduces light leaks as well as "fading"
diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp
index efa5106e9a..5cd6cb4ff1 100644
--- a/filament/src/ShadowMapManager.cpp
+++ b/filament/src/ShadowMapManager.cpp
@@ -159,6 +159,8 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG
utils::FixedCapacityVector passList;
};
+ VsmShadowOptions const& vsmShadowOptions = view.getVsmShadowOptions();
+
auto& prepareShadowPass = fg.addPass("Prepare Shadow Pass",
[&](FrameGraph::Builder& builder, auto& data) {
data.passList.reserve(CONFIG_MAX_SHADOWMAPS);
@@ -203,7 +205,8 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG
// "read" from one of its resource (only writes), so the FrameGraph culls it.
builder.sideEffect();
},
- [this, &engine, &view, scene, mainCameraInfo, userTime, passTemplate = pass](
+ [this, &engine, &view, vsmShadowOptions,
+ scene, mainCameraInfo, userTime, passTemplate = pass](
FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
// Note: we could almost parallel_for the loop below, the problem currently is
@@ -251,7 +254,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG
ShadowMap::prepareViewport(transaction, shadowMap.getViewport());
ShadowMap::prepareTime(transaction, engine, userTime);
ShadowMap::prepareShadowMapping(transaction,
- view.getVsmShadowOptions().highPrecision);
+ vsmShadowOptions.highPrecision);
shadowMap.commit(transaction, driver);
// updatePrimitivesLod must be run before RenderPass::appendCommands.
@@ -305,6 +308,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG
const uint8_t layer = entry.shadowMap->getLayer();
const auto* options = entry.shadowMap->getShadowOptions();
+ const auto msaaSamples = textureRequirements.msaaSamples;
auto& shadowPass = fg.addPass("Shadow Pass",
[&](FrameGraph::Builder& builder, auto& data) {
@@ -323,7 +327,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG
// We specify the sample count here because we don't need automatic resolve.
auto depth = builder.createTexture("Temporary VSM Depth Texture", {
.width = textureRequirements.size, .height = textureRequirements.size,
- .samples = options->vsm.msaaSamples,
+ .samples = msaaSamples,
.format = TextureFormat::DEPTH16,
});
@@ -346,7 +350,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH;
// we need to clear the shadow map with the max EVSM moments
renderTargetDesc.clearColor = vsmClearColor;
- renderTargetDesc.samples = options->vsm.msaaSamples;
+ renderTargetDesc.samples = msaaSamples;
if (blur) {
data.tempBlurSrc = builder.write(data.tempBlurSrc,
@@ -357,7 +361,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG
.color = { data.tempBlurSrc },
.depth = depth },
.clearColor = vsmClearColor,
- .samples = options->vsm.msaaSamples,
+ .samples = msaaSamples,
.clearFlags = TargetBufferFlags::COLOR
| TargetBufferFlags::DEPTH
});
@@ -837,6 +841,8 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
const bool useMipmapping = view.hasVSM() &&
((vsmShadowOptions.anisotropy > 0) || vsmShadowOptions.mipmapping);
+ const uint8_t msaaSamples = vsmShadowOptions.msaaSamples;
+
TextureFormat format = TextureFormat::DEPTH16;
if (view.hasVSM()) {
if (vsmShadowOptions.highPrecision) {
@@ -868,6 +874,7 @@ void ShadowMapManager::calculateTextureRequirements(FEngine& engine, FView& view
(uint16_t)maxDimension,
layersNeeded,
mipLevels,
+ msaaSamples,
format
};
}
diff --git a/filament/src/ShadowMapManager.h b/filament/src/ShadowMapManager.h
index b35780ebe5..6a49e5debe 100644
--- a/filament/src/ShadowMapManager.h
+++ b/filament/src/ShadowMapManager.h
@@ -190,6 +190,7 @@ private:
uint16_t size = 0;
uint8_t layers = 0;
uint8_t levels = 0;
+ uint8_t msaaSamples = 1;
backend::TextureFormat format = backend::TextureFormat::DEPTH16;
} mTextureAtlasRequirements;
diff --git a/filament/src/components/LightManager.cpp b/filament/src/components/LightManager.cpp
index 239774f2ac..8e084554b1 100644
--- a/filament/src/components/LightManager.cpp
+++ b/filament/src/components/LightManager.cpp
@@ -240,7 +240,6 @@ void FLightManager::setShadowOptions(Instance i, ShadowOptions const& options) n
params.options.shadowFar = std::max(options.shadowFar, 0.0f);
params.options.shadowNearHint = std::max(options.shadowNearHint, 0.0f);
params.options.shadowFarHint = std::max(options.shadowFarHint, 0.0f);
- params.options.vsm.msaaSamples = std::max(uint8_t(0), options.vsm.msaaSamples);
params.options.vsm.blurWidth = std::max(0.0f, options.vsm.blurWidth);
}
diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp
index 6285e19194..9024934c09 100644
--- a/filament/src/details/View.cpp
+++ b/filament/src/details/View.cpp
@@ -994,6 +994,10 @@ void FView::setAmbientOcclusionOptions(AmbientOcclusionOptions options) noexcept
options.ssct.rayCount = math::clamp((unsigned)options.ssct.rayCount, 1u, 255u);
mAmbientOcclusionOptions = options;
}
+void FView::setVsmShadowOptions(VsmShadowOptions options) noexcept {
+ options.msaaSamples = std::max(uint8_t(0), options.msaaSamples);
+ mVsmShadowOptions = options;
+}
void FView::setSoftShadowOptions(SoftShadowOptions options) noexcept {
options.penumbraScale = std::max(0.0f, options.penumbraScale);
diff --git a/filament/src/details/View.h b/filament/src/details/View.h
index 4a696c2bd2..adfaba74ad 100644
--- a/filament/src/details/View.h
+++ b/filament/src/details/View.h
@@ -305,9 +305,7 @@ public:
mShadowType = shadow;
}
- void setVsmShadowOptions(VsmShadowOptions const& options) noexcept {
- mVsmShadowOptions = options;
- }
+ void setVsmShadowOptions(VsmShadowOptions options) noexcept;
VsmShadowOptions getVsmShadowOptions() const noexcept {
return mVsmShadowOptions;
diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp
index 1b70fb03ac..51809f1b17 100644
--- a/libs/viewer/src/Settings.cpp
+++ b/libs/viewer/src/Settings.cpp
@@ -320,9 +320,7 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
- if (compare(tok, jsonChunk, "msaaSamples") == 0) {
- i = parse(tokens, i + 1, jsonChunk, &out->msaaSamples);
- } else if (compare(tok, jsonChunk, "elvsm") == 0) {
+ if (compare(tok, jsonChunk, "elvsm") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->elvsm);
} else if (compare(tok, jsonChunk, "blurWidth") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->blurWidth);
@@ -678,7 +676,6 @@ static std::ostream& operator<<(std::ostream& out, const LightManager::ShadowOpt
math::float3 splitsVector = { splits[0], splits[1], splits[2] };
return out << "{\n"
<< "\"vsm\": {\n"
- << "\"msaaSamples\": " << int(in.vsm.msaaSamples) << ",\n"
<< "\"elvsm\": " << to_string(in.vsm.elvsm) << ",\n"
<< "\"blurWidth\": " << in.vsm.blurWidth << "\n"
<< "},\n"
diff --git a/libs/viewer/src/Settings_generated.cpp b/libs/viewer/src/Settings_generated.cpp
index 0b35caa5e0..cac1d5f2da 100644
--- a/libs/viewer/src/Settings_generated.cpp
+++ b/libs/viewer/src/Settings_generated.cpp
@@ -800,6 +800,8 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, VsmShadowOption
i = parse(tokens, i + 1, jsonChunk, &out->anisotropy);
} else if (compare(tok, jsonChunk, "mipmapping") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->mipmapping);
+ } else if (compare(tok, jsonChunk, "msaaSamples") == 0) {
+ i = parse(tokens, i + 1, jsonChunk, &out->msaaSamples);
} else if (compare(tok, jsonChunk, "highPrecision") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->highPrecision);
} else if (compare(tok, jsonChunk, "minVarianceScale") == 0) {
@@ -822,6 +824,7 @@ std::ostream& operator<<(std::ostream& out, const VsmShadowOptions& in) {
return out << "{\n"
<< "\"anisotropy\": " << int(in.anisotropy) << ",\n"
<< "\"mipmapping\": " << to_string(in.mipmapping) << ",\n"
+ << "\"msaaSamples\": " << int(in.msaaSamples) << ",\n"
<< "\"highPrecision\": " << to_string(in.highPrecision) << ",\n"
<< "\"minVarianceScale\": " << (in.minVarianceScale) << ",\n"
<< "\"lightBleedReduction\": " << (in.lightBleedReduction) << "\n"
diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp
index 0f8ea04574..ec41977f73 100644
--- a/libs/viewer/src/ViewerGui.cpp
+++ b/libs/viewer/src/ViewerGui.cpp
@@ -846,7 +846,7 @@ void ViewerGui::updateUserInterface() {
char label[32];
snprintf(label, 32, "%d", 1 << mVsmMsaaSamplesLog2);
ImGui::SliderInt("VSM MSAA samples", &mVsmMsaaSamplesLog2, 0, 3, label);
- light.shadowOptions.vsm.msaaSamples =
+ mSettings.view.vsmShadowOptions.msaaSamples =
static_cast(1u << mVsmMsaaSamplesLog2);
int vsmAnisotropy = mSettings.view.vsmShadowOptions.anisotropy;
diff --git a/web/filament-js/extensions_generated.js b/web/filament-js/extensions_generated.js
index c4e66306d6..85dfd61bfd 100644
--- a/web/filament-js/extensions_generated.js
+++ b/web/filament-js/extensions_generated.js
@@ -164,6 +164,7 @@ Filament.loadGeneratedExtensions = function() {
const options = {
anisotropy: 0,
mipmapping: false,
+ msaaSamples: 1,
highPrecision: false,
minVarianceScale: 0.5,
lightBleedReduction: 0.15,
diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts
index 16e2b5e37b..8f787f879e 100644
--- a/web/filament-js/filament.d.ts
+++ b/web/filament-js/filament.d.ts
@@ -1678,6 +1678,13 @@ export interface View$VsmShadowOptions {
* Whether to generate mipmaps for all VSM shadow maps.
*/
mipmapping?: boolean;
+ /**
+ * The number of MSAA samples to use when rendering VSM shadow maps.
+ * Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns
+ * off MSAA.
+ * Higher values may not be available depending on the underlying hardware.
+ */
+ msaaSamples?: number;
/**
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
* precision is rarely needed, but it does reduces light leaks as well as "fading"
diff --git a/web/filament-js/jsbindings_generated.cpp b/web/filament-js/jsbindings_generated.cpp
index 43b6506d2a..5d49d25fbe 100644
--- a/web/filament-js/jsbindings_generated.cpp
+++ b/web/filament-js/jsbindings_generated.cpp
@@ -135,6 +135,7 @@ value_object("View$GuardBandOptions")
value_object("View$VsmShadowOptions")
.field("anisotropy", &View::VsmShadowOptions::anisotropy)
.field("mipmapping", &View::VsmShadowOptions::mipmapping)
+ .field("msaaSamples", &View::VsmShadowOptions::msaaSamples)
.field("highPrecision", &View::VsmShadowOptions::highPrecision)
.field("minVarianceScale", &View::VsmShadowOptions::minVarianceScale)
.field("lightBleedReduction", &View::VsmShadowOptions::lightBleedReduction)