Implement lod bias for user materials

A lod bias can be applied to textures when dynamic resolution is
enabled. A proper lod bias is calculated and applied for quality levels
HIGH and ULTRA.

Lod Bias is implemented in glslang, which mean it's ignored when
glslang is not used, which currently happens with OpenGL backends with
optimization turned off (which includes filament LITE). This may be
fixed in the future.
This commit is contained in:
Mathias Agopian
2021-09-14 17:25:25 -07:00
committed by Mathias Agopian
parent 1a8550e8e0
commit ceeccde92c
18 changed files with 151 additions and 3 deletions

View File

@@ -5,6 +5,9 @@ A new header is inserted each time a *tag* is created.
## v1.12.6 (currently main branch)
- engine: Added concept of lod bias to materials.
[⚠️ **Materials need to be rebuilt to access this new feature**].
## v1.12.5
- engine: Fix, BGRA ordering respected for external images with OpenGL on iOS.

View File

@@ -87,6 +87,16 @@ void PerViewUniforms::prepareCamera(const CameraInfo& camera) noexcept {
s.clipControl = mClipControl;
}
void PerViewUniforms::prepareUpscaler(math::float2 scale,
DynamicResolutionOptions const& options) noexcept {
auto& s = mPerViewUb.edit();
if (options.quality >= QualityLevel::HIGH) {
s.lodBias = std::log2(std::min(scale.x, scale.y));
} else {
s.lodBias = 0.0f;
}
}
void PerViewUniforms::prepareExposure(float ev100) noexcept {
const float exposure = Exposure::exposure(ev100);
auto& s = mPerViewUb.edit();

View File

@@ -31,6 +31,7 @@
namespace filament {
struct FogOptions;
struct DynamicResolutionOptions;
struct AmbientOcclusionOptions;
struct VsmShadowOptions;
@@ -53,6 +54,7 @@ public:
void terminate(FEngine& engine);
void prepareCamera(const CameraInfo& camera) noexcept;
void prepareUpscaler(math::float2 scale, DynamicResolutionOptions const& options) noexcept;
void prepareViewport(const filament::Viewport& viewport) noexcept;
void prepareTime(FEngine& engine, math::float4 const& userTime) noexcept;
void prepareExposure(float ev100) noexcept;

View File

@@ -258,6 +258,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
view.prepare(engine, driver, arena, svp, getShaderUserTime());
view.prepareUpscaler(scale);
// start froxelization immediately, it has no dependencies
JobSystem::Job* jobFroxelize = nullptr;
if (view.hasDynamicLighting()) {

View File

@@ -554,6 +554,11 @@ UTILS_NOINLINE
});
}
void FView::prepareUpscaler(float2 scale) const noexcept {
SYSTRACE_CALL();
mPerViewUniforms.prepareUpscaler(scale, mDynamicResolution);
}
void FView::prepareCamera(const CameraInfo& camera) const noexcept {
SYSTRACE_CALL();
mPerViewUniforms.prepareCamera(camera);

View File

@@ -159,6 +159,7 @@ public:
return mName.c_str();
}
void prepareUpscaler(math::float2 scale) const noexcept;
void prepareCamera(const CameraInfo& camera) const noexcept;
void prepareViewport(const Viewport& viewport) const noexcept;
void prepareShadowing(FEngine& engine, backend::DriverApi& driver,

View File

@@ -128,8 +128,13 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float vsmLightBleedReduction;
float vsmReserved0;
float lodBias;
float reserved1;
float reserved2;
float reserved3;
// bring PerViewUib to 2 KiB
math::float4 padding2[59];
math::float4 padding2[58];
};
// 2 KiB == 128 float4s

View File

@@ -30,6 +30,7 @@
#include "sca/GLSLTools.h"
#include <utils/Log.h>
#include <filament/MaterialEnums.h>
using namespace glslang;
using namespace spirv_cross;
@@ -203,6 +204,12 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
return false;
}
// add texture lod bias
if (config.shaderType == filament::backend::FRAGMENT &&
config.domain == filament::MaterialDomain::SURFACE) {
GLSLTools::textureLodBias(tShader);
}
program.addShader(&tShader);
// Even though we only have a single shader stage, linking is still necessary to finalize
// SPIR-V types

View File

@@ -50,6 +50,7 @@ public:
struct Config {
filament::backend::ShaderType shaderType;
filament::backend::ShaderModel shaderModel;
filament::MaterialDomain domain;
bool hasFramebufferFetch;
struct {
std::vector<std::pair<uint32_t, uint32_t>> subpassInputToColorLocation;

View File

@@ -726,7 +726,8 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
GLSLPostProcessor::Config config{
.shaderType = v.stage,
.shaderModel = shaderModel,
.glsl = {}
.domain = mMaterialDomain,
.glsl = {},
};
config.hasFramebufferFetch = mEnableFramebufferFetch;

View File

@@ -109,8 +109,13 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
.add("vsmLightBleedReduction", 1, UniformInterfaceBlock::Type::FLOAT)
.add("vsmReserved0", 1, UniformInterfaceBlock::Type::FLOAT)
.add("lodBias", 1, UniformInterfaceBlock::Type::FLOAT)
.add("reserved1", 1, UniformInterfaceBlock::Type::FLOAT)
.add("reserved2", 1, UniformInterfaceBlock::Type::FLOAT)
.add("reserved3", 1, UniformInterfaceBlock::Type::FLOAT)
// bring PerViewUib to 2 KiB
.add("padding2", 59, UniformInterfaceBlock::Type::FLOAT4)
.add("padding2", 58, UniformInterfaceBlock::Type::FLOAT4)
.build();
return uib;
}

View File

@@ -359,4 +359,91 @@ void getFunctionParameters(TIntermAggregate* func, std::vector<FunctionParameter
}
}
template <typename F>
class TraverserAdapter: public TIntermTraverser {
F closure;
public:
explicit TraverserAdapter(F closure)
: TIntermTraverser(true, false, false, false),
closure(closure) {
}
bool visitAggregate(TVisit visit, TIntermAggregate* node) override {
return closure(visit, node);
}
};
void textureLodBias(TIntermediate* intermediate, TIntermNode* root,
const char* lodBiasSymbolName) {
// we need to run this only from the user's main entry point
// First, find the "lodBias" symbol
TIntermSymbol* pIntermSymbolLodBias = nullptr;
TraverserAdapter findLodBiasSymbol(
[&, done = false](TVisit visit, TIntermAggregate* node) mutable {
if (node->getOp() == glslang::EOpSequence) {
return !done;
}
if (node->getOp() == glslang::EOpLinkerObjects) {
for (TIntermNode* item : node->getSequence()) {
TIntermSymbol* symbol = item->getAsSymbolNode();
if (symbol && symbol->getBasicType() == TBasicType::EbtFloat) {
if (symbol->getName() == lodBiasSymbolName) {
pIntermSymbolLodBias = symbol;
done = true;
break;
}
}
}
}
return false;
});
root->traverse(&findLodBiasSymbol);
if (!pIntermSymbolLodBias) {
// something went wrong
utils::slog.e << "lod bias ignored because \"" << lodBiasSymbolName << "\" was not found!" << utils::io::endl;
return;
}
// add lod bias to texture calls
TraverserAdapter addLodBiasToTextureCalls(
[&](TVisit visit, TIntermAggregate* node) {
// skip everything that's not a texture() call
if (node->getOp() != glslang::EOpTexture) {
return true;
}
TIntermSequence& sequence = node->getSequence();
// first check that we have the correct sampler
TIntermTyped* pTyped = sequence[0]->getAsTyped();
if (!pTyped) {
return false;
}
TSampler const& sampler = pTyped->getType().getSampler();
if (sampler.isArrayed() && sampler.isShadow()) {
// sampler2DArrayShadow is not supported
return false;
}
// Then add the lod bias to the texture() call
if (sequence.size() == 2) {
// we only have 2 parameters, add the 3rd one
TIntermSymbol* symbol = intermediate->addSymbol(*pIntermSymbolLodBias);
sequence.push_back(symbol);
} else if (sequence.size() == 3) {
// load bias is already specified
TIntermSymbol* symbol = intermediate->addSymbol(*pIntermSymbolLodBias);
TIntermTyped* pAdd = intermediate->addBinaryMath(TOperator::EOpAdd,
sequence[2]->getAsTyped(), symbol,
node->getLoc());
sequence[2] = pAdd;
}
return false;
});
root->traverse(&addLodBiasToTextureCalls);
}
} // namespace ASTHelpers

View File

@@ -71,5 +71,10 @@ struct FunctionParameter {
void getFunctionParameters(glslang::TIntermAggregate* func, std::vector<FunctionParameter>& output)
noexcept;
// add lod bias to texture() calls
void textureLodBias(glslang::TIntermediate* intermediate, TIntermNode* root,
const char* lodBiasSymbolName);
} // namespace ASTutils
#endif //TNT_SCAHELPERS_H_H

View File

@@ -359,4 +359,10 @@ void GLSLTools::prepareShaderParser(MaterialBuilder::TargetApi targetApi, glslan
}
}
void GLSLTools::textureLodBias(TShader& shader) {
TIntermediate* intermediate = shader.getIntermediate();
TIntermNode* root = intermediate->getTreeRoot();
ASTUtils::textureLodBias(intermediate, root, "filament_lodBias");
}
} // namespace filamat

View File

@@ -150,6 +150,8 @@ public:
static void prepareShaderParser(MaterialBuilder::TargetApi targetApi, glslang::TShader& shader,
EShLanguage language, int version, MaterialBuilder::Optimization optimization);
static void textureLodBias(glslang::TShader& shader);
private:

View File

@@ -434,6 +434,8 @@ std::string ShaderGenerator::createFragmentProgram(filament::backend::ShaderMode
material.samplerBindings.getBlockOffset(BindingPoints::PER_MATERIAL_INSTANCE),
material.sib);
fs << "float filament_lodBias;\n";
// shading code
cg.generateCommon(fs, ShaderType::FRAGMENT);
cg.generateGetters(fs, ShaderType::FRAGMENT);

View File

@@ -7,6 +7,8 @@ layout(location = 0) out vec4 fragColor;
//------------------------------------------------------------------------------
void main() {
filament_lodBias = frameUniforms.lodBias;
#if defined(BLEND_MODE_MASKED) || (defined(BLEND_MODE_TRANSPARENT) && defined(HAS_TRANSPARENT_SHADOW))
MaterialInputs inputs;
initMaterial(inputs);

View File

@@ -17,6 +17,8 @@ void blendPostLightingColor(const MaterialInputs material, inout vec4 color) {
#endif
void main() {
filament_lodBias = frameUniforms.lodBias;
// See shading_parameters.fs
// Computes global variables we need to evaluate material and lighting
computeShadingParams();