Add a way to specify an additional uniform name along with a sampler param for the associated transform matrix (#8490)

BUGS=[399959254]
This commit is contained in:
Ajmal Kunnummal
2025-03-06 10:42:24 -08:00
committed by GitHub
parent 47112f136e
commit 02223dcaab
9 changed files with 93 additions and 18 deletions

View File

@@ -7,3 +7,4 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- materials: add an optional `transformName` field to sampler material parameters. [⚠️ **New Material Version**]

View File

@@ -991,10 +991,13 @@ Type
Value
: Each entry is an object with the properties `name` and `type`, both of `string` type. The
name must be a valid GLSL identifier. Entries also have an optional `precision`, which can be
name must be a valid GLSL identifier. Entries have an optional `precision`, which can be
one of `default` (best precision for the platform, typically `high` on desktop, `medium` on
mobile), `low`, `medium`, `high`. The type must be one of the types described in
table [materialParamsTypes].
table [materialParamsTypes]. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
this will always be identity.
Type | Description
:----------------------|:---------------------------------

View File

@@ -421,6 +421,7 @@ bool ChunkUniformInterfaceBlock::unflatten(Unflattener& unflattener,
uint64_t fieldSize = 0;
uint8_t fieldType = 0;
uint8_t fieldPrecision = 0;
uint8_t fieldAssociatedSampler = 0;
if (!unflattener.read(&fieldName)) {
return false;
@@ -438,8 +439,13 @@ bool ChunkUniformInterfaceBlock::unflatten(Unflattener& unflattener,
return false;
}
if (!unflattener.read(&fieldAssociatedSampler)) {
return false;
}
// a size of 1 means not an array
builder.add({{{ fieldName.data(), fieldName.size() },
fieldAssociatedSampler,
uint32_t(fieldSize == 1 ? 0 : fieldSize),
BufferInterfaceBlock::Type(fieldType),
BufferInterfaceBlock::Precision(fieldPrecision) }});

View File

@@ -40,11 +40,28 @@ public:
std::string_view name;
uint32_t size;
backend::UniformType type;
backend::Precision precision{};
backend::FeatureLevel minFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1;
std::string_view structName{};
uint32_t stride{};
std::string_view sizeName{};
backend::Precision precision;
uint8_t associatedSampler = 0;
backend::FeatureLevel minFeatureLevel;
std::string_view structName;
uint32_t stride;
std::string_view sizeName;
InterfaceBlockEntry() = default;
InterfaceBlockEntry(std::string_view name, uint32_t size, backend::UniformType type,
backend::Precision precision = {},
backend::FeatureLevel minFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1, std::string_view structName = {},
uint32_t stride = {}, std::string_view sizeName = {}) noexcept
: name(name), size(size), type(type), precision(precision),
associatedSampler(0), minFeatureLevel(minFeatureLevel),
structName(structName), stride(stride), sizeName(sizeName) {}
InterfaceBlockEntry(std::string_view name, uint8_t associatedSampler, uint32_t size, backend::UniformType type,
backend::Precision precision = {},
backend::FeatureLevel minFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1, std::string_view structName = {},
uint32_t stride = {}, std::string_view sizeName = {}) noexcept
: name(name), size(size), type(type), precision(precision),
associatedSampler(associatedSampler), minFeatureLevel(minFeatureLevel),
structName(structName), stride(stride), sizeName(sizeName) {}
};
BufferInterfaceBlock();
@@ -68,6 +85,7 @@ public:
bool isArray; // true if the field is an array
uint32_t size; // size of the array in elements, or 0 if not an array
Precision precision; // precision of this field
uint8_t associatedSampler; // sampler associated with this field
backend::FeatureLevel minFeatureLevel; // below this feature level, this field is not needed
utils::CString structName; // name of this field structure if type is STRUCT
utils::CString sizeName; // name of the size parameter in the shader
@@ -152,6 +170,10 @@ public:
// negative value if name doesn't exist or Panic if exceptions are enabled
ssize_t getFieldOffset(std::string_view name, size_t index) const;
// returns offset in bytes of the transform matrix for the given external texture binding
// returns -1 if the field doesn't exist
ssize_t getTransformFieldOffset(uint8_t binding) const;
FieldInfo const* getFieldInfo(std::string_view name) const;
bool hasField(std::string_view name) const noexcept {
@@ -179,6 +201,7 @@ private:
utils::CString mName;
utils::FixedCapacityVector<FieldInfo> mFieldInfoList;
std::unordered_map<std::string_view , uint32_t> mInfoMap;
std::unordered_map<uint8_t, uint32_t> mTransformOffsetMap;
uint32_t mSize = 0; // size in bytes rounded to multiple of 4
Alignment mAlignment = Alignment::std140;
Target mTarget = Target::UNIFORM;

View File

@@ -58,7 +58,7 @@ BufferInterfaceBlock::Builder& BufferInterfaceBlock::Builder::add(
for (auto const& item : list) {
mEntries.push_back({
{ item.name.data(), item.name.size() },
0, uint8_t(item.stride), item.type, item.size > 0, item.size, item.precision, item.minFeatureLevel,
0, uint8_t(item.stride), item.type, item.size > 0, item.size, item.precision, item.associatedSampler, item.minFeatureLevel,
{ item.structName.data(), item.structName.size() },
{ item.sizeName.data(), item.sizeName.size() }
});
@@ -71,7 +71,7 @@ BufferInterfaceBlock::Builder& BufferInterfaceBlock::Builder::addVariableSizedAr
mHasVariableSizeArray = true;
mEntries.push_back({
{ item.name.data(), item.name.size() },
0, uint8_t(item.stride), item.type, true, 0, item.precision, item.minFeatureLevel,
0, uint8_t(item.stride), item.type, true, 0, item.precision, item.associatedSampler, item.minFeatureLevel,
{ item.structName.data(), item.structName.size() },
{ item.sizeName.data(), item.sizeName.size() }
});
@@ -122,6 +122,8 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept
auto& uniformsInfoList = mFieldInfoList;
auto& transformOffsetMap = mTransformOffsetMap;
uint32_t i = 0;
uint16_t offset = 0;
for (auto const& e : builder.mEntries) {
@@ -143,11 +145,16 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept
FieldInfo& info = uniformsInfoList[i];
info = { e.name, offset, uint8_t(stride), e.type, e.isArray, e.size,
e.precision, e.minFeatureLevel, e.structName, e.sizeName };
e.precision, e.associatedSampler, e.minFeatureLevel, e.structName, e.sizeName };
// record this uniform info
infoMap[{ info.name.data(), info.name.size() }] = i;
// record the transform offset if this is an external sampler
if (e.associatedSampler != 0) {
transformOffsetMap[e.associatedSampler] = offset;
}
// advance offset to next slot
offset += stride * std::max(1u, e.size);
++i;
@@ -163,6 +170,14 @@ ssize_t BufferInterfaceBlock::getFieldOffset(std::string_view name, size_t index
return (ssize_t)info->getBufferOffset(index);
}
ssize_t BufferInterfaceBlock::getTransformFieldOffset(uint8_t binding) const {
auto pos = mTransformOffsetMap.find(binding);
if (pos == mTransformOffsetMap.end()) {
return -1;
}
return (ssize_t)pos->second;
}
BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo(
std::string_view name) const {
auto pos = mInfoMap.find(name);

View File

@@ -318,7 +318,8 @@ public:
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
SamplerFormat format = SamplerFormat::FLOAT,
ParameterPrecision precision = ParameterPrecision::DEFAULT,
bool multisample = false) noexcept;
bool multisample = false,
const char* transformName = "") noexcept;
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept;
@@ -650,8 +651,8 @@ public:
Parameter() noexcept: parameterType(INVALID) {}
// Sampler
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, bool ms)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER), multisample(ms) { }
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, bool ms, const char* tn)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER), multisample(ms), transformName(tn) { }
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
@@ -669,6 +670,7 @@ public:
SubpassType subpassType;
SamplerFormat format;
bool multisample;
utils::CString transformName;
enum {
INVALID,
UNIFORM,

View File

@@ -279,7 +279,7 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType type,
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
SamplerFormat format, ParameterPrecision precision, bool multisample) noexcept {
SamplerFormat format, ParameterPrecision precision, bool multisample, const char* transformName) noexcept {
FILAMENT_CHECK_PRECONDITION(!multisample ||
(format != SamplerFormat::SHADOW &&
(samplerType == SamplerType::SAMPLER_2D ||
@@ -288,7 +288,7 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType sample
" as long as type is not SHADOW";
FILAMENT_CHECK_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT) << "Too many parameters";
mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample };
mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample, transformName };
return *this;
}
@@ -609,12 +609,18 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
SamplerInterfaceBlock::Builder sbb;
BufferInterfaceBlock::Builder ibb;
// sampler bindings start at 1, 0 is the ubo
for (size_t i = 0, binding = 1, c = mParameterCount; i < c; i++) {
uint16_t binding = 1;
for (size_t i = 0, c = mParameterCount; i < c; i++) {
auto const& param = mParameters[i];
assert_invariant(!param.isSubpass());
if (param.isSampler()) {
sbb.add({ param.name.data(), param.name.size() },
binding++, param.samplerType, param.format, param.precision, param.multisample);
binding, param.samplerType, param.format, param.precision, param.multisample);
if (!param.transformName.empty()) {
ibb.add({{{ param.transformName.data(), param.transformName.size() }, uint8_t(binding),
0, UniformType::MAT3, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 }});
}
binding++;
} else if (param.isUniform()) {
ibb.add({{{ param.name.data(), param.name.size() },
uint32_t(param.size == 1u ? 0u : param.size), param.uniformType,

View File

@@ -52,6 +52,7 @@ void MaterialUniformInterfaceBlockChunk::flatten(Flattener& f) {
f.writeUint64(uInfo.size);
f.writeUint8(static_cast<uint8_t>(uInfo.type));
f.writeUint8(static_cast<uint8_t>(uInfo.precision));
f.writeUint8(static_cast<uint8_t>(uInfo.associatedSampler));
}
}

View File

@@ -153,6 +153,12 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
std::cerr << "parameters: name value must be STRING." << std::endl;
return false;
}
const JsonishValue* transformNameValue = jsonObject.getValue("transformName");
if (transformNameValue && transformNameValue->getType() != JsonishValue::STRING) {
std::cerr << "parameters: transformName value must be STRING." << std::endl;
return false;
}
const JsonishValue* precisionValue = jsonObject.getValue("precision");
if (precisionValue) {
@@ -221,7 +227,19 @@ static bool processParameter(MaterialBuilder& builder, const JsonishObject& json
auto multisample = multiSampleValue ? multiSampleValue->toJsonBool()->getBool() : false;
builder.parameter(nameString.c_str(), type, format, precision, multisample);
if (transformNameValue) {
if (type != MaterialBuilder::SamplerType::SAMPLER_EXTERNAL) {
std::cerr << "parameters: the parameter with name '" << nameString << "'"
<< " is a sampler of type " << typeString << " and has a transformName."
<< " Transform names are only supported for external samplers."
<< std::endl;
return false;
}
auto transformName = transformNameValue->toJsonString()->getString();
builder.parameter(nameString.c_str(), type, format, precision, multisample, transformName.c_str());
} else {
builder.parameter(nameString.c_str(), type, format, precision, multisample);
}
} else {
std::cerr << "parameters: the type '" << typeString