diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e2a1703ff5..f8120f3185 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,7 @@ A new header is inserted each time a *tag* is created. - Metal: Shaders now use `half` floating-point arithmetic when possible for improved performance. [⚠️ **Recompile Materials**] - engine: add support for presentation time in `Renderer` +- engine: added guard bands support for screen-space effects ## v1.22.0 diff --git a/android/filament-android/src/main/cpp/View.cpp b/android/filament-android/src/main/cpp/View.cpp index c03ce40f56..a8bcc22f3d 100644 --- a/android/filament-android/src/main/cpp/View.cpp +++ b/android/filament-android/src/main/cpp/View.cpp @@ -461,3 +461,11 @@ Java_com_google_android_filament_View_nPick(JNIEnv* env, jclass, JniCallback::postToJavaAndDestroy(callback); }, callback->getHandler()); } + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_View_nSetGuardBandOptions(JNIEnv *, jclass, + jlong nativeView, jboolean enabled) { + View* view = (View*) nativeView; + view->setGuardBandOptions({ .enabled = (bool)enabled }); +} 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 ff078a479a..d210bc91b8 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 @@ -83,6 +83,7 @@ public class View { private MultiSampleAntiAliasingOptions mMultiSampleAntiAliasingOptions; private VsmShadowOptions mVsmShadowOptions; private SoftShadowOptions mSoftShadowOptions; + private GuardBandOptions mGuardBandOptions; /** * Generic quality level. @@ -357,6 +358,19 @@ public class View { public boolean enabled = false; }; + /** + * Options for the screen-space guard band. + * A guard band can be enabled to avoid some artifacts towards the edge of the screen when + * using screen-space effects such as SSAO. + * Enabling the guard band reduces performance slightly. + * Currently the guard band can only be enabled or disabled. + * + * @see View#setGuardBandOptions + */ + public static class GuardBandOptions { + /** enables or disables the guard band */ + public boolean enabled = false; + }; /** * Options for controlling the Bloom effect @@ -1208,17 +1222,6 @@ public class View { options.feedback, options.filterWidth, options.enabled); } - /** - * Enables or disable screen-space reflections. Disabled by default. - * - * @param options screen-space reflections options - */ - public void setScreenSpaceReflectionsOptions(@NonNull ScreenSpaceReflectionsOptions options) { - mScreenSpaceReflectionsOptions = options; - nSetScreenSpaceReflectionsOptions(getNativeObject(), options.thickness, options.bias, - options.maxDistance, options.stride, options.enabled); - } - /** * Returns temporal anti-aliasing options. * @@ -1232,6 +1235,17 @@ public class View { return mTemporalAntiAliasingOptions; } + /** + * Enables or disable screen-space reflections. Disabled by default. + * + * @param options screen-space reflections options + */ + public void setScreenSpaceReflectionsOptions(@NonNull ScreenSpaceReflectionsOptions options) { + mScreenSpaceReflectionsOptions = options; + nSetScreenSpaceReflectionsOptions(getNativeObject(), options.thickness, options.bias, + options.maxDistance, options.stride, options.enabled); + } + /** * Returns screen-space reflections options. * @@ -1245,6 +1259,30 @@ public class View { return mScreenSpaceReflectionsOptions; } + /** + * Enables or disable screen-space guard band. Disabled by default. + * + * @param options guard band options + */ + public void setGuardBandOptions(@NonNull GuardBandOptions options) { + mGuardBandOptions = options; + nSetGuardBandOptions(getNativeObject(), options.enabled); + } + + /** + * Returns screen-space guard band options. + * + * @return guard band options + */ + @NonNull + public GuardBandOptions getGuardBandOptions() { + if (mGuardBandOptions == null) { + mGuardBandOptions = new GuardBandOptions(); + } + return mGuardBandOptions; + } + + /** * Enables or disables tone-mapping in the post-processing stage. Enabled by default. * @@ -1833,6 +1871,7 @@ public class View { private static native void nSetMultiSampleAntiAliasingOptions(long nativeView, boolean enabled, int sampleCount, boolean customResolve); private static native boolean nIsShadowingEnabled(long nativeView); private static native void nSetScreenSpaceRefractionEnabled(long nativeView, boolean enabled); + private static native void nSetGuardBandOptions(long nativeView, boolean enabled); private static native boolean nIsScreenSpaceRefractionEnabled(long nativeView); private static native void nPick(long nativeView, int x, int y, Object handler, InternalOnPickCallback internalCallback); } diff --git a/filament/include/filament/Options.h b/filament/include/filament/Options.h index 6c750ae9dd..c0df378b21 100644 --- a/filament/include/filament/Options.h +++ b/filament/include/filament/Options.h @@ -328,6 +328,16 @@ struct ScreenSpaceReflectionsOptions { bool enabled = false; }; +/** + * Options for the screen-space guard band. + * A guard band can be enabled to avoid some artifacts towards the edge of the screen when + * using screen-space effects such as SSAO. Enabling the guard band reduces performance slightly. + * Currently the guard band can only be enabled or disabled. + */ +struct GuardBandOptions { + bool enabled = false; +}; + /** * List of available post-processing anti-aliasing techniques. * @see setAntiAliasing, getAntiAliasing, setSampleCount diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index 14c7b8383f..2de0601401 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -84,6 +84,7 @@ public: using VsmShadowOptions = VsmShadowOptions; using SoftShadowOptions = SoftShadowOptions; using ScreenSpaceReflectionsOptions = ScreenSpaceReflectionsOptions; + using GuardBandOptions = GuardBandOptions; /** * Sets the View's name. Only useful for debugging. @@ -345,6 +346,20 @@ public: */ ScreenSpaceReflectionsOptions const& getScreenSpaceReflectionsOptions() const noexcept; + /** + * Enables or disable screen-space guard band. Disabled by default. + * + * @param options guard band options + */ + void setGuardBandOptions(GuardBandOptions options) noexcept; + + /** + * Returns screen-space guard band options. + * + * @return guard band options + */ + GuardBandOptions const& getGuardBandOptions() const noexcept; + /** * Enables or disable multi-sample anti-aliasing (MSAA). Disabled by default. * diff --git a/filament/src/View.cpp b/filament/src/View.cpp index ec65744bd8..9c47e6c6c8 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -123,6 +123,14 @@ const View::ScreenSpaceReflectionsOptions& View::getScreenSpaceReflectionsOption return upcast(this)->getScreenSpaceReflectionsOptions(); } +void View::setGuardBandOptions(GuardBandOptions options) noexcept { + upcast(this)->setGuardBandOptions(options); +} + +GuardBandOptions const& View::getGuardBandOptions() const noexcept { + return upcast(this)->getGuardBandOptions(); +} + void View::setColorGrading(ColorGrading* colorGrading) noexcept { return upcast(this)->setColorGrading(upcast(colorGrading)); } diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index a5c404987e..370b140de0 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -477,13 +477,6 @@ public: float dzn = -1.0f; float dzf = 1.0f; } shadowmap; - struct { - bool enabled = true; - int sampleCount = 7; - int spiralTurns = 1; - int kernelSize = 23; - float stddev = 8.0f; - } ssao; struct { bool camera_at_origin = true; struct { diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index 00b8e9f841..25e172f20d 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -66,8 +66,6 @@ FRenderer::FRenderer(FEngine& engine) : mPerRenderPassArena(engine.getPerRenderPassAllocator()) { FDebugRegistry& debugRegistry = engine.getDebugRegistry(); - debugRegistry.registerProperty("d.ssao.enabled", - &engine.debug.ssao.enabled); debugRegistry.registerProperty("d.renderer.doFrameCapture", &engine.debug.renderer.doFrameCapture); @@ -476,6 +474,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { auto vignetteOptions = view.getVignetteOptions(); auto colorGrading = view.getColorGrading(); auto ssReflectionsOptions = view.getScreenSpaceReflectionsOptions(); + auto guardBandOptions = view.getGuardBandOptions(); const uint8_t msaaSampleCount = msaaOptions.enabled ? msaaOptions.sampleCount : 1u; if (!hasPostProcess) { // disable all effects that are part of post-processing @@ -538,12 +537,15 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { CameraInfo cameraInfo = view.computeCameraInfo(engine); // when colorgrading-as-subpass is active, we know that many other effects are disabled - // such as dof, bloom. Moreover if fxaa and scaling are not enabled, we're essentially in + // such as dof, bloom. Moreover, if fxaa and scaling are not enabled, we're essentially in // a very fast rendering path -- in this case, we would need an extra blit to "resolve" the // buffer padding (because there are no other pass that can do it as a side effect). // In this case, it is better to skip the padding, which won't be helping much. const bool noBufferPadding = colorGradingConfig.asSubpass && !hasFXAA && !scaled; + // guardBand must be a multiple of 16 to guarantee the same exact rendering up to 4 mip levels. + float guardBand = guardBandOptions.enabled ? 16.0f : 0.0f; + if (hasPostProcess && !noBufferPadding) { // We always pad the rendering viewport to dimensions multiple of 16, this guarantees // that up to 4 mipmap levels are possible with an exact 1:2 scale. This also helps @@ -558,10 +560,14 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // Without post-processing, we usually draw directly into // the SwapChain, and we might want to keep it this way. + auto round = [](uint32_t x) { + constexpr uint32_t rounding = 16u; + return (x + (rounding - 1u)) & ~(rounding - 1u); + }; + // compute the new rendering width and height, multiple of 16. - constexpr uint32_t rounding = 16u; - const float width = float( (svp.width + (rounding - 1u)) & ~(rounding - 1u) ); - const float height = float( (svp.height + (rounding - 1u)) & ~(rounding - 1u) ); + const float width = float(round(svp.width )) + 2.0f * guardBand; + const float height = float(round(svp.height)) + 2.0f * guardBand; // scale the field-of-view up, so it covers exactly the extra pixels const float3 clipSpaceScaling{ @@ -576,8 +582,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // note: this creates an asymmetric frustum -- but we eventually copy only the // left/bottom part, which is a symmetric region. const float2 clipSpaceTranslation{ - 1.0f - clipSpaceScaling.x, - 1.0f - clipSpaceScaling.y + 1.0f - clipSpaceScaling.x - 2.0f * guardBand / width, + 1.0f - clipSpaceScaling.y - 2.0f * guardBand / height }; mat4f ts = mat4f::scaling(clipSpaceScaling); @@ -589,6 +595,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // adjust svp to the new, larger, rendering dimensions svp.width = uint32_t(width); svp.height = uint32_t(height); + xvp.left = int32_t(guardBand); + xvp.bottom = int32_t(guardBand); } view.prepare(engine, driver, arena, svp, cameraInfo, getShaderUserTime(), needsAlphaChannel); diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index 828bf31c40..01aebcd7f9 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -953,6 +953,10 @@ void FView::setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions optio mScreenSpaceReflectionsOptions = options; } +void FView::setGuardBandOptions(GuardBandOptions options) noexcept { + mGuardBandOptions = options; +} + void FView::setAmbientOcclusionOptions(AmbientOcclusionOptions options) noexcept { options.radius = math::max(0.0f, options.radius); options.power = std::max(0.0f, options.power); diff --git a/filament/src/details/View.h b/filament/src/details/View.h index 2ea488d15e..a25531126f 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -232,6 +232,12 @@ public: return mScreenSpaceReflectionsOptions; } + void setGuardBandOptions(GuardBandOptions options) noexcept; + + GuardBandOptions const& getGuardBandOptions() const noexcept { + return mGuardBandOptions; + } + void setColorGrading(FColorGrading* colorGrading) noexcept { mColorGrading = colorGrading == nullptr ? mDefaultColorGrading : colorGrading; } @@ -494,6 +500,7 @@ private: TemporalAntiAliasingOptions mTemporalAntiAliasingOptions; MultiSampleAntiAliasingOptions mMultiSampleAntiAliasingOptions; ScreenSpaceReflectionsOptions mScreenSpaceReflectionsOptions; + GuardBandOptions mGuardBandOptions; BlendMode mBlendMode = BlendMode::OPAQUE; const FColorGrading* mColorGrading = nullptr; const FColorGrading* mDefaultColorGrading = nullptr; diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h index 248553f15a..df9fa36c17 100644 --- a/libs/viewer/include/viewer/Settings.h +++ b/libs/viewer/include/viewer/Settings.h @@ -72,6 +72,7 @@ using MultiSampleAntiAliasingOptions = filament::View::MultiSampleAntiAliasingOp using TemporalAntiAliasingOptions = filament::View::TemporalAntiAliasingOptions; using VignetteOptions = filament::View::VignetteOptions; using VsmShadowOptions = filament::View::VsmShadowOptions; +using GuardBandOptions = filament::View::GuardBandOptions; using LightManager = filament::LightManager; // These functions push all editable property values to their respective Filament objects. @@ -171,6 +172,7 @@ struct ViewSettings { TemporalAntiAliasingOptions taa; VignetteOptions vignette; VsmShadowOptions vsmShadowOptions; + GuardBandOptions guardBand; // Custom View Options ColorGradingSettings colorGrading; diff --git a/libs/viewer/src/AutomationSpec.cpp b/libs/viewer/src/AutomationSpec.cpp index 9224efff00..12acbce555 100644 --- a/libs/viewer/src/AutomationSpec.cpp +++ b/libs/viewer/src/AutomationSpec.cpp @@ -35,10 +35,8 @@ using std::vector; static const bool VERBOSE = false; -namespace filament { -namespace viewer { +namespace filament::viewer { -// The default spec generates 66 test cases. static const char* DEFAULT_AUTOMATION = R"TXT([ { "name": "ppoff", @@ -63,7 +61,8 @@ static const char* DEFAULT_AUTOMATION = R"TXT([ "view.ssao.enabled": [false, true], "view.screenSpaceReflections.enabled": [false, true] "view.bloom.enabled": [false, true], - "view.dof.enabled": [false, true] + "view.dof.enabled": [false, true], + "view.guardBand.enabled": [false, true] } } ] @@ -335,5 +334,4 @@ size_t AutomationSpec::size() const { return mImpl->cases.size(); } AutomationSpec::AutomationSpec(Impl* impl) : mImpl(impl) {} AutomationSpec::~AutomationSpec() { delete mImpl; } -} // namespace viewer -} // namespace filament +} // namespace filament::viewer diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index cc09990ab7..0e7e4f7b52 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -247,6 +247,27 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, return i; } +static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, + GuardBandOptions* out) { + CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i++].size; + for (int j = 0; j < size; ++j) { + const jsmntok_t tok = tokens[i]; + CHECK_KEY(tok); + if (0 == compare(tok, jsonChunk, "enabled")) { + i = parse(tokens, i + 1, jsonChunk, &out->enabled); + } else { + slog.w << "Invalid guard band options key: '" << STR(tok, jsonChunk) << "'" << io::endl; + i = parse(tokens, i + 1); + } + if (i < 0) { + slog.e << "Invalid guard band options value: '" << STR(tok, jsonChunk) << "'" << io::endl; + return i; + } + } + return i; +} + static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, DynamicResolutionOptions* out) { CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); @@ -783,6 +804,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ViewSett i = parse(tokens, i + 1, jsonChunk, &out->dynamicLighting); } else if (compare(tok, jsonChunk, "shadowType") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->shadowType); + } else if (compare(tok, jsonChunk, "guardBand") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->guardBand); } else if (compare(tok, jsonChunk, "vsmShadowOptions") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->vsmShadowOptions); } else if (compare(tok, jsonChunk, "postProcessingEnabled") == 0) { @@ -1051,6 +1074,7 @@ void applySettings(const ViewSettings& settings, View* dest) { settings.dynamicLighting.zLightFar); dest->setShadowType(settings.shadowType); dest->setVsmShadowOptions(settings.vsmShadowOptions); + dest->setGuardBandOptions(settings.guardBand); dest->setPostProcessingEnabled(settings.postProcessingEnabled); } @@ -1551,6 +1575,12 @@ static std::ostream& operator<<(std::ostream& out, const VsmShadowOptions& in) { << "}"; } +static std::ostream& operator<<(std::ostream& out, const GuardBandOptions& in) { + return out << "{\n" + << "\"enabled\": " << to_string(in.enabled) << "\n" + << "}"; +} + static std::ostream& operator<<(std::ostream& out, const ViewSettings& in) { return out << "{\n" << "\"antiAliasing\": " << in.antiAliasing << ",\n" @@ -1569,6 +1599,7 @@ static std::ostream& operator<<(std::ostream& out, const ViewSettings& in) { << "\"dynamicLighting\": " << (in.dynamicLighting) << ",\n" << "\"shadowType\": " << (in.shadowType) << ",\n" << "\"vsmShadowOptions\": " << (in.vsmShadowOptions) << ",\n" + << "\"guardBand\": " << (in.guardBand) << ",\n" << "\"postProcessingEnabled\": " << to_string(in.postProcessingEnabled) << "\n" << "}"; } diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp index 3516fcc654..e890307f09 100644 --- a/libs/viewer/src/ViewerGui.cpp +++ b/libs/viewer/src/ViewerGui.cpp @@ -757,6 +757,8 @@ void ViewerGui::updateUserInterface() { ImGui::SliderFloat("Stride", &ssrefl.stride, 1.0, 10.0f); } ImGui::Unindent(); + + ImGui::Checkbox("Screen-space Guard Band", &mSettings.view.guardBand.enabled); } if (ImGui::CollapsingHeader("Dynamic Resolution")) { diff --git a/web/filament-js/extensions.js b/web/filament-js/extensions.js index 346fa02aff..ee7afc5ecd 100644 --- a/web/filament-js/extensions.js +++ b/web/filament-js/extensions.js @@ -403,6 +403,17 @@ Filament.loadClassExtensions = function() { this._setVignetteOptions(options); }; + /// setGuardBandOptions ::method:: + /// overrides ::argument:: Dictionary with one or more of the following properties: \ + /// enabled. + Filament.View.prototype.setGuardBandOptions = function(overrides) { + const options = { + enabled: false + }; + Object.assign(options, overrides); + this._setGuardBandOptions(options); + }; + /// BufferObject ::core class:: /// setBuffer ::method:: diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts index b8a992eddc..210b581f34 100644 --- a/web/filament-js/filament.d.ts +++ b/web/filament-js/filament.d.ts @@ -152,6 +152,10 @@ export interface View$VignetteOptions { enabled?: boolean; } +export interface View$GuardBandOptions { + enabled?: boolean; +} + export function fitIntoUnitCube(box: Aabb): mat4; export function multiplyMatrices(a: mat4, b: mat4): mat4; @@ -532,6 +536,7 @@ export class View { public setScreenSpaceReflectionsOptions(options: View$ScreenSpaceReflectionsOptions): void; public setFogOptions(options: View$FogOptions): void; public setVignetteOptions(options: View$VignetteOptions): void; + public setGuardBandOptions(options: View$GuardBandOptions): void; public setAmbientOcclusion(ambientOcclusion: View$AmbientOcclusion): void; public getAmbientOcclusion(): View$AmbientOcclusion; public setBlendMode(mode: View$BlendMode): void; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 997b2662a7..3d68e9ff5f 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -447,6 +447,9 @@ value_object("View$VignetteOptions") .field("color", &filament::View::VignetteOptions::color) .field("enabled", &filament::View::VignetteOptions::enabled); +value_object("View$GuardBandOptions") + .field("enabled", &filament::View::GuardBandOptions::enabled); + value_object("LightManager$ShadowOptions") .field("mapSize", &LightManager::ShadowOptions::mapSize) .field("shadowCascades", &LightManager::ShadowOptions::shadowCascades) @@ -707,6 +710,7 @@ class_("View") .function("_setBloomOptions", &View::setBloomOptions) .function("_setFogOptions", &View::setFogOptions) .function("_setVignetteOptions", &View::setVignetteOptions) + .function("_setGuardBandOptions", &View::setGuardBandOptions) .function("setAmbientOcclusion", &View::setAmbientOcclusion) .function("getAmbientOcclusion", &View::getAmbientOcclusion) .function("setAntiAliasing", &View::setAntiAliasing)