@@ -204,6 +204,7 @@ class PixTest {
204204 TEST_METHOD (PixStructAnnotation_SequentialFloatN)
205205 TEST_METHOD (PixStructAnnotation_EmbeddedFloatN)
206206 TEST_METHOD (PixStructAnnotation_Matrix)
207+ TEST_METHOD (PixStructAnnotation_MemberFunction)
207208 TEST_METHOD (PixStructAnnotation_BigMess)
208209
209210 dxc::DxcDllSupport m_dllSupport;
@@ -1767,61 +1768,65 @@ PixTest::TestableResults PixTest::TestStructAnnotationCase(const char* hlsl)
17671768 {
17681769 llvm::Value* Address = dbgDeclare->getAddress ();
17691770 auto * AddressAsAlloca = llvm::dyn_cast<llvm::AllocaInst>(Address);
1770- auto * Expression = dbgDeclare->getExpression ();
1771-
1772- std::unique_ptr<dxil_debug_info::MemberIterator> iterator = dxil_debug_info::CreateMemberIterator (
1773- dbgDeclare,
1774- moduleEtc.GetDxilModule ().GetModule ()->getDataLayout (),
1775- AddressAsAlloca,
1776- Expression);
1777-
1778- unsigned int startingBit = 0 ;
1779- unsigned int coveredBits = 0 ;
1780- unsigned int memberIndex = 0 ;
1781- while (iterator->Next (&memberIndex))
1771+ if (AddressAsAlloca != nullptr )
17821772 {
1783- if (memberIndex == 0 )
1784- {
1785- startingBit = iterator->OffsetInBits (memberIndex);
1786- coveredBits = iterator->SizeInBits (memberIndex);
1787- }
1788- else
1789- {
1790- coveredBits = std::max<unsigned int >( coveredBits, iterator->OffsetInBits (memberIndex) + iterator->SizeInBits (memberIndex));
1791- }
1792- }
1793-
1794- // memberIndex is now the count of members in this aggregate type
1795- ret.OffsetAndSizes .push_back ({ memberIndex, startingBit, coveredBits });
1796-
1797- // Use this independent count of number of struct members to test the
1798- // function that operates on the alloca type:
1799- llvm::Type *pAllocaTy = AddressAsAlloca->getType ()->getElementType ();
1800- if (auto *AT = llvm::dyn_cast<llvm::ArrayType>(pAllocaTy))
1801- {
1802- // This is the case where a struct is passed to a function, and in
1803- // these tests there should be only one struct behind the pointer.
1804- VERIFY_ARE_EQUAL (AT->getNumElements (), 1 );
1805- pAllocaTy = AT->getArrayElementType ();
1806- }
1807-
1808- if (auto * ST = llvm::dyn_cast<llvm::StructType>(pAllocaTy))
1809- {
1810- uint32_t countOfMembers = CountStructMembers (ST);
1811- // memberIndex might be greater, because the fragment iterator also includes contained derived types as
1812- // fragments, in addition to the members of that contained derived types. CountStructMembers only counts
1813- // the leaf-node types.
1814- VERIFY_ARE_EQUAL (countOfMembers, memberIndex);
1815- }
1816- else if (pAllocaTy->isFloatingPointTy () || pAllocaTy->isIntegerTy ())
1817- {
1818- // If there's only one member in the struct in the pass-to-function (by pointer)
1819- // case, then the underlying type will have been reduced to the contained type.
1820- VERIFY_ARE_EQUAL (1 , memberIndex);
1821- }
1822- else
1823- {
1824- VERIFY_IS_TRUE (false );
1773+ auto * Expression = dbgDeclare->getExpression ();
1774+
1775+ std::unique_ptr<dxil_debug_info::MemberIterator> iterator = dxil_debug_info::CreateMemberIterator (
1776+ dbgDeclare,
1777+ moduleEtc.GetDxilModule ().GetModule ()->getDataLayout (),
1778+ AddressAsAlloca,
1779+ Expression);
1780+
1781+ unsigned int startingBit = 0 ;
1782+ unsigned int coveredBits = 0 ;
1783+ unsigned int memberIndex = 0 ;
1784+ unsigned int memberCount = 0 ;
1785+ while (iterator->Next (&memberIndex))
1786+ {
1787+ memberCount++;
1788+ if (memberIndex == 0 )
1789+ {
1790+ startingBit = iterator->OffsetInBits (memberIndex);
1791+ coveredBits = iterator->SizeInBits (memberIndex);
1792+ }
1793+ else
1794+ {
1795+ coveredBits = std::max<unsigned int >(coveredBits, iterator->OffsetInBits (memberIndex) + iterator->SizeInBits (memberIndex));
1796+ }
1797+ }
1798+
1799+ ret.OffsetAndSizes .push_back ({ memberCount, startingBit, coveredBits });
1800+
1801+ // Use this independent count of number of struct members to test the
1802+ // function that operates on the alloca type:
1803+ llvm::Type* pAllocaTy = AddressAsAlloca->getType ()->getElementType ();
1804+ if (auto * AT = llvm::dyn_cast<llvm::ArrayType>(pAllocaTy))
1805+ {
1806+ // This is the case where a struct is passed to a function, and in
1807+ // these tests there should be only one struct behind the pointer.
1808+ VERIFY_ARE_EQUAL (AT->getNumElements (), 1 );
1809+ pAllocaTy = AT->getArrayElementType ();
1810+ }
1811+
1812+ if (auto * ST = llvm::dyn_cast<llvm::StructType>(pAllocaTy))
1813+ {
1814+ uint32_t countOfMembers = CountStructMembers (ST);
1815+ // memberIndex might be greater, because the fragment iterator also includes contained derived types as
1816+ // fragments, in addition to the members of that contained derived types. CountStructMembers only counts
1817+ // the leaf-node types.
1818+ VERIFY_ARE_EQUAL (countOfMembers, memberCount);
1819+ }
1820+ else if (pAllocaTy->isFloatingPointTy () || pAllocaTy->isIntegerTy ())
1821+ {
1822+ // If there's only one member in the struct in the pass-to-function (by pointer)
1823+ // case, then the underlying type will have been reduced to the contained type.
1824+ VERIFY_ARE_EQUAL (1 , memberCount);
1825+ }
1826+ else
1827+ {
1828+ VERIFY_IS_TRUE (false );
1829+ }
18251830 }
18261831 }
18271832 }
@@ -1892,7 +1897,7 @@ PixTest::TestableResults PixTest::TestStructAnnotationCase(const char* hlsl)
18921897
18931898void PixTest::ValidateAllocaWrite (std::vector<AllocaWrite> const &allocaWrites,
18941899 size_t index, const char *name) {
1895- VERIFY_ARE_EQUAL (index, allocaWrites[index].index );
1900+ VERIFY_ARE_EQUAL (index, allocaWrites[index].regBase + allocaWrites[index]. index );
18961901#if DBG
18971902 // Compilation may add a prefix to the struct member name:
18981903 VERIFY_IS_TRUE (0 == strncmp (name, allocaWrites[index].memberName .c_str (), strlen (name)));
@@ -2295,6 +2300,123 @@ void main()
22952300
22962301}
22972302
2303+ TEST_F (PixTest, PixStructAnnotation_MemberFunction) {
2304+ const char *hlsl = R"(
2305+
2306+ RWStructuredBuffer<float> floatRWUAV: register(u0);
2307+
2308+ struct smallPayload
2309+ {
2310+ int i;
2311+ };
2312+
2313+ float2 signNotZero(float2 v)
2314+ {
2315+ return (v > 0.0f ? float(1).xx : float(-1).xx);
2316+ }
2317+
2318+ float2 unpackUnorm2(uint packed)
2319+ {
2320+ return (1.0 / 65535.0) * float2((packed >> 16) & 0xffff, packed & 0xffff);
2321+ }
2322+
2323+ float3 unpackOctahedralSnorm(float2 e)
2324+ {
2325+ float3 v = float3(e.xy, 1.0f - abs(e.x) - abs(e.y));
2326+ if (v.z < 0.0f) v.xy = (1.0f - abs(v.yx)) * signNotZero(v.xy);
2327+ return normalize(v);
2328+ }
2329+
2330+ float3 unpackOctahedralUnorm(float2 e)
2331+ {
2332+ return unpackOctahedralSnorm(e * 2.0f - 1.0f);
2333+ }
2334+
2335+ float2 unpackHalf2(uint packed)
2336+ {
2337+ return float2(f16tof32(packed >> 16), f16tof32(packed & 0xffff));
2338+ }
2339+
2340+ struct Gbuffer
2341+ {
2342+ float3 worldNormal;
2343+ float3 objectNormal; //offset:12
2344+
2345+ float linearZ; //24
2346+ float prevLinearZ; //28
2347+
2348+
2349+ float fwidthLinearZ; //32
2350+ float fwidthObjectNormal; //36
2351+
2352+
2353+ uint materialType; //40
2354+ uint2 materialParams0; //44
2355+ uint4 materialParams1; //52 <--------- this is the variable that's being covered twice (52*8 = 416 416)
2356+
2357+ uint instanceId; //68 <------- and there's one dword left over, as expected
2358+
2359+
2360+ void load(int2 pixelPos, Texture2DArray<uint4> gbTex)
2361+ {
2362+ uint4 data0 = gbTex.Load(int4(pixelPos, 0, 0));
2363+ uint4 data1 = gbTex.Load(int4(pixelPos, 1, 0));
2364+ uint4 data2 = gbTex.Load(int4(pixelPos, 2, 0));
2365+
2366+
2367+ worldNormal = unpackOctahedralUnorm(unpackUnorm2(data0.x));
2368+ linearZ = f16tof32((data0.y >> 8) & 0xffff);
2369+ materialType = (data0.y & 0xff);
2370+ materialParams0 = data0.zw;
2371+
2372+
2373+ materialParams1 = data1.xyzw;
2374+
2375+
2376+ instanceId = data2.x;
2377+ prevLinearZ = asfloat(data2.y);
2378+ objectNormal = unpackOctahedralUnorm(unpackUnorm2(data2.z));
2379+ float2 fwidth = unpackHalf2(data2.w);
2380+ fwidthLinearZ = fwidth.x;
2381+ fwidthObjectNormal = fwidth.y;
2382+ }
2383+ };
2384+
2385+ Gbuffer loadGbuffer(int2 pixelPos, Texture2DArray<uint4> gbTex)
2386+ {
2387+ Gbuffer output;
2388+ output.load(pixelPos, gbTex);
2389+ return output;
2390+ }
2391+
2392+ Texture2DArray<uint4> g_gbuffer : register(t0, space0);
2393+
2394+ [numthreads(1, 1, 1)]
2395+ void main()
2396+ {
2397+ const Gbuffer gbuffer = loadGbuffer(int2(0,0), g_gbuffer);
2398+ smallPayload p;
2399+ p.i = gbuffer.materialParams1.x + gbuffer.materialParams1.y + gbuffer.materialParams1.z + gbuffer.materialParams1.w;
2400+ DispatchMesh(1, 1, 1, p);
2401+ }
2402+
2403+
2404+ )" ;
2405+
2406+ auto Testables = TestStructAnnotationCase (hlsl);
2407+ // Can't test member iterator until dbg.declare instructions are emitted when structs
2408+ // contain pointers-to-pointers
2409+
2410+ // Can't validate # of writes: rel and dbg are different
2411+ // VERIFY_ARE_EQUAL(43, Testables.AllocaWrites.size());
2412+
2413+ // Can't test individual writes until struct member names are returned:
2414+ // for (int i = 0; i < 51; ++i)
2415+ // {
2416+ // ValidateAllocaWrite(Testables.AllocaWrites, i, "");
2417+ // }
2418+ }
2419+
22982420TEST_F (PixTest, PixStructAnnotation_BigMess) {
22992421 if (m_ver.SkipDxilVersion (1 , 5 )) return ;
23002422
0 commit comments