Implement an optional guard band for screen-space effects

When the guard band is enabled, the effective rendering area is
increased by a certain amount (currently 16 pixels on each side) so
that screen-space effects (e.g. SSAO, SSR, DoF) behave better around
the edges of the screen. Of course, this comes at a performance and
memory cost.

Currently the guard band is not configurable other than being optional.

Added c++, java and js bindings.
This commit is contained in:
Mathias Agopian
2022-05-06 16:08:32 -07:00
committed by Mathias Agopian
parent f40ef6209b
commit 626beee345
17 changed files with 178 additions and 32 deletions
+1
View File
@@ -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
@@ -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 });
}
@@ -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);
}
+10
View File
@@ -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
+15
View File
@@ -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.
*
+8
View File
@@ -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));
}
-7
View File
@@ -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 {
+16 -8
View File
@@ -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);
+4
View File
@@ -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);
+7
View File
@@ -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;
+2
View File
@@ -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;
+4 -6
View File
@@ -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
+31
View File
@@ -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"
<< "}";
}
+2
View File
@@ -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")) {
+11
View File
@@ -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::
+5
View File
@@ -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;
+4
View File
@@ -447,6 +447,9 @@ value_object<filament::View::VignetteOptions>("View$VignetteOptions")
.field("color", &filament::View::VignetteOptions::color)
.field("enabled", &filament::View::VignetteOptions::enabled);
value_object<filament::View::GuardBandOptions>("View$GuardBandOptions")
.field("enabled", &filament::View::GuardBandOptions::enabled);
value_object<LightManager::ShadowOptions>("LightManager$ShadowOptions")
.field("mapSize", &LightManager::ShadowOptions::mapSize)
.field("shadowCascades", &LightManager::ShadowOptions::shadowCascades)
@@ -707,6 +710,7 @@ class_<View>("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)