Add support for SGSR 1.0

SGSR stands for Snapdragon Game Super Resolution. It is an efficient
upscaler for mobile.

Split the upscaling passes into multiple methods in PostProcessManager.
Also, add a way to preserve the alpha channel in the FXAA pass.
This commit is contained in:
Mathias Agopian
2025-01-25 00:38:18 -08:00
committed by Mathias Agopian
parent 5b3343fcc2
commit 48a2c6483b
10 changed files with 458 additions and 68 deletions

View File

@@ -1420,10 +1420,10 @@ public class View {
/**
* Upscaling quality
* LOW: bilinear filtered blit. Fastest, poor quality
* MEDIUM: AMD FidelityFX FSR1 w/ mobile optimizations
* MEDIUM: Qualcomm Snapdragon Game Super Resolution (SGSR) 1.0
* HIGH: AMD FidelityFX FSR1 w/ mobile optimizations
* ULTRA: AMD FidelityFX FSR1
* FSR1 require a well anti-aliased (MSAA or TAA), noise free scene.
* FSR1 and SGSR require a well anti-aliased (MSAA or TAA), noise free scene. Avoid FXAA and dithering.
*
* The default upscaling quality is set to LOW.
*/

View File

@@ -252,6 +252,7 @@ set(MATERIAL_SRCS
src/materials/fsr/fsr_easu_mobile.mat
src/materials/fsr/fsr_easu_mobileF.mat
src/materials/fsr/fsr_rcas.mat
src/materials/sgsr/sgsr1.mat
src/materials/resolveDepth.mat
src/materials/separableGaussianBlur.mat
src/materials/skybox.mat
@@ -504,6 +505,12 @@ add_custom_command(
APPEND
)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/sgsr1.filamat"
DEPENDS src/materials/sgsr/sgsr1_shader_mobile.fs
APPEND
)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/separableGaussianBlur.filamat"
DEPENDS src/materials/separableGaussianBlur.vs

View File

@@ -86,10 +86,10 @@ struct DynamicResolutionOptions {
/**
* Upscaling quality
* LOW: bilinear filtered blit. Fastest, poor quality
* MEDIUM: AMD FidelityFX FSR1 w/ mobile optimizations
* MEDIUM: Qualcomm Snapdragon Game Super Resolution (SGSR) 1.0
* HIGH: AMD FidelityFX FSR1 w/ mobile optimizations
* ULTRA: AMD FidelityFX FSR1
* FSR1 require a well anti-aliased (MSAA or TAA), noise free scene.
* FSR1 and SGSR require a well anti-aliased (MSAA or TAA), noise free scene. Avoid FXAA and dithering.
*
* The default upscaling quality is set to LOW.
*/

View File

@@ -311,6 +311,7 @@ static const PostProcessManager::StaticMaterialInfo sMaterialList[] = {
{ "fsr_easu_mobile", MATERIAL(FSR_EASU_MOBILE) },
{ "fsr_easu_mobileF", MATERIAL(FSR_EASU_MOBILEF) },
{ "fsr_rcas", MATERIAL(FSR_RCAS) },
{ "sgsr1", MATERIAL(SGSR1) },
{ "debugShadowCascades", MATERIAL(DEBUGSHADOWCASCADES) },
{ "resolveDepth", MATERIAL(RESOLVEDEPTH) },
{ "shadowmap", MATERIAL(SHADOWMAP) },
@@ -2328,7 +2329,7 @@ void PostProcessManager::colorGradingPrepareSubpass(DriverApi& driver,
mi->setParameter("vignette", vignetteParameters);
mi->setParameter("vignetteColor", vignetteOptions.color);
mi->setParameter("dithering", colorGradingConfig.dithering);
mi->setParameter("fxaa", colorGradingConfig.fxaa);
mi->setParameter("outputLuminance", colorGradingConfig.outputLuminance);
mi->setParameter("temporalNoise", temporalNoise);
mi->commit(driver);
@@ -2544,7 +2545,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
mi->setParameter("bloom", bloomParameters);
mi->setParameter("vignette", vignetteParameters);
mi->setParameter("vignetteColor", vignetteOptions.color);
mi->setParameter("fxaa", colorGradingConfig.fxaa);
mi->setParameter("outputLuminance", colorGradingConfig.outputLuminance);
mi->setParameter("temporalNoise", temporalNoise);
mi->setParameter("viewport", float4{
float(vp.left) / input.width,
@@ -2562,7 +2563,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::colorGrading(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> const input, filament::Viewport const& vp,
TextureFormat const outFormat, bool const translucent) noexcept {
TextureFormat const outFormat, bool const preserveAlphaChannel) noexcept {
struct PostProcessFXAA {
FrameGraphId<FrameGraphTexture> input;
@@ -2587,7 +2588,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::fxaa(FrameGraph& fg,
auto const& material = getPostProcessMaterial("fxaa");
PostProcessVariant const variant = translucent ?
PostProcessVariant const variant = preserveAlphaChannel ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE;
FMaterialInstance* const mi =
@@ -2920,20 +2921,171 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
}
FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool translucent,
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> const input,
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
SamplerMagFilter filter) noexcept {
bool sourceHasLuminance, DynamicResolutionOptions dsrOptions,
FrameGraphId<FrameGraphTexture> const input, filament::Viewport const& vp,
FrameGraphTexture::Descriptor const& outDesc, SamplerMagFilter filter) noexcept {
// The code below cannot handle sub-resources
assert_invariant(fg.getSubResourceDescriptor(input).layer == 0);
assert_invariant(fg.getSubResourceDescriptor(input).level == 0);
const bool lowQualityFallback = translucent && dsrOptions.quality != QualityLevel::LOW;
const bool lowQualityFallback = translucent;
if (lowQualityFallback) {
// FidelityFX-FSR doesn't support the alpha channel currently
// FidelityFX-FSR nor SGSR support the source alpha channel currently
dsrOptions.quality = QualityLevel::LOW;
}
if (dsrOptions.quality == QualityLevel::LOW) {
return upscaleBilinear(fg, translucent, dsrOptions, input, vp, outDesc, filter);
}
if (dsrOptions.quality == QualityLevel::MEDIUM) {
return upscaleSGSR1(fg, sourceHasLuminance, dsrOptions, input, vp, outDesc);
}
return upscaleFSR1(fg, dsrOptions, input, vp, outDesc);
}
FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph& fg, bool translucent,
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> const input,
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
SamplerMagFilter filter) noexcept {
struct QuadBlitData {
FrameGraphId<FrameGraphTexture> input;
FrameGraphId<FrameGraphTexture> output;
};
auto const& ppQuadBlit = fg.addPass<QuadBlitData>(dsrOptions.enabled ? "upscaling" : "compositing",
[&](FrameGraph::Builder& builder, auto& data) {
data.input = builder.sample(input);
data.output = builder.createTexture("upscaled output", outDesc);
data.output = builder.write(data.output, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
builder.declareRenderPass(builder.getName(data.output), {
.attachments = { .color = { data.output } },
.clearFlags = TargetBufferFlags::DEPTH });
},
[this, vp, translucent, filter](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
// helper to enable blending
auto enableTranslucentBlending = [](PipelineState& pipeline) {
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA;
pipeline.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
};
auto color = resources.getTexture(data.input);
auto const& inputDesc = resources.getDescriptor(data.input);
// --------------------------------------------------------------------------------
// set uniforms
auto& material = getPostProcessMaterial("blitLow");
FMaterialInstance* const mi =
PostProcessMaterial::getMaterialInstance(mEngine, material);
mi->setParameter("color", color, {
.filterMag = filter
});
mi->setParameter("levelOfDetail", 0.0f);
mi->setParameter("viewport", float4{
float(vp.left) / inputDesc.width,
float(vp.bottom) / inputDesc.height,
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->commit(driver);
mi->use(driver);
auto out = resources.getRenderPassInfo();
auto pipeline = getPipelineState(material.getMaterial(mEngine));
if (translucent) {
enableTranslucentBlending(pipeline);
}
renderFullScreenQuad(out, pipeline, driver);
});
auto output = ppQuadBlit->output;
// if we had to take the low quality fallback, we still do the "sharpen pass"
if (dsrOptions.sharpness > 0.0f) {
output = rcas(fg, dsrOptions.sharpness, output, outDesc, translucent);
}
// we rely on automatic culling of unused render passes
return output;
}
FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg, bool sourceHasLuminance,
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> const input,
filament::Viewport const&, FrameGraphTexture::Descriptor const& outDesc) noexcept {
struct QuadBlitData {
FrameGraphId<FrameGraphTexture> input;
FrameGraphId<FrameGraphTexture> output;
};
auto const& ppQuadBlit = fg.addPass<QuadBlitData>(dsrOptions.enabled ? "upscaling" : "compositing",
[&](FrameGraph::Builder& builder, auto& data) {
data.input = builder.sample(input);
data.output = builder.createTexture("upscaled output", outDesc);
data.output = builder.write(data.output, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
builder.declareRenderPass(builder.getName(data.output), {
.attachments = { .color = { data.output } },
.clearFlags = TargetBufferFlags::DEPTH });
},
[this, sourceHasLuminance](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
auto color = resources.getTexture(data.input);
auto const& inputDesc = resources.getDescriptor(data.input);
// --------------------------------------------------------------------------------
// set uniforms
auto const& material = getPostProcessMaterial("sgsr1");
PostProcessVariant const variant = sourceHasLuminance ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE;
FMaterialInstance* const mi =
PostProcessMaterial::getMaterialInstance(mEngine, material, variant);
mi->setParameter("color", color, {
// The SGSR documentation doesn't clarify if LINEAR or NEAREST should be used. The
// sample code uses NEAREST, but that doesn't seem right, since it would mean the
// LERP mode would not be a LERP, and the non-edges would be sampled as NEAREST.
.filterMag = SamplerMagFilter::LINEAR
});
mi->setParameter("viewport", float4{
1.0f / inputDesc.width,
1.0f / inputDesc.height,
float(inputDesc.width),
float(inputDesc.height),
});
mi->commit(driver);
mi->use(driver);
auto out = resources.getRenderPassInfo();
commitAndRenderFullScreenQuad(driver, out, mi, variant);
});
auto output = ppQuadBlit->output;
// we rely on automatic culling of unused render passes
return output;
}
FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> const input,
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc) noexcept {
const bool twoPassesEASU = mWorkaroundSplitEasu &&
(dsrOptions.quality == QualityLevel::MEDIUM
|| dsrOptions.quality == QualityLevel::HIGH);
@@ -2964,7 +3116,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
.attachments = { .color = { data.output }, .depth = { data.depth }},
.clearFlags = TargetBufferFlags::DEPTH });
},
[this, twoPassesEASU, dsrOptions, vp, translucent, filter](FrameGraphResources const& resources,
[this, twoPassesEASU, dsrOptions, vp](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
@@ -2989,14 +3141,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
float2{ inputDesc.width, inputDesc.height });
};
// helper to enable blending
auto enableTranslucentBlending = [](PipelineState& pipeline) {
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA;
pipeline.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
};
auto color = resources.getTexture(data.input);
auto const& inputDesc = resources.getDescriptor(data.input);
auto const& outputDesc = resources.getDescriptor(data.output);
@@ -3023,29 +3167,21 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
}
{ // just a scope to not leak local variables
const std::string_view blitterNames[4] = {
"blitLow", "fsr_easu_mobile", "fsr_easu_mobile", "fsr_easu" };
unsigned const index = std::min(3u, unsigned(dsrOptions.quality));
const std::string_view blitterNames[2] = { "fsr_easu_mobile", "fsr_easu" };
unsigned const index = std::min(1u, unsigned(dsrOptions.quality) - 2);
easuMaterial = &getPostProcessMaterial(blitterNames[index]);
FMaterialInstance* const mi =
PostProcessMaterial::getMaterialInstance(mEngine, *easuMaterial);
if (dsrOptions.quality != QualityLevel::LOW) {
setEasuUniforms(mi, inputDesc, outputDesc);
}
setEasuUniforms(mi, inputDesc, outputDesc);
mi->setParameter("color", color, {
.filterMag = (dsrOptions.quality == QualityLevel::LOW) ?
filter : SamplerMagFilter::LINEAR
.filterMag = SamplerMagFilter::LINEAR
});
if (blitterNames[index] != "blitLow") {
mi->setParameter("resolution",
float4{outputDesc.width, outputDesc.height, 1.0f / outputDesc.width,
1.0f / outputDesc.height});
}
if (blitterNames[index] == "blitLow") {
mi->setParameter("levelOfDetail", 0.0f);
}
mi->setParameter("resolution",
float4{outputDesc.width, outputDesc.height, 1.0f / outputDesc.width,
1.0f / outputDesc.height});
mi->setParameter("viewport", float4{
float(vp.left) / inputDesc.width,
@@ -3066,29 +3202,19 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
auto pipeline0 = getPipelineState(splitEasuMaterial->getMaterial(mEngine));
auto pipeline1 = getPipelineState(easuMaterial->getMaterial(mEngine));
pipeline1.rasterState.depthFunc = SamplerCompareFunc::NE;
if (translucent) {
enableTranslucentBlending(pipeline0);
enableTranslucentBlending(pipeline1);
}
driver.beginRenderPass(out.target, out.params);
driver.draw(pipeline0, mFullScreenQuadRph, 0, 3, 1);
driver.draw(pipeline1, mFullScreenQuadRph, 0, 3, 1);
driver.endRenderPass();
} else {
auto pipeline = getPipelineState(easuMaterial->getMaterial(mEngine));
if (translucent) {
enableTranslucentBlending(pipeline);
}
renderFullScreenQuad(out, pipeline, driver);
}
});
auto output = ppQuadBlit->output;
// if we had to take the low quality fallback, we still do the "sharpen pass"
if (dsrOptions.sharpness > 0.0f &&
(dsrOptions.quality != QualityLevel::LOW || lowQualityFallback)) {
output = rcas(fg, dsrOptions.sharpness, output, outDesc, translucent);
if (dsrOptions.sharpness > 0.0f) {
output = rcas(fg, dsrOptions.sharpness, output, outDesc, false);
}
// we rely on automatic culling of unused render passes

View File

@@ -89,7 +89,7 @@ public:
bool asSubpass{};
bool customResolve{};
bool translucent{};
bool fxaa{};
bool outputLuminance{}; // Whether to output luminance in the alpha channel. Ignored by the TRANSLUCENT variant.
bool dithering{};
backend::TextureFormat ldrFormat{};
};
@@ -227,7 +227,7 @@ public:
// Anti-aliasing
FrameGraphId<FrameGraphTexture> fxaa(FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input, Viewport const& vp,
backend::TextureFormat outFormat, bool translucent) noexcept;
backend::TextureFormat outFormat, bool preserveAlphaChannel) noexcept;
// Temporal Anti-aliasing
void TaaJitterCamera(
@@ -251,12 +251,25 @@ public:
// high quality upscaler
// - when translucent, reverts to LINEAR
// - doens't handle sub-resouces
// - doesn't handle sub-resouces
FrameGraphId<FrameGraphTexture> upscale(FrameGraph& fg, bool translucent,
bool sourceHasLuminance, DynamicResolutionOptions dsrOptions,
FrameGraphId<FrameGraphTexture> input, Viewport const& vp,
FrameGraphTexture::Descriptor const& outDesc, backend::SamplerMagFilter filter) noexcept;
FrameGraphId<FrameGraphTexture> upscaleBilinear(FrameGraph& fg, bool translucent,
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> input,
Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
backend::SamplerMagFilter filter) noexcept;
FrameGraphId<FrameGraphTexture> upscaleFSR1(FrameGraph& fg,
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> input,
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc) noexcept;
FrameGraphId<FrameGraphTexture> upscaleSGSR1(FrameGraph& fg, bool sourceHasLuminance,
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> input,
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc) noexcept;
FrameGraphId<FrameGraphTexture> rcas(
FrameGraph& fg,
float sharpness,

View File

@@ -681,6 +681,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
hasColorGrading &&
!bloomOptions.enabled && !dofOptions.enabled && !taaOptions.enabled;
// whether we're scaled at all
bool scaled = any(notEqual(scale, float2(1.0f)));
// asSubpass is disabled with TAA (although it's supported) because performance was degraded
// on qualcomm hardware -- we might need a backend dependent toggle at some point
const PostProcessManager::ColorGradingConfig colorGradingConfig{
@@ -695,18 +698,15 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
hasColorGrading &&
!engine.debug.renderer.disable_subpasses,
.translucent = needsAlphaChannel,
.fxaa = hasFXAA,
.outputLuminance = hasFXAA || scaled, // ignored by translucent variants (false)
.dithering = hasDithering,
.ldrFormat = (hasColorGrading && hasFXAA) ?
.ldrFormat = (hasColorGrading && (hasFXAA || scaled)) ?
TextureFormat::RGBA8 : getLdrFormat(needsAlphaChannel)
};
// by construction (msaaSampleCount) both asSubpass and customResolve can't be true
assert_invariant(colorGradingConfig.asSubpass + colorGradingConfig.customResolve < 2);
// whether we're scaled at all
bool scaled = any(notEqual(scale, float2(1.0f)));
// vp is the user defined viewport within the View
filament::Viewport const& vp = view.getViewport();
@@ -1351,8 +1351,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
}
if (hasFXAA) {
input = ppm.fxaa(fg, input, xvp, colorGradingConfig.ldrFormat,
!hasColorGrading || needsAlphaChannel);
bool const preserveAlphaChannel = needsAlphaChannel ||
(hasColorGrading && colorGradingConfig.outputLuminance);
input = ppm.fxaa(fg, input, xvp, colorGradingConfig.ldrFormat, preserveAlphaChannel);
// the padded buffer is resolved now
xvp.left = xvp.bottom = 0;
svp = xvp;
@@ -1360,9 +1361,11 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
if (scaled) {
mightNeedFinalBlit = false;
auto viewport = DEBUG_DYNAMIC_SCALING ? xvp : vp;
input = ppm.upscale(fg, needsAlphaChannel, dsrOptions, input, xvp, {
.width = viewport.width, .height = viewport.height,
.format = colorGradingConfig.ldrFormat }, SamplerMagFilter::LINEAR);
bool const sourceHasLuminance = !needsAlphaChannel &&
(hasColorGrading && colorGradingConfig.outputLuminance);
input = ppm.upscale(fg, needsAlphaChannel, sourceHasLuminance, dsrOptions, input, xvp, {
.width = viewport.width, .height = viewport.height,
.format = colorGradingConfig.ldrFormat }, SamplerMagFilter::LINEAR);
xvp.left = xvp.bottom = 0;
svp = xvp;
}

View File

@@ -41,7 +41,7 @@ material {
},
{
type : int,
name : fxaa
name : outputLuminance
},
{
type : float,
@@ -166,10 +166,10 @@ void postProcess(inout PostProcessInputs postProcess) {
color = dither(color, materialParams.temporalNoise);
}
// kill alpha computations when opaque / fxaa luminance
// kill alpha computations when opaque / luminance
#if POST_PROCESS_OPAQUE
color.a = 1.0;
if (materialParams.fxaa > 0) {
if (materialParams.outputLuminance > 0) {
color.a = luminance(color.rgb);
}
#endif

View File

@@ -0,0 +1,29 @@
Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
SPDX-License-Identifier: BSD-3-Clause

View File

@@ -0,0 +1,80 @@
material {
name : sgsr1,
parameters : [
{
type : sampler2d,
name : color,
precision: medium
},
{
type : float4,
name : viewport,
precision: high
}
],
variables : [
vertex
],
depthWrite : false,
depthCulling : false,
domain: postprocess
}
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.normalizedUV = uvToRenderTargetUV(postProcess.normalizedUV);
postProcess.vertex.xy = postProcess.normalizedUV;
}
}
fragment {
void dummy(){}
precision mediump float;
precision highp int;
#if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER)
#define gather textureGather
#else
vec4 gather(const mediump sampler2D color, highp vec2 p, const int comp) {
highp ivec2 i = ivec2(p * materialParams.viewport.zw - 0.5);
vec4 d;
d[0] = texelFetchOffset(color, i, 0, ivec2(0, 1))[comp];
d[1] = texelFetchOffset(color, i, 0, ivec2(1, 1))[comp];
d[2] = texelFetchOffset(color, i, 0, ivec2(1, 0))[comp];
d[3] = texelFetchOffset(color, i, 0, ivec2(0, 0))[comp];
return d;
}
#endif
/*
* Operation modes:
* RGBA -> 1
* RGBY -> 3
* LERP -> 4
*/
// SGSR cannot support translucency;
// So we hijack the POST_PROCESS variant to select whether a luminance channel is present
#if POST_PROCESS_OPAQUE
# define OperationMode 1
#else
# define OperationMode 3
#endif
/*
* If set, will use edge direction to improve visual quality
* Expect a minimal cost increase
*/
#define UseEdgeDirection
#define EdgeThreshold 8.0 / 255.0
#define EdgeSharpness 2.0
#include "sgsr1_shader_mobile.fs"
void postProcess(inout PostProcessInputs postProcess) {
highp vec4 vp[1] = vec4[1]( materialParams.viewport );
postProcess.color = sgsr(materialParams_color, variable_vertex.xy, vp);
}
}

View File

@@ -0,0 +1,132 @@
//============================================================================================================
//
//
// Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause
//
//============================================================================================================
////////////////////////
// USER CONFIGURATION //
////////////////////////
float fastLanczos2(float x)
{
float wA = x-4.0;
float wB = x*wA-wA;
wA *= wA;
return wB*wA;
}
#if defined(UseEdgeDirection)
vec2 weightY(float dx, float dy, float c, vec3 data)
#else
vec2 weightY(float dx, float dy, float c, float data)
#endif
{
#if defined(UseEdgeDirection)
float std = data.x;
vec2 dir = data.yz;
float edgeDis = ((dx*dir.y)+(dy*dir.x));
float x = (((dx*dx)+(dy*dy))+((edgeDis*edgeDis)*((clamp(((c*c)*std),0.0,1.0)*0.7)+-1.0)));
#else
float std = data;
float x = ((dx*dx)+(dy*dy))* 0.55 + clamp(abs(c)*std, 0.0, 1.0);
#endif
float w = fastLanczos2(x);
return vec2(w, w * c);
}
vec2 edgeDirection(vec4 left, vec4 right)
{
vec2 dir;
float RxLz = (right.x + (-left.z));
float RwLy = (right.w + (-left.y));
vec2 delta;
delta.x = (RxLz + RwLy);
delta.y = (RxLz + (-RwLy));
float lengthInv = inversesqrt((delta.x * delta.x+ 3.075740e-05) + (delta.y * delta.y));
dir.x = (delta.x * lengthInv);
dir.y = (delta.y * lengthInv);
return dir;
}
vec4 sgsr(mediump sampler2D ps0, highp vec2 in_TEXCOORD0, highp vec4 ViewportInfo[1])
{
vec4 color;
if(OperationMode == 1)
color.xyz = textureLod(ps0,in_TEXCOORD0.xy,0.0).xyz;
else
color.xyzw = textureLod(ps0,in_TEXCOORD0.xy,0.0).xyzw;
#if OperationMode != 4
if (OperationMode!=4)
{
highp vec2 imgCoord = ((in_TEXCOORD0.xy*ViewportInfo[0].zw)+vec2(-0.5,0.5));
highp vec2 imgCoordPixel = floor(imgCoord);
highp vec2 coord = (imgCoordPixel*ViewportInfo[0].xy);
vec2 pl = (imgCoord+(-imgCoordPixel));
vec4 left = gather(ps0,coord, OperationMode);
float edgeVote = abs(left.z - left.y) + abs(color[OperationMode] - left.y) + abs(color[OperationMode] - left.z) ;
if(edgeVote > EdgeThreshold)
{
coord.x += ViewportInfo[0].x;
vec4 right = gather(ps0,coord + vec2(ViewportInfo[0].x, 0.0), OperationMode);
vec4 upDown;
upDown.xy = gather(ps0,coord + vec2(0.0, -ViewportInfo[0].y),OperationMode).wz;
upDown.zw = gather(ps0,coord+ vec2(0.0, ViewportInfo[0].y), OperationMode).yx;
float mean = (left.y+left.z+right.x+right.w)*0.25;
left = left - vec4(mean);
right = right - vec4(mean);
upDown = upDown - vec4(mean);
color.w = color[OperationMode] - mean;
float sum = (((((abs(left.x)+abs(left.y))+abs(left.z))+abs(left.w)) +
(((abs(right.x)+abs(right.y))+abs(right.z))+abs(right.w))) +
(((abs(upDown.x)+abs(upDown.y))+abs(upDown.z))+abs(upDown.w)));
#if defined(UseEdgeDirection)
float sumMean = 1.014185e+01 / sum;
float std = (sumMean * sumMean);
vec3 data = vec3(std, edgeDirection(left, right));
#else
// taken from sgsr sample
float data = 2.181818 / sum;
#endif
vec2 aWY = weightY(pl.x, pl.y+1.0, upDown.x,data);
aWY += weightY(pl.x-1.0, pl.y+1.0, upDown.y,data);
aWY += weightY(pl.x-1.0, pl.y-2.0, upDown.z,data);
aWY += weightY(pl.x, pl.y-2.0, upDown.w,data);
aWY += weightY(pl.x+1.0, pl.y-1.0, left.x,data);
aWY += weightY(pl.x, pl.y-1.0, left.y,data);
aWY += weightY(pl.x, pl.y, left.z,data);
aWY += weightY(pl.x+1.0, pl.y, left.w,data);
aWY += weightY(pl.x-1.0, pl.y-1.0, right.x,data);
aWY += weightY(pl.x-2.0, pl.y-1.0, right.y,data);
aWY += weightY(pl.x-2.0, pl.y, right.z,data);
aWY += weightY(pl.x-1.0, pl.y, right.w,data);
float finalY = aWY.y/aWY.x;
float maxY = max(max(left.y,left.z),max(right.x,right.w));
float minY = min(min(left.y,left.z),min(right.x,right.w));
float deltaY = clamp(EdgeSharpness*finalY, minY, maxY) -color.w;
//smooth high contrast input
deltaY = clamp(deltaY, -23.0 / 255.0, 23.0 / 255.0);
color.x = clamp(color.x+deltaY, 0.0, 1.0);
color.y = clamp(color.y+deltaY, 0.0, 1.0);
color.z = clamp(color.z+deltaY, 0.0, 1.0);
}
}
#endif
color.w = 1.0; //assume alpha channel is not used
return color;
}