@@ -45,8 +45,6 @@ struct StructFormatData
45
45
46
46
// does this contain a member annotated with [[single]] ?
47
47
bool singleMember = false ;
48
-
49
- bool signedEnum = false ;
50
48
};
51
49
52
50
GraphicsAPI BufferFormatter::m_API;
@@ -1042,7 +1040,9 @@ ParsedFormat BufferFormatter::ParseFormatString(const QString &formatString, uin
1042
1040
}
1043
1041
1044
1042
cur->structDef .type .matrixByteStride = VarTypeByteSize (tmp.type .baseType );
1045
- cur->signedEnum = (VarTypeCompType (tmp.type .baseType ) == CompType::SInt);
1043
+ cur->structDef .type .flags = (VarTypeCompType (tmp.type .baseType ) == CompType::SInt)
1044
+ ? ShaderVariableFlags::SignedEnum
1045
+ : ShaderVariableFlags::NoFlags;
1046
1046
}
1047
1047
1048
1048
continue ;
@@ -1065,7 +1065,7 @@ ParsedFormat BufferFormatter::ParseFormatString(const QString &formatString, uin
1065
1065
QString valueNum = enumMatch.captured (2 );
1066
1066
1067
1067
bool ok = false ;
1068
- if (cur->signedEnum )
1068
+ if (cur->structDef . type . flags & ShaderVariableFlags::SignedEnum )
1069
1069
{
1070
1070
int64_t val = valueNum.toLongLong (&ok, 0 );
1071
1071
@@ -2844,17 +2844,43 @@ QString BufferFormatter::DeclareStruct(Packing::Rules pack, ResourceId shader,
2844
2844
const ShaderConstantType &pointeeType =
2845
2845
PointerTypeRegistry::GetTypeDescriptor (shader, members[i].type .pointerTypeID );
2846
2846
2847
- varTypeName = MakeIdentifierName (pointeeType.name );
2847
+ // don't declare pointer structs for plain scalar pointers (float *foo type)
2848
+ if (pointeeType.name .empty () && pointeeType.members .size () == 1 )
2849
+ {
2850
+ varTypeName = pointeeType.members [0 ].type .name ;
2851
+ }
2852
+ else
2853
+ {
2854
+ varTypeName = MakeIdentifierName (pointeeType.name );
2855
+
2856
+ if (!declaredStructs.contains (varTypeName))
2857
+ {
2858
+ declaredStructs.push_back (varTypeName);
2859
+ declarations += DeclareStruct (pack, shader, declaredStructs, anonStructs, varTypeName,
2860
+ pointeeType.members , pointeeType.arrayByteStride , QString ()) +
2861
+ lit (" \n " );
2862
+ }
2863
+ }
2848
2864
2865
+ varTypeName += lit (" *" );
2866
+ }
2867
+ else if (members[i].type .baseType == VarType::Enum)
2868
+ {
2849
2869
if (!declaredStructs.contains (varTypeName))
2850
2870
{
2851
2871
declaredStructs.push_back (varTypeName);
2852
- declarations += DeclareStruct (pack, shader, declaredStructs, anonStructs, varTypeName,
2853
- pointeeType.members , pointeeType.arrayByteStride , QString ()) +
2854
- lit (" \n " );
2855
- }
2856
2872
2857
- varTypeName += lit (" *" );
2873
+ const bool signedEnum = bool (members[i].type .flags & ShaderVariableFlags::SignedEnum);
2874
+ VarType enumType = signedEnum ? VarType::SInt : VarType::UInt;
2875
+ if (members[i].type .arrayByteStride == 1 )
2876
+ enumType = signedEnum ? VarType::SByte : VarType::UByte;
2877
+ else if (members[i].type .arrayByteStride == 2 )
2878
+ enumType = signedEnum ? VarType::SShort : VarType::UShort;
2879
+ else if (members[i].type .arrayByteStride == 8 )
2880
+ enumType = signedEnum ? VarType::SLong : VarType::ULong;
2881
+
2882
+ declarations += DeclareEnum (varTypeName, members[i].type .members , enumType) + lit (" \n " );
2883
+ }
2858
2884
}
2859
2885
else if (members[i].type .baseType == VarType::Struct)
2860
2886
{
@@ -2891,6 +2917,23 @@ QString BufferFormatter::DeclareStruct(Packing::Rules pack, ResourceId shader,
2891
2917
if (varName.isEmpty ())
2892
2918
varName = QFormatStr (" _child%1" ).arg (i);
2893
2919
2920
+ if (members[i].type .flags & ShaderVariableFlags::RGBDisplay)
2921
+ varTypeName = lit (" [[rgb]] " ) + varTypeName;
2922
+ if (members[i].type .flags & ShaderVariableFlags::SNorm)
2923
+ varTypeName = lit (" [[snorm]] " ) + varTypeName;
2924
+ if (members[i].type .flags & ShaderVariableFlags::UNorm)
2925
+ varTypeName = lit (" [[unorm]] " ) + varTypeName;
2926
+ if (members[i].type .flags & ShaderVariableFlags::R10G10B10A2)
2927
+ varTypeName = lit (" [[packed(r10g10b10a2)]] " ) + varTypeName;
2928
+ if (members[i].type .flags & ShaderVariableFlags::R11G11B10)
2929
+ varTypeName = lit (" [[packed(r11g11b10)]] " ) + varTypeName;
2930
+ // don't print the [[hex]] for pointer types
2931
+ if (members[i].type .pointerTypeID == ~0U &&
2932
+ (members[i].type .flags & ShaderVariableFlags::HexDisplay))
2933
+ varTypeName = lit (" [[hex]] " ) + varTypeName;
2934
+ if (members[i].type .flags & ShaderVariableFlags::BinaryDisplay)
2935
+ varTypeName = lit (" [[bin]] " ) + varTypeName;
2936
+
2894
2937
if (members[i].type .rows > 1 )
2895
2938
{
2896
2939
if (members[i].type .RowMajor ())
@@ -2947,6 +2990,26 @@ QString BufferFormatter::DeclareStruct(Packing::Rules pack, ResourceId shader,
2947
2990
return declarations + ret;
2948
2991
}
2949
2992
2993
+ QString BufferFormatter::DeclareEnum (const QString &name, const rdcarray<ShaderConstant> &members,
2994
+ VarType baseType)
2995
+ {
2996
+ QString ret;
2997
+
2998
+ const bool signedEnum = VarTypeCompType (baseType) == CompType::SInt;
2999
+
3000
+ ShaderVariable tmp;
3001
+ tmp.columns = 1 ;
3002
+
3003
+ ret += QFormatStr (" enum %1 : %2\n {\n " ).arg (name).arg (ToQStr (baseType));
3004
+ for (const ShaderConstant &c : members)
3005
+ {
3006
+ tmp.value .u64v [0 ] = c.defaultValue ;
3007
+ ret += QFormatStr (" %1 = %2,\n " ).arg (c.name ).arg (RowString (tmp, 0 , baseType));
3008
+ }
3009
+ ret += lit (" }\n " );
3010
+ return ret;
3011
+ }
3012
+
2950
3013
QString BufferFormatter::DeclareStruct (Packing::Rules pack, ResourceId shader, const QString &name,
2951
3014
const rdcarray<ShaderConstant> &members,
2952
3015
uint32_t requiredByteStride)
@@ -3925,7 +3988,79 @@ QString RowTypeString(const ShaderVariable &v)
3925
3988
3926
3989
#include " 3rdparty/catch/catch.hpp"
3927
3990
3928
- TEST_CASE (" round-trip via format" , " [formatter]" )
3991
+ TEST_CASE (" Round-trip struct declarations" , " [formatter]" )
3992
+ {
3993
+ BufferFormatter::Init (GraphicsAPI::Vulkan);
3994
+
3995
+ QString fmt;
3996
+
3997
+ // things not tested here:
3998
+ // comments, spacing, etc for obvious reasons
3999
+ // [[pad]] because it's eliminated entirely and will not round-trip
4000
+ // packing rules, they are tested below
4001
+ // aliased typenames like float32_t or int16_t
4002
+
4003
+ fmt = lit (R"( #pack(structured)
4004
+
4005
+ struct ptr_struct
4006
+ {
4007
+ [[snorm]] byte4 a;
4008
+ [[unorm]] byte4 a;
4009
+ [[packed(r11g11b10)]] float3 b;
4010
+ [[packed(r10g10b10a2)]] uint4 c;
4011
+ }
4012
+
4013
+ struct nested
4014
+ {
4015
+ float x[4];
4016
+ [[hex]] uint y;
4017
+ [[bin]] uint z;
4018
+ [[row_major]] float3x4 w;
4019
+ [[rgb]] float4 col;
4020
+ [[offset(128)]]
4021
+ int* p;
4022
+ ptr_struct* q;
4023
+ }
4024
+
4025
+ enum MyEnum : uint
4026
+ {
4027
+ A = 1,
4028
+ B = 2,
4029
+ C = 10,
4030
+ D = 10,
4031
+ }
4032
+
4033
+ struct root
4034
+ {
4035
+ float a;
4036
+ int b;
4037
+ uint c;
4038
+ int d;
4039
+ double e;
4040
+ short f;
4041
+ byte g;
4042
+ float3 h;
4043
+ float2x2 i;
4044
+ int3x4 j;
4045
+ uint k : 5;
4046
+ uint l : 6;
4047
+ uint : 3;
4048
+ uint m : 7;
4049
+ nested n;
4050
+ MyEnum o;
4051
+ }
4052
+ )" );
4053
+
4054
+ ParsedFormat parsed = BufferFormatter::ParseFormatString (fmt, 0 , true );
4055
+ CHECK (parsed.errors .isEmpty ());
4056
+ QString regenerated =
4057
+ BufferFormatter::DeclareStruct (parsed.packing , ResourceId (), parsed.fixed .type .name ,
4058
+ parsed.fixed .type .members , parsed.fixed .type .arrayByteStride );
4059
+
4060
+ CHECK ((fmt == regenerated));
4061
+ }
4062
+
4063
+ TEST_CASE (" Packing rules respected when round-tripping" , " [formatter]" )
3929
4064
{
3930
4065
BufferFormatter::Init (GraphicsAPI::Vulkan);
3931
4066
@@ -5202,6 +5337,86 @@ uint inthird : 14;
5202
5337
CHECK (parsed.fixed .type .members [3 ].byteOffset == 8 );
5203
5338
CHECK (parsed.fixed .type .members [3 ].bitFieldOffset == 0 );
5204
5339
CHECK (parsed.fixed .type .members [3 ].bitFieldSize == 14 );
5340
+
5341
+ // check that offsets are correctly processed after a bitfield that might have left some pending space
5342
+ def = R"(
5343
+ uint a : 16;
5344
+ uint b : 14;
5345
+
5346
+ [[offset(12)]]
5347
+ uint c;
5348
+ )" ;
5349
+ parsed = BufferFormatter::ParseFormatString (def, 0 , true );
5350
+
5351
+ CHECK (parsed.errors .isEmpty ());
5352
+ REQUIRE (parsed.fixed .type .members .size () == 3 );
5353
+ CHECK (parsed.fixed .type .arrayByteStride == 16 );
5354
+ CHECK (parsed.fixed .type .members [0 ].name == " a" );
5355
+ CHECK (parsed.fixed .type .members [0 ].byteOffset == 0 );
5356
+ CHECK (parsed.fixed .type .members [0 ].bitFieldOffset == 0 );
5357
+ CHECK (parsed.fixed .type .members [0 ].bitFieldSize == 16 );
5358
+ CHECK (parsed.fixed .type .members [1 ].name == " b" );
5359
+ CHECK (parsed.fixed .type .members [1 ].byteOffset == 0 );
5360
+ CHECK (parsed.fixed .type .members [1 ].bitFieldOffset == 16 );
5361
+ CHECK (parsed.fixed .type .members [1 ].bitFieldSize == 14 );
5362
+ CHECK (parsed.fixed .type .members [2 ].name == " c" );
5363
+ CHECK (parsed.fixed .type .members [2 ].byteOffset == 12 );
5364
+ CHECK (parsed.fixed .type .members [2 ].bitFieldOffset == 0 );
5365
+ CHECK (parsed.fixed .type .members [2 ].bitFieldSize == 0 );
5366
+
5367
+ // check that an offset mid-bitfield is properly respected
5368
+ def = R"(
5369
+ uint a : 13;
5370
+ [[offset(8)]]
5371
+ uint b : 14;
5372
+ )" ;
5373
+ parsed = BufferFormatter::ParseFormatString (def, 0 , true );
5374
+
5375
+ CHECK (parsed.errors .isEmpty ());
5376
+ REQUIRE (parsed.fixed .type .members .size () == 2 );
5377
+ CHECK (parsed.fixed .type .arrayByteStride == 12 );
5378
+ CHECK (parsed.fixed .type .members [0 ].name == " a" );
5379
+ CHECK (parsed.fixed .type .members [0 ].byteOffset == 0 );
5380
+ CHECK (parsed.fixed .type .members [0 ].bitFieldOffset == 0 );
5381
+ CHECK (parsed.fixed .type .members [0 ].bitFieldSize == 13 );
5382
+ CHECK (parsed.fixed .type .members [1 ].name == " b" );
5383
+ CHECK (parsed.fixed .type .members [1 ].byteOffset == 8 );
5384
+ CHECK (parsed.fixed .type .members [1 ].bitFieldOffset == 0 );
5385
+ CHECK (parsed.fixed .type .members [1 ].bitFieldSize == 14 );
5386
+
5387
+ // check that non-bitfield values after bitfields have the right offset calculated
5388
+ def = R"(
5389
+ struct str
5390
+ {
5391
+ int x;
5392
+ };
5393
+
5394
+ uint a : 13;
5395
+ uint b;
5396
+ uint c : 14;
5397
+ str d;
5398
+ )" ;
5399
+ parsed = BufferFormatter::ParseFormatString (def, 0 , true );
5400
+
5401
+ CHECK (parsed.errors .isEmpty ());
5402
+ REQUIRE (parsed.fixed .type .members .size () == 4 );
5403
+ CHECK (parsed.fixed .type .arrayByteStride == 16 );
5404
+ CHECK (parsed.fixed .type .members [0 ].name == " a" );
5405
+ CHECK (parsed.fixed .type .members [0 ].byteOffset == 0 );
5406
+ CHECK (parsed.fixed .type .members [0 ].bitFieldOffset == 0 );
5407
+ CHECK (parsed.fixed .type .members [0 ].bitFieldSize == 13 );
5408
+ CHECK (parsed.fixed .type .members [1 ].name == " b" );
5409
+ CHECK (parsed.fixed .type .members [1 ].byteOffset == 4 );
5410
+ CHECK (parsed.fixed .type .members [1 ].bitFieldOffset == 0 );
5411
+ CHECK (parsed.fixed .type .members [1 ].bitFieldSize == 0 );
5412
+ CHECK (parsed.fixed .type .members [2 ].name == " c" );
5413
+ CHECK (parsed.fixed .type .members [2 ].byteOffset == 8 );
5414
+ CHECK (parsed.fixed .type .members [2 ].bitFieldOffset == 0 );
5415
+ CHECK (parsed.fixed .type .members [2 ].bitFieldSize == 14 );
5416
+ CHECK (parsed.fixed .type .members [3 ].name == " d" );
5417
+ CHECK (parsed.fixed .type .members [3 ].byteOffset == 12 );
5418
+ CHECK (parsed.fixed .type .members [3 ].bitFieldOffset == 0 );
5419
+ CHECK (parsed.fixed .type .members [3 ].bitFieldSize == 0 );
5205
5420
};
5206
5421
5207
5422
SECTION (" pointers" )
0 commit comments