Subpasses should not be used anyways due to the result of WebGPUDriver::isFrameBufferFetchSupported. However, we need to make sure the GLSL is not generated for WebGPU, or Tint is unable to convert.
863 lines
36 KiB
C++
863 lines
36 KiB
C++
/*
|
|
* Copyright (C) 2017 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "ShaderGenerator.h"
|
|
|
|
#include "CodeGenerator.h"
|
|
#include "SibGenerator.h"
|
|
#include "UibGenerator.h"
|
|
|
|
#include <filament/MaterialEnums.h>
|
|
|
|
#include <private/filament/DescriptorSets.h>
|
|
#include <private/filament/EngineEnums.h>
|
|
#include <private/filament/Variant.h>
|
|
|
|
#include <filamat/MaterialBuilder.h>
|
|
|
|
#include <backend/DriverEnums.h>
|
|
|
|
#include <utils/CString.h>
|
|
#include <utils/debug.h>
|
|
#include <utils/sstream.h>
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
namespace filamat {
|
|
|
|
using namespace filament;
|
|
using namespace filament::backend;
|
|
using namespace utils;
|
|
|
|
void ShaderGenerator::generateSurfaceMaterialVariantDefines(utils::io::sstream& out,
|
|
ShaderStage stage, MaterialBuilder::FeatureLevel featureLevel,
|
|
MaterialInfo const& material, filament::Variant variant) noexcept {
|
|
|
|
bool const litVariants = material.isLit || material.hasShadowMultiplier;
|
|
|
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_DIRECTIONAL_LIGHTING",
|
|
litVariants && variant.hasDirectionalLighting());
|
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_DYNAMIC_LIGHTING",
|
|
litVariants && variant.hasDynamicLighting());
|
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_SHADOWING",
|
|
litVariants && filament::Variant::isShadowReceiverVariant(variant));
|
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
|
|
filament::Variant::isVSMVariant(variant));
|
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO",
|
|
hasStereo(variant, featureLevel));
|
|
|
|
switch (stage) {
|
|
case ShaderStage::VERTEX:
|
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_SKINNING_OR_MORPHING",
|
|
hasSkinningOrMorphing(variant, featureLevel));
|
|
break;
|
|
case ShaderStage::FRAGMENT:
|
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_FOG",
|
|
filament::Variant::isFogVariant(variant));
|
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_PICKING",
|
|
filament::Variant::isPickingVariant(variant));
|
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_SSR",
|
|
filament::Variant::isSSRVariant(variant));
|
|
break;
|
|
case ShaderStage::COMPUTE:
|
|
break;
|
|
}
|
|
|
|
out << '\n';
|
|
CodeGenerator::generateDefine(out, "MATERIAL_FEATURE_LEVEL", uint32_t(featureLevel));
|
|
|
|
CodeGenerator::generateDefine(out, "MATERIAL_HAS_SHADOW_MULTIPLIER",
|
|
material.hasShadowMultiplier);
|
|
|
|
CodeGenerator::generateDefine(out, "MATERIAL_HAS_INSTANCES", material.instanced);
|
|
|
|
CodeGenerator::generateDefine(out, "MATERIAL_HAS_VERTEX_DOMAIN_DEVICE_JITTERED",
|
|
material.vertexDomainDeviceJittered);
|
|
|
|
CodeGenerator::generateDefine(out, "MATERIAL_HAS_TRANSPARENT_SHADOW",
|
|
material.hasTransparentShadow);
|
|
|
|
if (stage == ShaderStage::FRAGMENT) {
|
|
// We only support both screen-space refractions and reflections at the same time.
|
|
// And the MATERIAL_HAS_REFRACTION/MATERIAL_HAS_REFLECTIONS defines signify if
|
|
// refraction/reflections are supported by the material.
|
|
|
|
CodeGenerator::generateDefine(out, "MATERIAL_HAS_REFRACTION",
|
|
material.refractionMode != RefractionMode::NONE);
|
|
if (material.refractionMode != RefractionMode::NONE) {
|
|
CodeGenerator::generateDefine(out, "REFRACTION_MODE_CUBEMAP",
|
|
uint32_t(RefractionMode::CUBEMAP));
|
|
CodeGenerator::generateDefine(out, "REFRACTION_MODE_SCREEN_SPACE",
|
|
uint32_t(RefractionMode::SCREEN_SPACE));
|
|
switch (material.refractionMode) {
|
|
case RefractionMode::NONE:
|
|
// can't be here
|
|
break;
|
|
case RefractionMode::CUBEMAP:
|
|
CodeGenerator::generateDefine(out, "REFRACTION_MODE",
|
|
"REFRACTION_MODE_CUBEMAP");
|
|
break;
|
|
case RefractionMode::SCREEN_SPACE:
|
|
CodeGenerator::generateDefine(out, "REFRACTION_MODE",
|
|
"REFRACTION_MODE_SCREEN_SPACE");
|
|
break;
|
|
}
|
|
CodeGenerator::generateDefine(out, "REFRACTION_TYPE_SOLID",
|
|
uint32_t(RefractionType::SOLID));
|
|
CodeGenerator::generateDefine(out, "REFRACTION_TYPE_THIN",
|
|
uint32_t(RefractionType::THIN));
|
|
switch (material.refractionType) {
|
|
case RefractionType::SOLID:
|
|
CodeGenerator::generateDefine(out, "REFRACTION_TYPE", "REFRACTION_TYPE_SOLID");
|
|
break;
|
|
case RefractionType::THIN:
|
|
CodeGenerator::generateDefine(out, "REFRACTION_TYPE", "REFRACTION_TYPE_THIN");
|
|
break;
|
|
}
|
|
}
|
|
CodeGenerator::generateDefine(out, "MATERIAL_HAS_REFLECTIONS",
|
|
material.reflectionMode == ReflectionMode::SCREEN_SPACE);
|
|
|
|
CodeGenerator::generateDefine(out, "MATERIAL_HAS_DOUBLE_SIDED_CAPABILITY",
|
|
material.hasDoubleSidedCapability);
|
|
|
|
CodeGenerator::generateDefine(out, "MATERIAL_HAS_CUSTOM_SURFACE_SHADING",
|
|
material.hasCustomSurfaceShading);
|
|
|
|
out << '\n';
|
|
switch (material.blendingMode) {
|
|
case BlendingMode::OPAQUE:
|
|
CodeGenerator::generateDefine(out, "BLEND_MODE_OPAQUE", true);
|
|
break;
|
|
case BlendingMode::TRANSPARENT:
|
|
CodeGenerator::generateDefine(out, "BLEND_MODE_TRANSPARENT", true);
|
|
break;
|
|
case BlendingMode::ADD:
|
|
CodeGenerator::generateDefine(out, "BLEND_MODE_ADD", true);
|
|
break;
|
|
case BlendingMode::MASKED:
|
|
CodeGenerator::generateDefine(out, "BLEND_MODE_MASKED", true);
|
|
break;
|
|
case BlendingMode::FADE:
|
|
// Fade is a special case of transparent
|
|
CodeGenerator::generateDefine(out, "BLEND_MODE_TRANSPARENT", true);
|
|
CodeGenerator::generateDefine(out, "BLEND_MODE_FADE", true);
|
|
break;
|
|
case BlendingMode::MULTIPLY:
|
|
CodeGenerator::generateDefine(out, "BLEND_MODE_MULTIPLY", true);
|
|
break;
|
|
case BlendingMode::SCREEN:
|
|
CodeGenerator::generateDefine(out, "BLEND_MODE_SCREEN", true);
|
|
break;
|
|
case BlendingMode::CUSTOM:
|
|
CodeGenerator::generateDefine(out, "BLEND_MODE_CUSTOM", true);
|
|
break;
|
|
}
|
|
|
|
switch (material.postLightingBlendingMode) {
|
|
case BlendingMode::OPAQUE:
|
|
CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_OPAQUE", true);
|
|
break;
|
|
case BlendingMode::TRANSPARENT:
|
|
CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_TRANSPARENT", true);
|
|
break;
|
|
case BlendingMode::ADD:
|
|
CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_ADD", true);
|
|
break;
|
|
case BlendingMode::MULTIPLY:
|
|
CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_MULTIPLY", true);
|
|
break;
|
|
case BlendingMode::SCREEN:
|
|
CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_SCREEN", true);
|
|
break;
|
|
case BlendingMode::CUSTOM:
|
|
CodeGenerator::generateDefine(out, "POST_LIGHTING_BLEND_MODE_CUSTOM", true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
out << '\n';
|
|
CodeGenerator::generateDefine(out, "GEOMETRIC_SPECULAR_AA",
|
|
material.specularAntiAliasing && material.isLit);
|
|
|
|
CodeGenerator::generateDefine(out, "CLEAR_COAT_IOR_CHANGE",
|
|
material.clearCoatIorChange);
|
|
}
|
|
}
|
|
|
|
void ShaderGenerator::generateSurfaceMaterialVariantProperties(io::sstream& out,
|
|
MaterialBuilder::PropertyList const properties,
|
|
const MaterialBuilder::PreprocessorDefineList& defines) noexcept {
|
|
|
|
UTILS_NOUNROLL
|
|
for (size_t i = 0; i < MaterialBuilder::MATERIAL_PROPERTIES_COUNT; i++) {
|
|
CodeGenerator::generateMaterialProperty(out,
|
|
static_cast<MaterialBuilder::Property>(i), properties[i]);
|
|
}
|
|
|
|
// synthetic defines
|
|
bool const hasTBN =
|
|
properties[static_cast<int>(MaterialBuilder::Property::ANISOTROPY)] ||
|
|
properties[static_cast<int>(MaterialBuilder::Property::NORMAL)] ||
|
|
properties[static_cast<int>(MaterialBuilder::Property::BENT_NORMAL)] ||
|
|
properties[static_cast<int>(MaterialBuilder::Property::CLEAR_COAT_NORMAL)];
|
|
|
|
CodeGenerator::generateDefine(out, "MATERIAL_NEEDS_TBN", hasTBN);
|
|
|
|
// Additional, user-provided defines.
|
|
for (const auto& define : defines) {
|
|
CodeGenerator::generateDefine(out, define.name.c_str(), define.value.c_str());
|
|
}
|
|
}
|
|
|
|
void ShaderGenerator::generateVertexDomainDefines(io::sstream& vs, VertexDomain domain) noexcept {
|
|
switch (domain) {
|
|
case VertexDomain::OBJECT:
|
|
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_OBJECT", true);
|
|
break;
|
|
case VertexDomain::WORLD:
|
|
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_WORLD", true);
|
|
break;
|
|
case VertexDomain::VIEW:
|
|
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_VIEW", true);
|
|
break;
|
|
case VertexDomain::DEVICE:
|
|
CodeGenerator::generateDefine(vs, "VERTEX_DOMAIN_DEVICE", true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ShaderGenerator::generatePostProcessMaterialVariantDefines(io::sstream& out,
|
|
PostProcessVariant variant) noexcept {
|
|
switch (variant) {
|
|
case PostProcessVariant::OPAQUE:
|
|
CodeGenerator::generateDefine(out, "POST_PROCESS_OPAQUE", 1u);
|
|
break;
|
|
case PostProcessVariant::TRANSLUCENT:
|
|
CodeGenerator::generateDefine(out, "POST_PROCESS_OPAQUE", 0u);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ShaderGenerator::appendShader(io::sstream& ss,
|
|
const CString& shader, size_t lineOffset) noexcept {
|
|
|
|
auto countLines = [](const char* s) -> size_t {
|
|
size_t lines = 0;
|
|
size_t i = 0;
|
|
while (s[i]) {
|
|
if (s[i++] == '\n') lines++;
|
|
}
|
|
return lines;
|
|
};
|
|
|
|
if (!shader.empty()) {
|
|
size_t lines = countLines(ss.c_str());
|
|
ss << "#line " << lineOffset + 1 << '\n';
|
|
ss << shader.c_str();
|
|
if (shader[shader.size() - 1] != '\n') {
|
|
ss << "\n";
|
|
lines++;
|
|
}
|
|
// + 2 to account for the #line directives we just added
|
|
ss << "#line " << lines + countLines(shader.c_str()) + 2 << "\n";
|
|
}
|
|
}
|
|
|
|
void ShaderGenerator::generateUserSpecConstants(
|
|
const CodeGenerator& cg, io::sstream& fs, MaterialBuilder::ConstantList const& constants) {
|
|
// Constants 0 to CONFIG_MAX_RESERVED_SPEC_CONSTANTS - 1 are reserved by Filament.
|
|
size_t index = CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
|
|
for (const auto& constant : constants) {
|
|
std::string const fullName = std::string("materialConstants_") + constant.name.c_str();
|
|
switch (constant.type) {
|
|
case ConstantType::INT:
|
|
cg.generateSpecializationConstant(
|
|
fs, fullName.c_str(), index++, constant.defaultValue.i);
|
|
break;
|
|
case ConstantType::FLOAT:
|
|
cg.generateSpecializationConstant(
|
|
fs, fullName.c_str(), index++, constant.defaultValue.f);
|
|
break;
|
|
case ConstantType::BOOL:
|
|
cg.generateSpecializationConstant(
|
|
fs, fullName.c_str(), index++, constant.defaultValue.b);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
ShaderGenerator::ShaderGenerator(
|
|
MaterialBuilder::PropertyList const& properties,
|
|
MaterialBuilder::VariableList const& variables,
|
|
MaterialBuilder::OutputList const& outputs,
|
|
MaterialBuilder::PreprocessorDefineList const& defines,
|
|
MaterialBuilder::ConstantList const& constants,
|
|
MaterialBuilder::PushConstantList const& pushConstants,
|
|
CString const& materialCode, size_t lineOffset,
|
|
CString const& materialVertexCode, size_t vertexLineOffset,
|
|
MaterialBuilder::MaterialDomain materialDomain) noexcept {
|
|
|
|
if (materialDomain == MaterialBuilder::MaterialDomain::COMPUTE) {
|
|
// we shouldn't have a vertex shader in a compute material
|
|
assert_invariant(materialVertexCode.empty());
|
|
}
|
|
|
|
std::copy(std::begin(properties), std::end(properties), std::begin(mProperties));
|
|
std::copy(std::begin(variables), std::end(variables), std::begin(mVariables));
|
|
std::copy(std::begin(outputs), std::end(outputs), std::back_inserter(mOutputs));
|
|
|
|
mMaterialFragmentCode = materialCode;
|
|
mMaterialVertexCode = materialVertexCode;
|
|
mIsMaterialVertexShaderEmpty = materialVertexCode.empty();
|
|
mMaterialLineOffset = lineOffset;
|
|
mMaterialVertexLineOffset = vertexLineOffset;
|
|
mMaterialDomain = materialDomain;
|
|
mDefines = defines;
|
|
mConstants = constants;
|
|
mPushConstants = pushConstants;
|
|
|
|
if (mMaterialFragmentCode.empty()) {
|
|
if (mMaterialDomain == MaterialBuilder::MaterialDomain::SURFACE) {
|
|
mMaterialFragmentCode =
|
|
CString("void material(inout MaterialInputs m) {\n prepareMaterial(m);\n}\n");
|
|
} else if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
|
mMaterialFragmentCode =
|
|
CString("void postProcess(inout PostProcessInputs p) {\n}\n");
|
|
} else if (mMaterialDomain == MaterialBuilder::MaterialDomain::COMPUTE) {
|
|
mMaterialFragmentCode =
|
|
CString("void compute() {\n}\n");
|
|
}
|
|
}
|
|
if (mMaterialVertexCode.empty()) {
|
|
if (mMaterialDomain == MaterialBuilder::MaterialDomain::SURFACE) {
|
|
mMaterialVertexCode =
|
|
CString("void materialVertex(inout MaterialVertexInputs m) {\n}\n");
|
|
} else if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
|
mMaterialVertexCode =
|
|
CString("void postProcessVertex(inout PostProcessVertexInputs m) {\n}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ShaderGenerator::fixupExternalSamplers(ShaderModel sm, std::string& shader,
|
|
MaterialBuilder::FeatureLevel featureLevel,
|
|
MaterialInfo const& material) noexcept {
|
|
// External samplers are only supported on GL ES at the moment, we must
|
|
// skip the fixup on desktop targets
|
|
if (material.hasExternalSamplers && sm == ShaderModel::MOBILE) {
|
|
CodeGenerator::fixupExternalSamplers(shader, material.sib, featureLevel);
|
|
}
|
|
}
|
|
|
|
std::string ShaderGenerator::createSurfaceVertexProgram(ShaderModel shaderModel,
|
|
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
|
MaterialBuilder::FeatureLevel featureLevel,
|
|
MaterialInfo const& material, const filament::Variant variant, Interpolation interpolation,
|
|
VertexDomain vertexDomain) const noexcept {
|
|
|
|
assert_invariant(filament::Variant::isValid(variant));
|
|
assert_invariant(mMaterialDomain != MaterialBuilder::MaterialDomain::COMPUTE);
|
|
|
|
if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
|
return createPostProcessVertexProgram(
|
|
shaderModel, targetApi,
|
|
targetLanguage, featureLevel, material, variant.key);
|
|
}
|
|
|
|
io::sstream vs;
|
|
|
|
const CodeGenerator cg(shaderModel, targetApi, targetLanguage, featureLevel);
|
|
|
|
cg.generateCommonProlog(vs, ShaderStage::VERTEX, material, variant);
|
|
|
|
generateUserSpecConstants(cg, vs, mConstants);
|
|
|
|
// note: even if the user vertex shader is empty, we can't use the "optimized" version if
|
|
// we're in masked mode because fragment shader needs the color varyings
|
|
const bool useOptimizedDepthVertexShader =
|
|
// must be a depth variant
|
|
filament::Variant::isValidDepthVariant(variant) &&
|
|
// must have an empty vertex shader
|
|
mIsMaterialVertexShaderEmpty &&
|
|
// but must not be MASKED mode
|
|
material.blendingMode != BlendingMode::MASKED &&
|
|
// and must not have transparent shadows
|
|
!(material.hasTransparentShadow &&
|
|
(material.blendingMode == BlendingMode::TRANSPARENT ||
|
|
material.blendingMode == BlendingMode::FADE));
|
|
|
|
CodeGenerator::generateDefine(vs, "USE_OPTIMIZED_DEPTH_VERTEX_SHADER",
|
|
useOptimizedDepthVertexShader);
|
|
|
|
generateSurfaceMaterialVariantDefines(vs, ShaderStage::VERTEX, featureLevel, material, variant);
|
|
|
|
generateSurfaceMaterialVariantProperties(vs, mProperties, mDefines);
|
|
|
|
AttributeBitset attributes = material.requiredAttributes;
|
|
if (hasSkinningOrMorphing(variant, featureLevel)) {
|
|
attributes.set(VertexAttribute::BONE_INDICES);
|
|
attributes.set(VertexAttribute::BONE_WEIGHTS);
|
|
if (material.useLegacyMorphing) {
|
|
attributes.set(VertexAttribute::MORPH_POSITION_0);
|
|
attributes.set(VertexAttribute::MORPH_POSITION_1);
|
|
attributes.set(VertexAttribute::MORPH_POSITION_2);
|
|
attributes.set(VertexAttribute::MORPH_POSITION_3);
|
|
attributes.set(VertexAttribute::MORPH_TANGENTS_0);
|
|
attributes.set(VertexAttribute::MORPH_TANGENTS_1);
|
|
attributes.set(VertexAttribute::MORPH_TANGENTS_2);
|
|
attributes.set(VertexAttribute::MORPH_TANGENTS_3);
|
|
}
|
|
}
|
|
|
|
MaterialBuilder::PushConstantList vertexPushConstants;
|
|
std::copy_if(mPushConstants.begin(), mPushConstants.end(),
|
|
std::back_insert_iterator<MaterialBuilder::PushConstantList>(vertexPushConstants),
|
|
[](MaterialBuilder::PushConstant const& constant) {
|
|
return constant.stage == ShaderStage::VERTEX;
|
|
});
|
|
cg.generateSurfaceShaderInputs(vs, ShaderStage::VERTEX, attributes, interpolation,
|
|
vertexPushConstants);
|
|
|
|
CodeGenerator::generateSurfaceTypes(vs, ShaderStage::VERTEX);
|
|
|
|
// custom material variables
|
|
size_t variableIndex = 0;
|
|
for (const auto& variable : mVariables) {
|
|
CodeGenerator::generateCommonVariable(vs, ShaderStage::VERTEX, variable, variableIndex++);
|
|
}
|
|
|
|
// materials defines
|
|
generateVertexDomainDefines(vs, vertexDomain);
|
|
|
|
// uniforms
|
|
cg.generateUniforms(vs, ShaderStage::VERTEX,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::FRAME_UNIFORMS,
|
|
UibGenerator::getPerViewUib());
|
|
|
|
cg.generateUniforms(vs, ShaderStage::VERTEX,
|
|
DescriptorSetBindingPoints::PER_RENDERABLE,
|
|
+PerRenderableBindingPoints::OBJECT_UNIFORMS,
|
|
UibGenerator::getPerRenderableUib());
|
|
|
|
const bool litVariants = material.isLit || material.hasShadowMultiplier;
|
|
if (litVariants && filament::Variant::isShadowReceiverVariant(variant)) {
|
|
cg.generateUniforms(vs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::SHADOWS,
|
|
UibGenerator::getShadowUib());
|
|
}
|
|
|
|
if (hasSkinningOrMorphing(variant, featureLevel)) {
|
|
cg.generateUniforms(vs, ShaderStage::VERTEX,
|
|
DescriptorSetBindingPoints::PER_RENDERABLE,
|
|
+PerRenderableBindingPoints::BONES_UNIFORMS,
|
|
UibGenerator::getPerRenderableBonesUib());
|
|
cg.generateUniforms(vs, ShaderStage::VERTEX,
|
|
DescriptorSetBindingPoints::PER_RENDERABLE,
|
|
+PerRenderableBindingPoints::MORPHING_UNIFORMS,
|
|
UibGenerator::getPerRenderableMorphingUib());
|
|
cg.generateCommonSamplers(vs, DescriptorSetBindingPoints::PER_RENDERABLE,
|
|
SibGenerator::getPerRenderableSib(variant));
|
|
}
|
|
|
|
cg.generateUniforms(vs, ShaderStage::VERTEX,
|
|
DescriptorSetBindingPoints::PER_MATERIAL,
|
|
+PerMaterialBindingPoints::MATERIAL_PARAMS,
|
|
material.uib);
|
|
|
|
CodeGenerator::generateSeparator(vs);
|
|
|
|
cg.generateCommonSamplers(vs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib);
|
|
|
|
// shader code
|
|
CodeGenerator::generateSurfaceCommon(vs, ShaderStage::VERTEX);
|
|
CodeGenerator::generateSurfaceGetters(vs, ShaderStage::VERTEX);
|
|
CodeGenerator::generateSurfaceMaterial(vs, ShaderStage::VERTEX);
|
|
|
|
// main entry point
|
|
appendShader(vs, mMaterialVertexCode, mMaterialVertexLineOffset);
|
|
CodeGenerator::generateSurfaceMain(vs, ShaderStage::VERTEX);
|
|
|
|
CodeGenerator::generateCommonEpilog(vs);
|
|
|
|
return vs.c_str();
|
|
}
|
|
|
|
std::string ShaderGenerator::createSurfaceFragmentProgram(ShaderModel shaderModel,
|
|
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
|
MaterialBuilder::FeatureLevel featureLevel,
|
|
MaterialInfo const& material, const filament::Variant variant,
|
|
Interpolation interpolation, UserVariantFilterMask variantFilter) const noexcept {
|
|
|
|
assert_invariant(filament::Variant::isValid(variant));
|
|
assert_invariant(mMaterialDomain != MaterialBuilder::MaterialDomain::COMPUTE);
|
|
|
|
if (mMaterialDomain == MaterialBuilder::MaterialDomain::POST_PROCESS) {
|
|
return createPostProcessFragmentProgram(shaderModel, targetApi, targetLanguage,
|
|
featureLevel, material, variant.key);
|
|
}
|
|
|
|
const CodeGenerator cg(shaderModel, targetApi, targetLanguage, featureLevel);
|
|
|
|
io::sstream fs;
|
|
cg.generateCommonProlog(fs, ShaderStage::FRAGMENT, material, variant);
|
|
|
|
generateUserSpecConstants(cg, fs, mConstants);
|
|
|
|
generateSurfaceMaterialVariantDefines(
|
|
fs, ShaderStage::FRAGMENT, featureLevel, material, variant);
|
|
|
|
auto defaultSpecularAO = shaderModel == ShaderModel::MOBILE ?
|
|
SpecularAmbientOcclusion::NONE : SpecularAmbientOcclusion::SIMPLE;
|
|
auto specularAO = material.specularAOSet ? material.specularAO : defaultSpecularAO;
|
|
CodeGenerator::generateDefine(fs, "SPECULAR_AMBIENT_OCCLUSION", uint32_t(specularAO));
|
|
|
|
bool const multiBounceAO = material.multiBounceAOSet ?
|
|
material.multiBounceAO : shaderModel == ShaderModel::DESKTOP;
|
|
CodeGenerator::generateDefine(fs, "MULTI_BOUNCE_AMBIENT_OCCLUSION", multiBounceAO ? 1u : 0u);
|
|
|
|
generateSurfaceMaterialVariantProperties(fs, mProperties, mDefines);
|
|
|
|
MaterialBuilder::PushConstantList fragmentPushConstants;
|
|
std::copy_if(mPushConstants.begin(), mPushConstants.end(),
|
|
std::back_insert_iterator<MaterialBuilder::PushConstantList>(fragmentPushConstants),
|
|
[](MaterialBuilder::PushConstant const& constant) {
|
|
return constant.stage == ShaderStage::FRAGMENT;
|
|
});
|
|
cg.generateSurfaceShaderInputs(fs, ShaderStage::FRAGMENT, material.requiredAttributes, interpolation,
|
|
fragmentPushConstants);
|
|
|
|
CodeGenerator::generateSurfaceTypes(fs, ShaderStage::FRAGMENT);
|
|
|
|
// custom material variables
|
|
size_t variableIndex = 0;
|
|
for (const auto& variable : mVariables) {
|
|
CodeGenerator::generateCommonVariable(fs, ShaderStage::FRAGMENT, variable, variableIndex++);
|
|
}
|
|
|
|
// uniforms and samplers
|
|
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::FRAME_UNIFORMS,
|
|
UibGenerator::getPerViewUib());
|
|
|
|
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_RENDERABLE,
|
|
+PerRenderableBindingPoints::OBJECT_UNIFORMS,
|
|
UibGenerator::getPerRenderableUib());
|
|
|
|
if (variant.hasDynamicLighting()) {
|
|
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::LIGHTS,
|
|
UibGenerator::getLightsUib());
|
|
}
|
|
|
|
bool const litVariants = material.isLit || material.hasShadowMultiplier;
|
|
if (litVariants && filament::Variant::isShadowReceiverVariant(variant)) {
|
|
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::SHADOWS,
|
|
UibGenerator::getShadowUib());
|
|
}
|
|
|
|
if (variant.hasDynamicLighting()) {
|
|
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::RECORD_BUFFER,
|
|
UibGenerator::getFroxelRecordUib());
|
|
|
|
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::FROXEL_BUFFER,
|
|
UibGenerator::getFroxelsUib());
|
|
}
|
|
|
|
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_MATERIAL,
|
|
+PerMaterialBindingPoints::MATERIAL_PARAMS,
|
|
material.uib);
|
|
|
|
CodeGenerator::generateSeparator(fs);
|
|
|
|
if (featureLevel >= FeatureLevel::FEATURE_LEVEL_1) {
|
|
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
|
|
|
|
auto const perViewDescriptorSetLayout = getPerViewDescriptorSetLayoutWithVariant(
|
|
variant, variantFilter,
|
|
material.isLit || material.hasShadowMultiplier,
|
|
material.reflectionMode, material.refractionMode);
|
|
|
|
// this is the list of samplers we need to filter
|
|
auto list = SibGenerator::getPerViewSib(variant).getSamplerInfoList();
|
|
|
|
// remove all the samplers that are not included in the descriptor-set layout
|
|
list.erase(
|
|
std::remove_if(list.begin(), list.end(),
|
|
[&perViewDescriptorSetLayout](auto const& entry) {
|
|
auto pos = std::find_if(
|
|
perViewDescriptorSetLayout.bindings.begin(),
|
|
perViewDescriptorSetLayout.bindings.end(),
|
|
[&entry](const auto& item) {
|
|
return item.binding == entry.binding;
|
|
});
|
|
return pos == perViewDescriptorSetLayout.bindings.end();
|
|
}), list.end());
|
|
|
|
cg.generateCommonSamplers(fs, DescriptorSetBindingPoints::PER_VIEW, list);
|
|
}
|
|
|
|
cg.generateCommonSamplers(fs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib);
|
|
|
|
fs << "float filament_lodBias;\n";
|
|
|
|
// shading code
|
|
CodeGenerator::generateSurfaceCommon(fs, ShaderStage::FRAGMENT);
|
|
CodeGenerator::generateSurfaceGetters(fs, ShaderStage::FRAGMENT);
|
|
CodeGenerator::generateSurfaceMaterial(fs, ShaderStage::FRAGMENT);
|
|
CodeGenerator::generateSurfaceParameters(fs, ShaderStage::FRAGMENT);
|
|
|
|
if (filament::Variant::isFogVariant(variant)) {
|
|
CodeGenerator::generateSurfaceFog(fs, ShaderStage::FRAGMENT);
|
|
}
|
|
|
|
// shading model
|
|
if (filament::Variant::isValidDepthVariant(variant)) {
|
|
// In MASKED mode or with transparent shadows, we need the alpha channel computed by
|
|
// the material (user code), so we append it here.
|
|
if (material.userMaterialHasCustomDepth ||
|
|
material.blendingMode == BlendingMode::MASKED || ((
|
|
material.blendingMode == BlendingMode::TRANSPARENT ||
|
|
material.blendingMode == BlendingMode::FADE)
|
|
&& material.hasTransparentShadow)) {
|
|
appendShader(fs, mMaterialFragmentCode, mMaterialLineOffset);
|
|
}
|
|
// These variants are special and are treated as DEPTH variants. Filament will never
|
|
// request that variant for the color pass.
|
|
CodeGenerator::generateSurfaceDepthMain(fs, ShaderStage::FRAGMENT);
|
|
} else {
|
|
appendShader(fs, mMaterialFragmentCode, mMaterialLineOffset);
|
|
if (material.isLit) {
|
|
if (filament::Variant::isSSRVariant(variant)) {
|
|
CodeGenerator::generateSurfaceReflections(fs, ShaderStage::FRAGMENT);
|
|
} else {
|
|
CodeGenerator::generateSurfaceLit(fs, ShaderStage::FRAGMENT, variant,
|
|
material.shading,material.hasCustomSurfaceShading);
|
|
}
|
|
} else {
|
|
CodeGenerator::generateSurfaceUnlit(fs, ShaderStage::FRAGMENT, variant,
|
|
material.hasShadowMultiplier);
|
|
}
|
|
// entry point
|
|
CodeGenerator::generateSurfaceMain(fs, ShaderStage::FRAGMENT);
|
|
}
|
|
|
|
CodeGenerator::generateCommonEpilog(fs);
|
|
|
|
return fs.c_str();
|
|
}
|
|
|
|
std::string ShaderGenerator::createSurfaceComputeProgram(filament::backend::ShaderModel shaderModel,
|
|
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
|
MaterialBuilder::FeatureLevel featureLevel,
|
|
MaterialInfo const& material) const noexcept {
|
|
assert_invariant(mMaterialDomain == MaterialBuilder::MaterialDomain::COMPUTE);
|
|
assert_invariant(featureLevel >= FeatureLevel::FEATURE_LEVEL_2);
|
|
const CodeGenerator cg(shaderModel, targetApi, targetLanguage, featureLevel);
|
|
io::sstream s;
|
|
|
|
cg.generateCommonProlog(s, ShaderStage::COMPUTE, material, {});
|
|
|
|
generateUserSpecConstants(cg, s, mConstants);
|
|
|
|
CodeGenerator::generateSurfaceTypes(s, ShaderStage::COMPUTE);
|
|
|
|
cg.generateUniforms(s, ShaderStage::COMPUTE,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::FRAME_UNIFORMS,
|
|
UibGenerator::getPerViewUib());
|
|
|
|
cg.generateUniforms(s, ShaderStage::COMPUTE,
|
|
DescriptorSetBindingPoints::PER_MATERIAL,
|
|
+PerMaterialBindingPoints::MATERIAL_PARAMS,
|
|
material.uib);
|
|
|
|
cg.generateCommonSamplers(s, DescriptorSetBindingPoints::PER_MATERIAL, material.sib);
|
|
|
|
// generate SSBO
|
|
cg.generateBuffers(s, material.buffers);
|
|
|
|
// TODO: generate images
|
|
|
|
CodeGenerator::generateSurfaceCommon(s, ShaderStage::COMPUTE);
|
|
CodeGenerator::generateSurfaceGetters(s, ShaderStage::COMPUTE);
|
|
|
|
appendShader(s, mMaterialFragmentCode, mMaterialLineOffset);
|
|
|
|
CodeGenerator::generateSurfaceMain(s, ShaderStage::COMPUTE);
|
|
|
|
CodeGenerator::generateCommonEpilog(s);
|
|
return s.c_str();
|
|
}
|
|
|
|
std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel sm,
|
|
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
|
MaterialBuilder::FeatureLevel featureLevel,
|
|
MaterialInfo const& material, const filament::Variant::type_t variantKey) const noexcept {
|
|
const CodeGenerator cg(sm, targetApi, targetLanguage, featureLevel);
|
|
io::sstream vs;
|
|
cg.generateCommonProlog(vs, ShaderStage::VERTEX, material, {});
|
|
|
|
generateUserSpecConstants(cg, vs, mConstants);
|
|
|
|
CodeGenerator::generateDefine(vs, "LOCATION_POSITION", uint32_t(VertexAttribute::POSITION));
|
|
|
|
// custom material variables
|
|
size_t variableIndex = 0;
|
|
for (const auto& variable : mVariables) {
|
|
CodeGenerator::generateCommonVariable(vs, ShaderStage::VERTEX, variable, variableIndex++);
|
|
}
|
|
|
|
CodeGenerator::generatePostProcessInputs(vs, ShaderStage::VERTEX);
|
|
generatePostProcessMaterialVariantDefines(vs, PostProcessVariant(variantKey));
|
|
|
|
cg.generateUniforms(vs, ShaderStage::VERTEX,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::FRAME_UNIFORMS,
|
|
UibGenerator::getPerViewUib());
|
|
|
|
cg.generateUniforms(vs, ShaderStage::VERTEX,
|
|
DescriptorSetBindingPoints::PER_MATERIAL,
|
|
+PerMaterialBindingPoints::MATERIAL_PARAMS,
|
|
material.uib);
|
|
|
|
cg.generateCommonSamplers(vs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib);
|
|
|
|
CodeGenerator::generatePostProcessCommon(vs, ShaderStage::VERTEX);
|
|
CodeGenerator::generatePostProcessGetters(vs, ShaderStage::VERTEX);
|
|
|
|
appendShader(vs, mMaterialVertexCode, mMaterialVertexLineOffset);
|
|
|
|
CodeGenerator::generatePostProcessMain(vs, ShaderStage::VERTEX);
|
|
|
|
CodeGenerator::generateCommonEpilog(vs);
|
|
return vs.c_str();
|
|
}
|
|
|
|
std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel sm,
|
|
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
|
|
MaterialBuilder::FeatureLevel featureLevel,
|
|
MaterialInfo const& material, uint8_t variant) const noexcept {
|
|
const CodeGenerator cg(sm, targetApi, targetLanguage, featureLevel);
|
|
io::sstream fs;
|
|
cg.generateCommonProlog(fs, ShaderStage::FRAGMENT, material, {});
|
|
|
|
generateUserSpecConstants(cg, fs, mConstants);
|
|
|
|
generatePostProcessMaterialVariantDefines(fs, PostProcessVariant(variant));
|
|
|
|
// custom material variables
|
|
size_t variableIndex = 0;
|
|
for (const auto& variable : mVariables) {
|
|
CodeGenerator::generateCommonVariable(fs, ShaderStage::FRAGMENT, variable, variableIndex++);
|
|
}
|
|
|
|
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_VIEW,
|
|
+PerViewBindingPoints::FRAME_UNIFORMS,
|
|
UibGenerator::getPerViewUib());
|
|
|
|
cg.generateUniforms(fs, ShaderStage::FRAGMENT,
|
|
DescriptorSetBindingPoints::PER_MATERIAL,
|
|
+PerMaterialBindingPoints::MATERIAL_PARAMS,
|
|
material.uib);
|
|
|
|
cg.generateCommonSamplers(fs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib);
|
|
|
|
// Subpasses are not yet in WebGPU, https://github.com/gpuweb/gpuweb/issues/435
|
|
CodeGenerator::generatePostProcessSubpass(fs, targetApi == MaterialBuilderBase::TargetApi::WEBGPU
|
|
? SubpassInfo{}
|
|
: material.subpass);
|
|
|
|
CodeGenerator::generatePostProcessCommon(fs, ShaderStage::FRAGMENT);
|
|
CodeGenerator::generatePostProcessGetters(fs, ShaderStage::FRAGMENT);
|
|
|
|
// Generate post-process outputs.
|
|
for (const auto& output : mOutputs) {
|
|
if (output.target == MaterialBuilder::OutputTarget::COLOR) {
|
|
cg.generateOutput(fs, ShaderStage::FRAGMENT, output.name, output.location,
|
|
output.qualifier, output.precision, output.type);
|
|
}
|
|
if (output.target == MaterialBuilder::OutputTarget::DEPTH) {
|
|
CodeGenerator::generateDefine(fs, "FRAG_OUTPUT_DEPTH", 1u);
|
|
}
|
|
}
|
|
|
|
CodeGenerator::generatePostProcessInputs(fs, ShaderStage::FRAGMENT);
|
|
|
|
appendShader(fs, mMaterialFragmentCode, mMaterialLineOffset);
|
|
|
|
CodeGenerator::generatePostProcessMain(fs, ShaderStage::FRAGMENT);
|
|
CodeGenerator::generateCommonEpilog(fs);
|
|
return fs.c_str();
|
|
}
|
|
|
|
bool ShaderGenerator::hasSkinningOrMorphing(
|
|
filament::Variant variant, MaterialBuilder::FeatureLevel featureLevel) noexcept {
|
|
return variant.hasSkinningOrMorphing()
|
|
// HACK(exv): Ignore skinning/morphing variant when targeting ESSL 1.0. We should
|
|
// either properly support skinning on FL0 or build a system in matc which allows
|
|
// the set of included variants to differ per-feature level.
|
|
&& featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0;
|
|
}
|
|
|
|
bool ShaderGenerator::hasStereo(
|
|
filament::Variant variant, MaterialBuilder::FeatureLevel featureLevel) noexcept {
|
|
return variant.hasStereo()
|
|
// HACK(exv): Ignore stereo variant when targeting ESSL 1.0. We should properly build a
|
|
// system in matc which allows the set of included variants to differ per-feature level.
|
|
&& featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0;
|
|
}
|
|
|
|
backend::DescriptorSetLayout ShaderGenerator::getPerViewDescriptorSetLayoutWithVariant(
|
|
filament::Variant variant,
|
|
UserVariantFilterMask variantFilter,
|
|
bool isLit,
|
|
ReflectionMode reflectionMode,
|
|
RefractionMode refractionMode) {
|
|
if (filament::Variant::isValidDepthVariant(variant)) {
|
|
return descriptor_sets::getDepthVariantLayout();
|
|
}
|
|
if (filament::Variant::isSSRVariant(variant)) {
|
|
return descriptor_sets::getSsrVariantLayout();
|
|
}
|
|
// We need to filter out all the descriptors not included in the "resolved" layout below
|
|
return descriptor_sets::getPerViewDescriptorSetLayout(
|
|
MaterialDomain::SURFACE, variantFilter,
|
|
isLit, reflectionMode, refractionMode);
|
|
}
|
|
|
|
} // namespace filament
|