Until now we were using TARGET_MOBILE in our shaders for both quality setting and correctness. With this PR we introduce a new define FILAMENT_QUALITY which currently can take two value FILAMENT_QUALITY_LOW and FILAMENT_QUALITY_HIGH and we use that instead of TARGET_MOBILE for quality settings. Currently, FILAMENT_QUALITY_LOW is selected when TARGET_MOBILE is set.
720 lines
26 KiB
C++
720 lines
26 KiB
C++
/*
|
|
* Copyright (C) 2015 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 "CodeGenerator.h"
|
|
|
|
#include "generated/shaders.h"
|
|
|
|
#include <cctype>
|
|
#include <iomanip>
|
|
|
|
namespace filamat {
|
|
|
|
// From driverEnum namespace
|
|
using namespace filament;
|
|
using namespace backend;
|
|
using namespace utils;
|
|
|
|
io::sstream& CodeGenerator::generateSeparator(io::sstream& out) const {
|
|
out << '\n';
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateProlog(io::sstream& out, ShaderType type,
|
|
bool hasExternalSamplers) const {
|
|
assert(mShaderModel != ShaderModel::UNKNOWN);
|
|
switch (mShaderModel) {
|
|
case ShaderModel::UNKNOWN:
|
|
break;
|
|
case ShaderModel::GL_ES_30:
|
|
// Vulkan requires version 310 or higher
|
|
if (mTargetLanguage == TargetLanguage::SPIRV) {
|
|
// Vulkan requires layout locations on ins and outs, which were not supported
|
|
// in the OpenGL 4.1 GLSL profile.
|
|
out << "#version 310 es\n\n";
|
|
} else {
|
|
out << "#version 300 es\n\n";
|
|
}
|
|
if (hasExternalSamplers) {
|
|
out << "#extension GL_OES_EGL_image_external_essl3 : require\n\n";
|
|
}
|
|
out << "#define TARGET_MOBILE\n";
|
|
break;
|
|
case ShaderModel::GL_CORE_41:
|
|
if (mTargetLanguage == TargetLanguage::SPIRV) {
|
|
// Vulkan requires binding specifiers on uniforms and samplers, which were not
|
|
// supported in the OpenGL 4.1 GLSL profile.
|
|
out << "#version 450 core\n\n";
|
|
} else {
|
|
out << "#version 410 core\n\n";
|
|
}
|
|
break;
|
|
}
|
|
|
|
// This allows our includer system to use the #line directive to denote the source file for
|
|
// #included code. This way, glslang reports errors more accurately.
|
|
out << "#extension GL_GOOGLE_cpp_style_line_directive : enable\n\n";
|
|
|
|
if (mTargetApi == TargetApi::VULKAN) {
|
|
out << "#define TARGET_VULKAN_ENVIRONMENT\n";
|
|
}
|
|
if (mTargetApi == TargetApi::METAL) {
|
|
out << "#define TARGET_METAL_ENVIRONMENT\n";
|
|
}
|
|
if (mTargetLanguage == TargetLanguage::SPIRV) {
|
|
out << "#define TARGET_LANGUAGE_SPIRV\n";
|
|
}
|
|
|
|
out << '\n';
|
|
out << "#define FILAMENT_QUALITY_LOW 0\n";
|
|
out << "#define FILAMENT_QUALITY_HIGH 1\n";
|
|
out << "#if defined(TARGET_MOBILE)\n";
|
|
out << "#define FILAMENT_QUALITY FILAMENT_QUALITY_LOW\n";
|
|
out << "#else\n";
|
|
out << "#define FILAMENT_QUALITY FILAMENT_QUALITY_HIGH\n";
|
|
out << "#endif\n";
|
|
out << '\n';
|
|
|
|
Precision defaultPrecision = getDefaultPrecision(type);
|
|
const char* precision = getPrecisionQualifier(defaultPrecision, Precision::DEFAULT);
|
|
out << "precision " << precision << " float;\n";
|
|
out << "precision " << precision << " int;\n";
|
|
out << "precision lowp sampler2DArray;\n";
|
|
out << "precision lowp sampler3D;\n";
|
|
|
|
out << SHADERS_COMMON_TYPES_FS_DATA;
|
|
|
|
out << "\n";
|
|
return out;
|
|
}
|
|
|
|
Precision CodeGenerator::getDefaultPrecision(ShaderType type) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
return Precision::HIGH;
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
if (mShaderModel < ShaderModel::GL_CORE_41) {
|
|
return Precision::MEDIUM;
|
|
} else {
|
|
return Precision::HIGH;
|
|
}
|
|
}
|
|
return Precision::HIGH;
|
|
}
|
|
|
|
Precision CodeGenerator::getDefaultUniformPrecision() const {
|
|
if (mShaderModel < ShaderModel::GL_CORE_41) {
|
|
return Precision::MEDIUM;
|
|
} else {
|
|
return Precision::HIGH;
|
|
}
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateEpilog(io::sstream& out) const {
|
|
out << "\n"; // For line compression all shaders finish with a newline character.
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateShaderMain(io::sstream& out, ShaderType type) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
out << SHADERS_MAIN_VS_DATA;
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_MAIN_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generatePostProcessMain(io::sstream& out, ShaderType type) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
out << SHADERS_POST_PROCESS_VS_DATA;
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_POST_PROCESS_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateVariable(io::sstream& out, ShaderType type,
|
|
const CString& name, size_t index) const {
|
|
|
|
if (!name.empty()) {
|
|
if (type == ShaderType::VERTEX) {
|
|
out << "\n#define VARIABLE_CUSTOM" << index << " " << name.c_str() << "\n";
|
|
out << "\n#define VARIABLE_CUSTOM_AT" << index << " variable_" << name.c_str() << "\n";
|
|
out << "LAYOUT_LOCATION(" << index << ") out vec4 variable_" << name.c_str() << ";\n";
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << "\nLAYOUT_LOCATION(" << index << ") in highp vec4 variable_" << name.c_str() << ";\n";
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateShaderInputs(io::sstream& out, ShaderType type,
|
|
const AttributeBitset& attributes, Interpolation interpolation) const {
|
|
|
|
const char* shading = getInterpolationQualifier(interpolation);
|
|
out << "#define SHADING_INTERPOLATION " << shading << "\n";
|
|
|
|
bool hasTangents = attributes.test(VertexAttribute::TANGENTS);
|
|
generateDefine(out, "HAS_ATTRIBUTE_TANGENTS", hasTangents);
|
|
|
|
bool hasColor = attributes.test(VertexAttribute::COLOR);
|
|
generateDefine(out, "HAS_ATTRIBUTE_COLOR", hasColor);
|
|
|
|
bool hasUV0 = attributes.test(VertexAttribute::UV0);
|
|
generateDefine(out, "HAS_ATTRIBUTE_UV0", hasUV0);
|
|
|
|
bool hasUV1 = attributes.test(VertexAttribute::UV1);
|
|
generateDefine(out, "HAS_ATTRIBUTE_UV1", hasUV1);
|
|
|
|
bool hasBoneIndices = attributes.test(VertexAttribute::BONE_INDICES);
|
|
generateDefine(out, "HAS_ATTRIBUTE_BONE_INDICES", hasBoneIndices);
|
|
|
|
bool hasBoneWeights = attributes.test(VertexAttribute::BONE_WEIGHTS);
|
|
generateDefine(out, "HAS_ATTRIBUTE_BONE_WEIGHTS", hasBoneWeights);
|
|
|
|
for (int i = 0; i < MAX_CUSTOM_ATTRIBUTES; i++) {
|
|
bool hasCustom = attributes.test(VertexAttribute::CUSTOM0 + i);
|
|
if (hasCustom) {
|
|
generateIndexedDefine(out, "HAS_ATTRIBUTE_CUSTOM", i, 1);
|
|
}
|
|
}
|
|
|
|
if (type == ShaderType::VERTEX) {
|
|
out << "\n";
|
|
generateDefine(out, "LOCATION_POSITION", uint32_t(VertexAttribute::POSITION));
|
|
if (hasTangents) {
|
|
generateDefine(out, "LOCATION_TANGENTS", uint32_t(VertexAttribute::TANGENTS));
|
|
}
|
|
if (hasUV0) {
|
|
generateDefine(out, "LOCATION_UV0", uint32_t(VertexAttribute::UV0));
|
|
}
|
|
if (hasUV1) {
|
|
generateDefine(out, "LOCATION_UV1", uint32_t(VertexAttribute::UV1));
|
|
}
|
|
if (hasColor) {
|
|
generateDefine(out, "LOCATION_COLOR", uint32_t(VertexAttribute::COLOR));
|
|
}
|
|
if (hasBoneIndices) {
|
|
generateDefine(out, "LOCATION_BONE_INDICES", uint32_t(VertexAttribute::BONE_INDICES));
|
|
}
|
|
if (hasBoneWeights) {
|
|
generateDefine(out, "LOCATION_BONE_WEIGHTS", uint32_t(VertexAttribute::BONE_WEIGHTS));
|
|
}
|
|
|
|
for (int i = 0; i < MAX_CUSTOM_ATTRIBUTES; i++) {
|
|
if (attributes.test(VertexAttribute::CUSTOM0 + i)) {
|
|
generateIndexedDefine(out, "LOCATION_CUSTOM", i,
|
|
uint32_t(VertexAttribute::CUSTOM0) + i);
|
|
}
|
|
}
|
|
|
|
out << SHADERS_INPUTS_VS_DATA;
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_INPUTS_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
utils::io::sstream& CodeGenerator::generateOutput(utils::io::sstream& out, ShaderType type,
|
|
const utils::CString& name, size_t index,
|
|
MaterialBuilder::VariableQualifier qualifier,
|
|
MaterialBuilder::OutputType outputType) const {
|
|
if (name.empty() || type == ShaderType::VERTEX) {
|
|
return out;
|
|
}
|
|
|
|
// TODO: add and support additional variable qualifiers
|
|
(void) qualifier;
|
|
assert(qualifier == MaterialBuilder::VariableQualifier::OUT);
|
|
|
|
const char* typeString = getOutputTypeName(outputType);
|
|
|
|
out << "\n#define FRAG_OUTPUT" << index << " " << name.c_str() << "\n";
|
|
out << "\n#define FRAG_OUTPUT_AT" << index << " output_" << name.c_str() << "\n";
|
|
out << "\n#define FRAG_OUTPUT_TYPE" << index << " " << typeString << "\n";
|
|
out << "layout(location=" << index << ") out " << typeString <<
|
|
" output_" << name.c_str() << ";\n";
|
|
|
|
return out;
|
|
}
|
|
|
|
|
|
io::sstream& CodeGenerator::generateDepthShaderMain(io::sstream& out, ShaderType type) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
out << SHADERS_DEPTH_MAIN_VS_DATA;
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_DEPTH_MAIN_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const char* CodeGenerator::getUniformPrecisionQualifier(UniformType type, Precision precision,
|
|
Precision uniformPrecision, Precision defaultPrecision) const noexcept {
|
|
if (!hasPrecision(type)) {
|
|
return "";
|
|
}
|
|
if (precision == Precision::DEFAULT) {
|
|
precision = uniformPrecision;
|
|
}
|
|
return getPrecisionQualifier(precision, defaultPrecision);
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderType shaderType,
|
|
uint8_t binding, const UniformInterfaceBlock& uib) const {
|
|
auto const& infos = uib.getUniformInfoList();
|
|
if (infos.empty()) {
|
|
return out;
|
|
}
|
|
|
|
const CString& blockName = uib.getName();
|
|
std::string instanceName(uib.getName().c_str());
|
|
instanceName.front() = char(std::tolower((unsigned char)instanceName.front()));
|
|
|
|
Precision uniformPrecision = getDefaultUniformPrecision();
|
|
Precision defaultPrecision = getDefaultPrecision(shaderType);
|
|
|
|
out << "\nlayout(";
|
|
if (mTargetLanguage == TargetLanguage::SPIRV) {
|
|
uint32_t bindingIndex = (uint32_t) binding; // avoid char output
|
|
out << "binding = " << bindingIndex << ", ";
|
|
}
|
|
out << "std140) uniform " << blockName.c_str() << " {\n";
|
|
for (auto const& info : infos) {
|
|
char const* const type = getUniformTypeName(info.type);
|
|
char const* const precision = getUniformPrecisionQualifier(info.type, info.precision,
|
|
uniformPrecision, defaultPrecision);
|
|
out << " " << precision;
|
|
if (precision[0] != '\0') out << " ";
|
|
out << type << " " << info.name.c_str();
|
|
if (info.size > 1) {
|
|
out << "[" << info.size << "]";
|
|
}
|
|
out << ";\n";
|
|
}
|
|
out << "} " << instanceName << ";\n";
|
|
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateSamplers(
|
|
io::sstream& out, uint8_t firstBinding, const SamplerInterfaceBlock& sib) const {
|
|
auto const& infos = sib.getSamplerInfoList();
|
|
if (infos.empty()) {
|
|
return out;
|
|
}
|
|
|
|
for (auto const& info : infos) {
|
|
|
|
CString uniformName =
|
|
SamplerInterfaceBlock::getUniformName(
|
|
sib.getName().c_str(), info.name.c_str());
|
|
|
|
auto type = info.type;
|
|
if (type == SamplerType::SAMPLER_EXTERNAL && mShaderModel != ShaderModel::GL_ES_30) {
|
|
// we're generating the shader for the desktop, where we assume external textures
|
|
// are not supported, in which case we revert to texture2d
|
|
type = SamplerType::SAMPLER_2D;
|
|
}
|
|
char const* const typeName = getSamplerTypeName(type, info.format, info.multisample);
|
|
char const* const precision = getPrecisionQualifier(info.precision, Precision::DEFAULT);
|
|
if (mTargetLanguage == TargetLanguage::SPIRV) {
|
|
const uint32_t bindingIndex = (uint32_t) firstBinding + info.offset;
|
|
out << "layout(binding = " << bindingIndex;
|
|
|
|
// For Vulkan, we place uniforms in set 0 (the default set) and samplers in set 1. This
|
|
// allows the sampler bindings to live in a separate "namespace" that starts at zero.
|
|
// Note that the set specifier is not covered by the desktop GLSL spec, including
|
|
// recent versions. It is only documented in the GL_KHR_vulkan_glsl extension.
|
|
if (mTargetApi == TargetApi::VULKAN) {
|
|
out << ", set = 1";
|
|
}
|
|
|
|
out << ") ";
|
|
}
|
|
out << "uniform " << precision << " " << typeName << " " << uniformName.c_str();
|
|
out << ";\n";
|
|
}
|
|
out << "\n";
|
|
|
|
return out;
|
|
}
|
|
|
|
utils::io::sstream& CodeGenerator::generateSubpass(utils::io::sstream& out,
|
|
SubpassInfo subpass) const {
|
|
if (!subpass.isValid) {
|
|
return out;
|
|
}
|
|
|
|
CString subpassName =
|
|
SamplerInterfaceBlock::getUniformName(subpass.block.c_str(), subpass.name.c_str());
|
|
|
|
char const* const typeName = "subpassInput";
|
|
// In our Vulkan backend, subpass inputs always live in descriptor set 2. (ignored for GLES)
|
|
char const* const precision = getPrecisionQualifier(subpass.precision, Precision::DEFAULT);
|
|
out << "layout(input_attachment_index = " << (int) subpass.attachmentIndex
|
|
<< ", set = 2, binding = " << (int) subpass.binding
|
|
<< ") ";
|
|
out << "uniform " << precision << " " << typeName << " " << subpassName.c_str();
|
|
out << ";\n";
|
|
|
|
out << "\n";
|
|
|
|
return out;
|
|
}
|
|
|
|
void CodeGenerator::fixupExternalSamplers(
|
|
std::string& shader, SamplerInterfaceBlock const& sib) noexcept {
|
|
auto const& infos = sib.getSamplerInfoList();
|
|
if (infos.empty()) {
|
|
return;
|
|
}
|
|
|
|
bool hasExternalSampler = false;
|
|
|
|
// Replace sampler2D declarations by samplerExternal declarations as they may have
|
|
// been swapped during a previous optimization step
|
|
for (auto const& info : infos) {
|
|
if (info.type == SamplerType::SAMPLER_EXTERNAL) {
|
|
|
|
CString uniformName =
|
|
SamplerInterfaceBlock::getUniformName(
|
|
sib.getName().c_str(), info.name.c_str());
|
|
|
|
auto name = std::string("sampler2D ") + uniformName.c_str();
|
|
size_t index = shader.find(name);
|
|
|
|
if (index != std::string::npos) {
|
|
hasExternalSampler = true;
|
|
auto newName =
|
|
std::string("samplerExternalOES ") + uniformName.c_str();
|
|
shader.replace(index, name.size(), newName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// This method should only be called on shaders that have external samplers but since
|
|
// they may have been removed by previous optimization steps, we check again here
|
|
if (hasExternalSampler) {
|
|
// Find the #version line so we can insert the #extension directive
|
|
size_t index = shader.find("#version");
|
|
index += 8;
|
|
|
|
// Find the end of the line and skip the line return
|
|
while (shader[index] != '\n') index++;
|
|
index++;
|
|
|
|
shader.insert(index, "#extension GL_OES_EGL_image_external_essl3 : require\n");
|
|
}
|
|
}
|
|
|
|
|
|
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, bool value) const {
|
|
if (value) {
|
|
out << "#define " << name << "\n";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, uint32_t value) const {
|
|
out << "#define " << name << " " << value << "\n";
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateDefine(io::sstream& out, const char* name, const char* string) const {
|
|
out << "#define " << name << " " << string << "\n";
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateIndexedDefine(io::sstream& out, const char* name,
|
|
uint32_t index, uint32_t value) const {
|
|
out << "#define " << name << index << " " << value << "\n";
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateMaterialProperty(io::sstream& out,
|
|
MaterialBuilder::Property property, bool isSet) const {
|
|
if (isSet) {
|
|
out << "#define " << "MATERIAL_HAS_" << getConstantName(property) << "\n";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateCommon(io::sstream& out, ShaderType type) const {
|
|
out << SHADERS_COMMON_MATH_FS_DATA;
|
|
out << SHADERS_COMMON_SHADOWING_FS_DATA;
|
|
if (type == ShaderType::VERTEX) {
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_COMMON_SHADING_FS_DATA;
|
|
out << SHADERS_COMMON_GRAPHICS_FS_DATA;
|
|
out << SHADERS_COMMON_MATERIAL_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateFog(io::sstream& out, ShaderType type) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_FOG_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateCommonMaterial(io::sstream& out, ShaderType type) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
out << SHADERS_MATERIAL_INPUTS_VS_DATA;
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_MATERIAL_INPUTS_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generatePostProcessInputs(io::sstream& out, ShaderType type) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
out << SHADERS_POST_PROCESS_INPUTS_VS_DATA;
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_POST_PROCESS_INPUTS_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
utils::io::sstream& CodeGenerator::generatePostProcessGetters(utils::io::sstream& out,
|
|
ShaderType type) const {
|
|
out << SHADERS_COMMON_GETTERS_FS_DATA;
|
|
if (type == ShaderType::VERTEX) {
|
|
out << SHADERS_POST_PROCESS_GETTERS_VS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateGetters(io::sstream& out, ShaderType type) const {
|
|
out << SHADERS_COMMON_GETTERS_FS_DATA;
|
|
if (type == ShaderType::VERTEX) {
|
|
out << SHADERS_GETTERS_VS_DATA;
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_GETTERS_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateParameters(io::sstream& out, ShaderType type) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_SHADING_PARAMETERS_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateShaderLit(io::sstream& out, ShaderType type,
|
|
filament::Variant variant, filament::Shading shading) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
out << SHADERS_COMMON_LIGHTING_FS_DATA;
|
|
if (variant.hasShadowReceiver()) {
|
|
out << SHADERS_SHADOWING_FS_DATA;
|
|
}
|
|
|
|
out << SHADERS_BRDF_FS_DATA;
|
|
switch (shading) {
|
|
case Shading::UNLIT:
|
|
assert("Lit shader generated with unlit shading model");
|
|
break;
|
|
case Shading::SPECULAR_GLOSSINESS:
|
|
case Shading::LIT:
|
|
out << SHADERS_SHADING_MODEL_STANDARD_FS_DATA;
|
|
break;
|
|
case Shading::SUBSURFACE:
|
|
out << SHADERS_SHADING_MODEL_SUBSURFACE_FS_DATA;
|
|
break;
|
|
case Shading::CLOTH:
|
|
out << SHADERS_SHADING_MODEL_CLOTH_FS_DATA;
|
|
break;
|
|
}
|
|
|
|
if (shading != Shading::UNLIT) {
|
|
out << SHADERS_AMBIENT_OCCLUSION_FS_DATA;
|
|
out << SHADERS_LIGHT_INDIRECT_FS_DATA;
|
|
}
|
|
if (variant.hasDirectionalLighting()) {
|
|
out << SHADERS_LIGHT_DIRECTIONAL_FS_DATA;
|
|
}
|
|
if (variant.hasDynamicLighting()) {
|
|
out << SHADERS_LIGHT_PUNCTUAL_FS_DATA;
|
|
}
|
|
|
|
out << SHADERS_SHADING_LIT_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
io::sstream& CodeGenerator::generateShaderUnlit(io::sstream& out, ShaderType type,
|
|
filament::Variant variant, bool hasShadowMultiplier) const {
|
|
if (type == ShaderType::VERTEX) {
|
|
} else if (type == ShaderType::FRAGMENT) {
|
|
if (hasShadowMultiplier) {
|
|
if (variant.hasShadowReceiver()) {
|
|
out << SHADERS_SHADOWING_FS_DATA;
|
|
}
|
|
}
|
|
out << SHADERS_SHADING_UNLIT_FS_DATA;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/* static */
|
|
char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) noexcept {
|
|
using Property = MaterialBuilder::Property;
|
|
switch (property) {
|
|
case Property::BASE_COLOR: return "BASE_COLOR";
|
|
case Property::ROUGHNESS: return "ROUGHNESS";
|
|
case Property::METALLIC: return "METALLIC";
|
|
case Property::REFLECTANCE: return "REFLECTANCE";
|
|
case Property::AMBIENT_OCCLUSION: return "AMBIENT_OCCLUSION";
|
|
case Property::CLEAR_COAT: return "CLEAR_COAT";
|
|
case Property::CLEAR_COAT_ROUGHNESS: return "CLEAR_COAT_ROUGHNESS";
|
|
case Property::CLEAR_COAT_NORMAL: return "CLEAR_COAT_NORMAL";
|
|
case Property::ANISOTROPY: return "ANISOTROPY";
|
|
case Property::ANISOTROPY_DIRECTION: return "ANISOTROPY_DIRECTION";
|
|
case Property::THICKNESS: return "THICKNESS";
|
|
case Property::SUBSURFACE_POWER: return "SUBSURFACE_POWER";
|
|
case Property::SUBSURFACE_COLOR: return "SUBSURFACE_COLOR";
|
|
case Property::SHEEN_COLOR: return "SHEEN_COLOR";
|
|
case Property::SHEEN_ROUGHNESS: return "SHEEN_ROUGHNESS";
|
|
case Property::GLOSSINESS: return "GLOSSINESS";
|
|
case Property::SPECULAR_COLOR: return "SPECULAR_COLOR";
|
|
case Property::EMISSIVE: return "EMISSIVE";
|
|
case Property::NORMAL: return "NORMAL";
|
|
case Property::POST_LIGHTING_COLOR: return "POST_LIGHTING_COLOR";
|
|
case Property::CLIP_SPACE_TRANSFORM: return "CLIP_SPACE_TRANSFORM";
|
|
case Property::ABSORPTION: return "ABSORPTION";
|
|
case Property::TRANSMISSION: return "TRANSMISSION";
|
|
case Property::IOR: return "IOR";
|
|
case Property::MICRO_THICKNESS: return "MICRO_THICKNESS";
|
|
case Property::BENT_NORMAL: return "BENT_NORMAL";
|
|
}
|
|
}
|
|
|
|
char const* CodeGenerator::getUniformTypeName(UniformInterfaceBlock::Type type) noexcept {
|
|
using Type = UniformInterfaceBlock::Type;
|
|
switch (type) {
|
|
case Type::BOOL: return "bool";
|
|
case Type::BOOL2: return "bvec2";
|
|
case Type::BOOL3: return "bvec3";
|
|
case Type::BOOL4: return "bvec4";
|
|
case Type::FLOAT: return "float";
|
|
case Type::FLOAT2: return "vec2";
|
|
case Type::FLOAT3: return "vec3";
|
|
case Type::FLOAT4: return "vec4";
|
|
case Type::INT: return "int";
|
|
case Type::INT2: return "ivec2";
|
|
case Type::INT3: return "ivec3";
|
|
case Type::INT4: return "ivec4";
|
|
case Type::UINT: return "uint";
|
|
case Type::UINT2: return "uvec2";
|
|
case Type::UINT3: return "uvec3";
|
|
case Type::UINT4: return "uvec4";
|
|
case Type::MAT3: return "mat3";
|
|
case Type::MAT4: return "mat4";
|
|
}
|
|
}
|
|
|
|
char const* CodeGenerator::getOutputTypeName(MaterialBuilder::OutputType type) noexcept {
|
|
using Type = UniformInterfaceBlock::Type;
|
|
switch (type) {
|
|
case MaterialBuilder::OutputType::FLOAT: return "float";
|
|
case MaterialBuilder::OutputType::FLOAT2: return "float2";
|
|
case MaterialBuilder::OutputType::FLOAT3: return "float3";
|
|
case MaterialBuilder::OutputType::FLOAT4: return "float4";
|
|
}
|
|
}
|
|
|
|
char const* CodeGenerator::getSamplerTypeName(SamplerType type, SamplerFormat format,
|
|
bool multisample) const noexcept {
|
|
assert(!multisample); // multisample samplers not yet supported.
|
|
switch (type) {
|
|
case SamplerType::SAMPLER_2D:
|
|
switch (format) {
|
|
case SamplerFormat::INT: return "isampler2D";
|
|
case SamplerFormat::UINT: return "usampler2D";
|
|
case SamplerFormat::FLOAT: return "sampler2D";
|
|
case SamplerFormat::SHADOW: return "sampler2DShadow";
|
|
}
|
|
case SamplerType::SAMPLER_3D:
|
|
assert(format != SamplerFormat::SHADOW);
|
|
switch (format) {
|
|
case SamplerFormat::INT: return "isampler3D";
|
|
case SamplerFormat::UINT: return "usampler3D";
|
|
case SamplerFormat::FLOAT: return "sampler3D";
|
|
case SamplerFormat::SHADOW: return nullptr;
|
|
}
|
|
case SamplerType::SAMPLER_2D_ARRAY:
|
|
switch (format) {
|
|
case SamplerFormat::INT: return "isampler2DArray";
|
|
case SamplerFormat::UINT: return "usampler2DArray";
|
|
case SamplerFormat::FLOAT: return "sampler2DArray";
|
|
case SamplerFormat::SHADOW: return "sampler2DArrayShadow";
|
|
}
|
|
case SamplerType::SAMPLER_CUBEMAP:
|
|
switch (format) {
|
|
case SamplerFormat::INT: return "isamplerCube";
|
|
case SamplerFormat::UINT: return "usamplerCube";
|
|
case SamplerFormat::FLOAT: return "samplerCube";
|
|
case SamplerFormat::SHADOW: return "samplerCubeShadow";
|
|
}
|
|
case SamplerType::SAMPLER_EXTERNAL:
|
|
assert(format != SamplerFormat::SHADOW);
|
|
// Vulkan doesn't have external textures in the sense as GL. Vulkan external textures
|
|
// are created via VK_ANDROID_external_memory_android_hardware_buffer, but they are
|
|
// backed by VkImage just like a normal texture, and sampled from normally.
|
|
return (mTargetLanguage == TargetLanguage::SPIRV) ? "sampler2D" : "samplerExternalOES";
|
|
}
|
|
}
|
|
|
|
char const* CodeGenerator::getInterpolationQualifier(Interpolation interpolation) noexcept {
|
|
switch (interpolation) {
|
|
case Interpolation::SMOOTH: return "";
|
|
case Interpolation::FLAT: return "flat ";
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
char const* CodeGenerator::getPrecisionQualifier(Precision precision,
|
|
Precision defaultPrecision) noexcept {
|
|
if (precision == defaultPrecision) {
|
|
return "";
|
|
}
|
|
|
|
switch (precision) {
|
|
case Precision::LOW: return "lowp";
|
|
case Precision::MEDIUM: return "mediump";
|
|
case Precision::HIGH: return "highp";
|
|
case Precision::DEFAULT: return "ERROR";
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
bool CodeGenerator::hasPrecision(UniformInterfaceBlock::Type type) noexcept {
|
|
switch (type) {
|
|
case UniformType::BOOL:
|
|
case UniformType::BOOL2:
|
|
case UniformType::BOOL3:
|
|
case UniformType::BOOL4:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
} // namespace filamat
|