Skip to content

Commit 50141bd

Browse files
Radient: add texture parameter packing to material shader layouts
Encode UV selectors and address modes through GLTF texture attributes, populate atlas sampling data, and validate texture packing commands.
1 parent 7643caa commit 50141bd

3 files changed

Lines changed: 360 additions & 14 deletions

File tree

Radient/include/Assets/RadientMaterialImpl.hpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ struct RadientMaterialShaderParameterPacking
5656
Uint32 Offset = 0;
5757
};
5858

59+
/// Packs one texture parameter and its UV selector and address modes into a
60+
/// shader texture-attribute record. UV transform and bias parameters use
61+
/// ordinary RadientMaterialShaderParameterPacking mappings.
62+
struct RadientMaterialShaderTexturePacking
63+
{
64+
Uint32 TextureParameterIndex = 0;
65+
Uint32 UVSelectorParameterIndex = 0;
66+
Uint32 WrapUParameterIndex = 0;
67+
Uint32 WrapVParameterIndex = 0;
68+
Uint32 Offset = 0;
69+
};
70+
5971
/// Describes the immutable shader-readable data layout owned by a material
6072
/// definition. The definition copies the mappings during creation.
6173
struct RadientMaterialShaderDataLayoutDesc
@@ -64,6 +76,9 @@ struct RadientMaterialShaderDataLayoutDesc
6476

6577
const RadientMaterialShaderParameterPacking* pMappings = nullptr;
6678
Uint32 MappingCount = 0;
79+
80+
const RadientMaterialShaderTexturePacking* pTexturePackings = nullptr;
81+
Uint32 TexturePackingCount = 0;
6782
};
6883

6984
class RadientMaterialDefinitionImpl final : public ObjectBase<IRadientMaterialDefinition>
@@ -119,7 +134,8 @@ class RadientMaterialDefinitionImpl final : public ObjectBase<IRadientMaterialDe
119134

120135
/// Writes the complete shader-readable data block for Instance. Instance
121136
/// must have been created by this definition, and pData must reference at
122-
/// least GetShaderDataSize() bytes. Padding and unmapped bytes are set to zero.
137+
/// least GetShaderDataSize() bytes. Padding and unmapped bytes are set to
138+
/// zero. Non-null texture parameters must have initialized sampling data.
123139
void WriteShaderData(const IRadientMaterialInstance& Instance,
124140
void* pData) const noexcept;
125141

@@ -133,12 +149,14 @@ class RadientMaterialDefinitionImpl final : public ObjectBase<IRadientMaterialDe
133149

134150
struct ShaderDataPackingPlan
135151
{
136-
Uint32 Size = 0;
137-
Uint32 CopyCommandCount = 0;
138-
const ShaderDataCopyCommand* pCopyCommands = nullptr;
152+
Uint32 Size = 0;
153+
Uint32 CopyCommandCount = 0;
154+
const ShaderDataCopyCommand* pCopyCommands = nullptr;
155+
Uint32 TextureCommandCount = 0;
156+
const RadientMaterialShaderTexturePacking* pTextureCommands = nullptr;
139157
};
140158

141-
// Parameter descriptors, strings, default values, and shader data copy
159+
// Parameter descriptors, strings, default values, and shader data packing
142160
// commands reside in Memory. Default texture pointers are retained directly
143161
// by their descriptors.
144162
struct PackedData

Radient/src/Assets/RadientMaterialImpl.cpp

Lines changed: 174 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "RadientMaterials.h"
2828
#include "Assets/RadientMaterialAssetManager.hpp"
2929
#include "Assets/RadientMaterialImpl.hpp"
30+
#include "Assets/RadientTextureAssetManager.hpp"
3031

3132
#include "DebugUtilities.hpp"
3233
#include "EngineMemory.h"
@@ -37,6 +38,7 @@
3738

3839
#include <algorithm>
3940
#include <atomic>
41+
#include <cstddef>
4042
#include <cstring>
4143
#include <exception>
4244
#include <limits>
@@ -115,6 +117,22 @@ bool GetMaterialParameterDataSize(const RadientMaterialParameterDesc& Desc, Uint
115117
return true;
116118
}
117119

120+
using ShaderTextureAttribs = GLTF::Material::TextureShaderAttribs;
121+
122+
static_assert(static_cast<Uint32>(RADIENT_MATERIAL_TEXTURE_ADDRESS_MODE_WRAP) == static_cast<Uint32>(TEXTURE_ADDRESS_WRAP));
123+
static_assert(static_cast<Uint32>(RADIENT_MATERIAL_TEXTURE_ADDRESS_MODE_CLAMP) == static_cast<Uint32>(TEXTURE_ADDRESS_CLAMP));
124+
static_assert(sizeof(ShaderTextureAttribs) <= std::numeric_limits<Uint32>::max());
125+
126+
static constexpr Uint32 ShaderTexturePackedPropsOffset = static_cast<Uint32>(offsetof(ShaderTextureAttribs, PackedProps));
127+
static constexpr Uint32 ShaderTextureSliceOffset = static_cast<Uint32>(offsetof(ShaderTextureAttribs, TextureSlice));
128+
static constexpr Uint32 ShaderTextureAtlasUVOffset = static_cast<Uint32>(offsetof(ShaderTextureAttribs, AtlasUVScaleAndBias));
129+
static constexpr Uint32 ShaderTextureDataSize = static_cast<Uint32>(sizeof(ShaderTextureAttribs));
130+
131+
bool RangesOverlap(Uint32 LhsOffset, Uint32 LhsSize, Uint32 RhsOffset, Uint32 RhsSize) noexcept
132+
{
133+
return LhsOffset < RhsOffset + RhsSize && RhsOffset < LhsOffset + LhsSize;
134+
}
135+
118136
RADIENT_STATUS ValidateMaterialDefinitionDesc(const RadientMaterialDefinitionDesc& Desc)
119137
{
120138
if (Desc.Domain >= RADIENT_MATERIAL_DOMAIN_COUNT)
@@ -208,6 +226,12 @@ RADIENT_STATUS ValidateMaterialShaderDataLayout(
208226
" mappings, but pMappings is null");
209227
return RADIENT_STATUS_INVALID_ARGUMENT;
210228
}
229+
if (ShaderDataLayout.TexturePackingCount != 0 && ShaderDataLayout.pTexturePackings == nullptr)
230+
{
231+
LOG_ERROR_MESSAGE("Material shader data layout declares ", ShaderDataLayout.TexturePackingCount,
232+
" texture packings, but pTexturePackings is null");
233+
return RADIENT_STATUS_INVALID_ARGUMENT;
234+
}
211235

212236
for (Uint32 MappingIndex = 0; MappingIndex < ShaderDataLayout.MappingCount; ++MappingIndex)
213237
{
@@ -264,6 +288,101 @@ RADIENT_STATUS ValidateMaterialShaderDataLayout(
264288
}
265289
}
266290

291+
for (Uint32 PackingIndex = 0; PackingIndex < ShaderDataLayout.TexturePackingCount; ++PackingIndex)
292+
{
293+
const RadientMaterialShaderTexturePacking& Packing = ShaderDataLayout.pTexturePackings[PackingIndex];
294+
295+
const auto GetParameter = [&](Uint32 ParameterIndex, const char* Role) -> const RadientMaterialParameterDesc* {
296+
if (ParameterIndex >= DefinitionDesc.ParameterCount)
297+
{
298+
LOG_ERROR_MESSAGE("Material shader texture packing ", PackingIndex, " references ", Role,
299+
" parameter index ", ParameterIndex, ", but the definition only has ",
300+
DefinitionDesc.ParameterCount, " parameters");
301+
return nullptr;
302+
}
303+
return &DefinitionDesc.pParameters[ParameterIndex];
304+
};
305+
306+
const RadientMaterialParameterDesc* const pTexture = GetParameter(Packing.TextureParameterIndex, "texture");
307+
if (pTexture == nullptr)
308+
return RADIENT_STATUS_INVALID_ARGUMENT;
309+
if (!IsTextureParameter(pTexture->Type) || pTexture->ArraySize != 1)
310+
{
311+
LOG_ERROR_MESSAGE("Material shader texture packing ", PackingIndex, " texture parameter '",
312+
pTexture->Name, "' must be a scalar texture");
313+
return RADIENT_STATUS_INVALID_ARGUMENT;
314+
}
315+
316+
const auto ValidateScalarParameter = [&](Uint32 ParameterIndex,
317+
const char* Role,
318+
RADIENT_MATERIAL_PARAMETER_TYPE ExpectedType) -> bool {
319+
const RadientMaterialParameterDesc* const pParameter = GetParameter(ParameterIndex, Role);
320+
if (pParameter == nullptr)
321+
return false;
322+
if (pParameter->Type != ExpectedType || pParameter->ArraySize != 1)
323+
{
324+
LOG_ERROR_MESSAGE("Material shader texture packing ", PackingIndex, ' ', Role, " parameter '",
325+
pParameter->Name, "' has an incompatible type or array size");
326+
return false;
327+
}
328+
return true;
329+
};
330+
331+
if (!ValidateScalarParameter(Packing.UVSelectorParameterIndex, "UV selector", RADIENT_MATERIAL_PARAMETER_TYPE_INT) ||
332+
!ValidateScalarParameter(Packing.WrapUParameterIndex, "wrap U", RADIENT_MATERIAL_PARAMETER_TYPE_UINT) ||
333+
!ValidateScalarParameter(Packing.WrapVParameterIndex, "wrap V", RADIENT_MATERIAL_PARAMETER_TYPE_UINT))
334+
{
335+
return RADIENT_STATUS_INVALID_ARGUMENT;
336+
}
337+
338+
if (Packing.Offset > ShaderDataLayout.Size ||
339+
ShaderTextureDataSize > ShaderDataLayout.Size - Packing.Offset)
340+
{
341+
LOG_ERROR_MESSAGE("Material shader texture packing ", PackingIndex, " uses byte range [",
342+
Packing.Offset, ", ", Uint64{Packing.Offset} + ShaderTextureDataSize,
343+
"), which exceeds the shader data size ", ShaderDataLayout.Size);
344+
return RADIENT_STATUS_INVALID_ARGUMENT;
345+
}
346+
347+
for (Uint32 MappingIndex = 0; MappingIndex < ShaderDataLayout.MappingCount; ++MappingIndex)
348+
{
349+
const RadientMaterialShaderParameterPacking& Mapping = ShaderDataLayout.pMappings[MappingIndex];
350+
const RadientMaterialParameterDesc& Parameter = DefinitionDesc.pParameters[Mapping.ParameterIndex];
351+
Uint32 ParameterDataSize = 0;
352+
const bool IsValidDataSize = GetMaterialParameterDataSize(Parameter, ParameterDataSize);
353+
VERIFY_EXPR(IsValidDataSize);
354+
(void)IsValidDataSize;
355+
356+
const bool OverlapsPackedProperties =
357+
RangesOverlap(Mapping.Offset, ParameterDataSize,
358+
Packing.Offset + ShaderTexturePackedPropsOffset,
359+
ShaderTextureSliceOffset + sizeof(Float32) - ShaderTexturePackedPropsOffset);
360+
const bool OverlapsAtlasData =
361+
RangesOverlap(Mapping.Offset, ParameterDataSize,
362+
Packing.Offset + ShaderTextureAtlasUVOffset,
363+
sizeof(RadientFloat4));
364+
if (OverlapsPackedProperties || OverlapsAtlasData)
365+
{
366+
LOG_ERROR_MESSAGE("Material shader data mapping ", MappingIndex,
367+
" overlaps data written by texture packing ", PackingIndex);
368+
return RADIENT_STATUS_INVALID_ARGUMENT;
369+
}
370+
}
371+
372+
for (Uint32 PreviousIndex = 0; PreviousIndex < PackingIndex; ++PreviousIndex)
373+
{
374+
const RadientMaterialShaderTexturePacking& PreviousPacking =
375+
ShaderDataLayout.pTexturePackings[PreviousIndex];
376+
if (RangesOverlap(Packing.Offset, ShaderTextureDataSize,
377+
PreviousPacking.Offset, ShaderTextureDataSize))
378+
{
379+
LOG_ERROR_MESSAGE("Material shader texture packings ", PreviousIndex, " and ", PackingIndex,
380+
" overlap in the destination data block");
381+
return RADIENT_STATUS_INVALID_ARGUMENT;
382+
}
383+
}
384+
}
385+
267386
return RADIENT_STATUS_OK;
268387
}
269388

@@ -278,6 +397,7 @@ RadientMaterialDefinitionImpl::PackedData RadientMaterialDefinitionImpl::PackDat
278397
FixedLinearAllocator Allocator{GetRawAllocator()};
279398
Allocator.AddSpace<RadientMaterialParameterDesc>(Desc.ParameterCount);
280399
Allocator.AddSpace<ShaderDataCopyCommand>(ShaderDataLayout.MappingCount);
400+
Allocator.AddSpace<RadientMaterialShaderTexturePacking>(ShaderDataLayout.TexturePackingCount);
281401
Allocator.AddSpaceForString(DefinitionName);
282402
Allocator.AddSpaceForString(Desc.Reference.URI);
283403

@@ -305,15 +425,19 @@ RadientMaterialDefinitionImpl::PackedData RadientMaterialDefinitionImpl::PackDat
305425
Writer.ConstructArray<RadientMaterialParameterDesc>(Desc.ParameterCount);
306426
ShaderDataCopyCommand* const pShaderDataCopyCommands =
307427
Writer.ConstructArray<ShaderDataCopyCommand>(ShaderDataLayout.MappingCount);
308-
309-
Data.Desc = Desc;
310-
Data.Desc.Name = Writer.CopyString(DefinitionName);
311-
Data.Desc.Reference = Desc.Reference;
312-
Data.Desc.Reference.URI = Writer.CopyString(Desc.Reference.URI);
313-
Data.Desc.pParameters = pParameters;
314-
Data.PackingPlan.Size = ShaderDataLayout.Size;
315-
Data.PackingPlan.CopyCommandCount = ShaderDataLayout.MappingCount;
316-
Data.PackingPlan.pCopyCommands = pShaderDataCopyCommands;
428+
RadientMaterialShaderTexturePacking* const pShaderDataTextureCommands =
429+
Writer.ConstructArray<RadientMaterialShaderTexturePacking>(ShaderDataLayout.TexturePackingCount);
430+
431+
Data.Desc = Desc;
432+
Data.Desc.Name = Writer.CopyString(DefinitionName);
433+
Data.Desc.Reference = Desc.Reference;
434+
Data.Desc.Reference.URI = Writer.CopyString(Desc.Reference.URI);
435+
Data.Desc.pParameters = pParameters;
436+
Data.PackingPlan.Size = ShaderDataLayout.Size;
437+
Data.PackingPlan.CopyCommandCount = ShaderDataLayout.MappingCount;
438+
Data.PackingPlan.pCopyCommands = pShaderDataCopyCommands;
439+
Data.PackingPlan.TextureCommandCount = ShaderDataLayout.TexturePackingCount;
440+
Data.PackingPlan.pTextureCommands = pShaderDataTextureCommands;
317441

318442
for (Uint32 MappingIndex = 0; MappingIndex < ShaderDataLayout.MappingCount; ++MappingIndex)
319443
{
@@ -332,6 +456,9 @@ RadientMaterialDefinitionImpl::PackedData RadientMaterialDefinitionImpl::PackDat
332456
};
333457
}
334458

459+
for (Uint32 PackingIndex = 0; PackingIndex < ShaderDataLayout.TexturePackingCount; ++PackingIndex)
460+
pShaderDataTextureCommands[PackingIndex] = ShaderDataLayout.pTexturePackings[PackingIndex];
461+
335462
for (Uint32 Index = 0; Index < Desc.ParameterCount; ++Index)
336463
{
337464
const RadientMaterialParameterDesc& Src = Desc.pParameters[Index];
@@ -950,6 +1077,44 @@ void RadientMaterialDefinitionImpl::WriteShaderData(
9501077
InstanceImpl.m_Data.GetValueData(Command.ParameterIndex),
9511078
Command.Size);
9521079
}
1080+
1081+
for (Uint32 CommandIndex = 0; CommandIndex < m_Data.PackingPlan.TextureCommandCount; ++CommandIndex)
1082+
{
1083+
const RadientMaterialShaderTexturePacking& Command =
1084+
m_Data.PackingPlan.pTextureCommands[CommandIndex];
1085+
1086+
const Int32 UVSelector =
1087+
*static_cast<const Int32*>(InstanceImpl.m_Data.GetValueData(Command.UVSelectorParameterIndex));
1088+
const Uint32 WrapU =
1089+
*static_cast<const Uint32*>(InstanceImpl.m_Data.GetValueData(Command.WrapUParameterIndex));
1090+
const Uint32 WrapV =
1091+
*static_cast<const Uint32*>(InstanceImpl.m_Data.GetValueData(Command.WrapVParameterIndex));
1092+
ShaderTextureAttribs TextureAttribs{};
1093+
TextureAttribs.SetUVSelector(UVSelector);
1094+
TextureAttribs.SetWrapUMode(static_cast<TEXTURE_ADDRESS_MODE>(WrapU));
1095+
TextureAttribs.SetWrapVMode(static_cast<TEXTURE_ADDRESS_MODE>(WrapV));
1096+
std::memcpy(pShaderData + Command.Offset + ShaderTexturePackedPropsOffset,
1097+
&TextureAttribs.PackedProps,
1098+
sizeof(TextureAttribs.PackedProps));
1099+
1100+
IRadientTextureAsset* const pTexture = InstanceImpl.m_Data.GetTexture(Command.TextureParameterIndex, 0);
1101+
if (pTexture != nullptr)
1102+
{
1103+
RadientTextureSamplingInfo SamplingInfo{};
1104+
const bool SamplingInfoAvailable =
1105+
RadientTextureAssetManager::GetTextureSamplingInfo(pTexture, SamplingInfo);
1106+
VERIFY_EXPR(SamplingInfoAvailable);
1107+
if (SamplingInfoAvailable)
1108+
{
1109+
std::memcpy(pShaderData + Command.Offset + ShaderTextureSliceOffset,
1110+
&SamplingInfo.TextureSlice,
1111+
sizeof(SamplingInfo.TextureSlice));
1112+
std::memcpy(pShaderData + Command.Offset + ShaderTextureAtlasUVOffset,
1113+
&SamplingInfo.UVScaleBias,
1114+
sizeof(SamplingInfo.UVScaleBias));
1115+
}
1116+
}
1117+
}
9531118
}
9541119

9551120
RADIENT_STATUS DILIGENT_CALL_TYPE RadientMaterialDefinitionImpl::CreateInstance(

0 commit comments

Comments
 (0)