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)