|
14 | 14 | #endif |
15 | 15 |
|
16 | 16 | #include <algorithm> |
| 17 | +#include <array> |
17 | 18 | #include <cassert> |
18 | 19 | #include <cfloat> |
19 | 20 | #include <map> |
@@ -144,6 +145,8 @@ class PixTest : public ::testing::Test { |
144 | 145 | TEST_METHOD(DebugInstrumentation_TextOutput) |
145 | 146 | TEST_METHOD(DebugInstrumentation_BlockReport) |
146 | 147 |
|
| 148 | + TEST_METHOD(DebugInstrumentation_VectorAllocaWrite_Structs) |
| 149 | + |
147 | 150 | dxc::DxcDllSupport m_dllSupport; |
148 | 151 | VersionSupportInfo m_ver; |
149 | 152 |
|
@@ -435,6 +438,7 @@ class PixTest : public ::testing::Test { |
435 | 438 | CComPtr<IDxcBlob> RunDxilPIXDXRInvocationsLog(IDxcBlob *blob); |
436 | 439 | void TestPixUAVCase(char const *hlsl, wchar_t const *model, |
437 | 440 | wchar_t const *entry); |
| 441 | + std::string Disassemble(IDxcBlob *pProgram); |
438 | 442 | }; |
439 | 443 |
|
440 | 444 | bool PixTest::InitSupport() { |
@@ -1083,6 +1087,16 @@ static bool FindStructMemberFromStore(llvm::StoreInst *S, |
1083 | 1087 | return false; |
1084 | 1088 | } |
1085 | 1089 |
|
| 1090 | +std::string PixTest::Disassemble(IDxcBlob *pProgram) { |
| 1091 | + CComPtr<IDxcCompiler> pCompiler; |
| 1092 | + CComPtr<IDxcOperationResult> pResult; |
| 1093 | + CComPtr<IDxcBlobEncoding> pSource; |
| 1094 | + VERIFY_SUCCEEDED(CreateCompiler(m_dllSupport, &pCompiler)); |
| 1095 | + CComPtr<IDxcBlobEncoding> pDisassembly; |
| 1096 | + VERIFY_SUCCEEDED(pCompiler->Disassemble(pProgram, &pDisassembly)); |
| 1097 | + return BlobToUtf8(pDisassembly); |
| 1098 | +} |
| 1099 | + |
1086 | 1100 | // This function lives in lib\DxilPIXPasses\DxilAnnotateWithVirtualRegister.cpp |
1087 | 1101 | // Declared here so we can test it. |
1088 | 1102 | uint32_t CountStructMembers(llvm::Type const *pType); |
@@ -2955,3 +2969,172 @@ float4 main() : SV_Target { |
2955 | 2969 | VERIFY_IS_TRUE(foundDoubleAssignment); |
2956 | 2970 | VERIFY_IS_TRUE(found32BitAllocaStore); |
2957 | 2971 | } |
| 2972 | + |
| 2973 | +std::string ExtractBracedSubstring(std::string const &line) { |
| 2974 | + auto open = line.find('{'); |
| 2975 | + auto close = line.find('}'); |
| 2976 | + if (open != std::string::npos && close != std::string::npos && |
| 2977 | + open + 1 < close) { |
| 2978 | + return line.substr(open + 1, close - open - 1); |
| 2979 | + } |
| 2980 | + return {}; |
| 2981 | +} |
| 2982 | + |
| 2983 | +int ExtractMetaInt32Value(std::string const &token) { |
| 2984 | + auto findi32 = token.find("i32 "); |
| 2985 | + if (findi32 != std::string_view::npos) { |
| 2986 | + return atoi( |
| 2987 | + std::string(token.data() + findi32 + 4, token.length() - (findi32 + 4)) |
| 2988 | + .c_str()); |
| 2989 | + } |
| 2990 | + return -1; |
| 2991 | +} |
| 2992 | + |
| 2993 | +std::vector<std::string> Split(std::string str, char delimeter) { |
| 2994 | + std::vector<std::string> lines; |
| 2995 | + |
| 2996 | + auto const *p = str.data(); |
| 2997 | + auto const *justPastPreviousDelimiter = p; |
| 2998 | + while (p < str.data() + str.length()) { |
| 2999 | + if (*p == delimeter) { |
| 3000 | + lines.emplace_back(std::string(justPastPreviousDelimiter, |
| 3001 | + p - justPastPreviousDelimiter)); |
| 3002 | + justPastPreviousDelimiter = p + 1; |
| 3003 | + p = justPastPreviousDelimiter; |
| 3004 | + } else { |
| 3005 | + p++; |
| 3006 | + } |
| 3007 | + } |
| 3008 | + |
| 3009 | + lines.emplace_back( |
| 3010 | + std::string(justPastPreviousDelimiter, p - justPastPreviousDelimiter)); |
| 3011 | + |
| 3012 | + return lines; |
| 3013 | +} |
| 3014 | + |
| 3015 | +struct MetadataAllocaDefinition { |
| 3016 | + int base; |
| 3017 | + int count; |
| 3018 | +}; |
| 3019 | +using AllocaDefinitions = std::map<int, MetadataAllocaDefinition>; |
| 3020 | +struct MetadataAllocaWrite { |
| 3021 | + int allocaDefMetadataKey; |
| 3022 | + int offset; |
| 3023 | + int size; |
| 3024 | +}; |
| 3025 | +using AllocaWrites = std::map<int, MetadataAllocaWrite>; |
| 3026 | + |
| 3027 | +struct AllocaMetadata { |
| 3028 | + AllocaDefinitions allocaDefinitions; |
| 3029 | + AllocaWrites allocaWrites; |
| 3030 | + std::vector<int> allocaWritesMetaKeys; |
| 3031 | +}; |
| 3032 | + |
| 3033 | +AllocaMetadata |
| 3034 | +FindAllocaRelatedMetadata(std::vector<std::string> const &lines) { |
| 3035 | + |
| 3036 | + const char *allocaMetaDataAssignment = "= !{i32 1, "; |
| 3037 | + const char *allocaRegWRiteAssignment = "= !{i32 2, !"; |
| 3038 | + const char *allocaRegWriteTag = "!pix-alloca-reg-write !"; |
| 3039 | + |
| 3040 | + AllocaMetadata ret; |
| 3041 | + for (auto const &line : lines) { |
| 3042 | + if (line[0] == '!') { |
| 3043 | + auto key = atoi(std::string(line.data() + 1, line.length() - 1).c_str()); |
| 3044 | + if (key != -1) { |
| 3045 | + if (line.find(allocaMetaDataAssignment) != std::string::npos) { |
| 3046 | + std::string bitInBraces = ExtractBracedSubstring(line); |
| 3047 | + if (bitInBraces != "") { |
| 3048 | + auto tokens = Split(bitInBraces, ','); |
| 3049 | + if (tokens.size() == 3) { |
| 3050 | + auto value0 = ExtractMetaInt32Value(tokens[1]); |
| 3051 | + auto value1 = ExtractMetaInt32Value(tokens[2]); |
| 3052 | + if (value0 != -1 && value1 != -1) { |
| 3053 | + MetadataAllocaDefinition def; |
| 3054 | + def.base = value0; |
| 3055 | + def.count = value1; |
| 3056 | + ret.allocaDefinitions[key] = def; |
| 3057 | + } |
| 3058 | + } |
| 3059 | + } |
| 3060 | + } else if (line.find(allocaRegWRiteAssignment) != std::string::npos) { |
| 3061 | + std::string bitInBraces = ExtractBracedSubstring(line); |
| 3062 | + if (bitInBraces != "") { |
| 3063 | + auto tokens = Split(bitInBraces, ','); |
| 3064 | + if (tokens.size() == 4 && tokens[1][1] == '!') { |
| 3065 | + auto allocaKey = atoi(tokens[1].c_str() + 2); |
| 3066 | + auto value0 = ExtractMetaInt32Value(tokens[2]); |
| 3067 | + auto value1 = ExtractMetaInt32Value(tokens[3]); |
| 3068 | + if (value0 != -1 && value1 != -1) { |
| 3069 | + MetadataAllocaWrite aw; |
| 3070 | + aw.allocaDefMetadataKey = allocaKey; |
| 3071 | + aw.size = value0; |
| 3072 | + aw.offset = value1; |
| 3073 | + ret.allocaWrites[key] = aw; |
| 3074 | + } |
| 3075 | + } |
| 3076 | + } |
| 3077 | + } |
| 3078 | + } |
| 3079 | + } else { |
| 3080 | + auto findAw = line.find(allocaRegWriteTag); |
| 3081 | + if (findAw != std::string::npos) { |
| 3082 | + ret.allocaWritesMetaKeys.push_back( |
| 3083 | + atoi(line.c_str() + findAw + strlen(allocaRegWriteTag))); |
| 3084 | + } |
| 3085 | + } |
| 3086 | + } |
| 3087 | + return ret; |
| 3088 | +} |
| 3089 | + |
| 3090 | +TEST_F(PixTest, DebugInstrumentation_VectorAllocaWrite_Structs) { |
| 3091 | + const char *source = R"x( |
| 3092 | +RaytracingAccelerationStructure Scene : register(t0, space0); |
| 3093 | +struct RayPayload |
| 3094 | +{ |
| 3095 | + float4 color; |
| 3096 | +}; |
| 3097 | +RWStructuredBuffer<float> UAV: register(u0); |
| 3098 | +[shader("raygeneration")] |
| 3099 | +void RaygenInternalName() |
| 3100 | +{ |
| 3101 | + RayDesc ray; |
| 3102 | + ray.Origin = float3(UAV[0], UAV[1],UAV[3]); |
| 3103 | + ray.Direction = float3(4.4,5.5,6.6); |
| 3104 | + ray.TMin = 0.001; |
| 3105 | + ray.TMax = 10000.0; |
| 3106 | + RayPayload payload = { float4(0, 1, 0, 1) }; |
| 3107 | + TraceRay(Scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, ~0, 0, 1, 0, ray, payload); |
| 3108 | +})x"; |
| 3109 | + |
| 3110 | + auto compiled = Compile(m_dllSupport, source, L"lib_6_6", {L"-Od"}); |
| 3111 | + auto output = RunDebugPass(compiled); |
| 3112 | + auto disassembly = Disassemble(output.blob); |
| 3113 | + auto lines = Split(disassembly, '\n'); |
| 3114 | + auto metaDataKeyToValue = FindAllocaRelatedMetadata(lines); |
| 3115 | + // To validate that the RayDesc and RayPayload instances were fully covered, |
| 3116 | + // check that there are alloca writes that cover all of them. RayPayload |
| 3117 | + // has four elements, and RayDesc has eight. |
| 3118 | + std::array<bool, 4> RayPayloadElementCoverage; |
| 3119 | + std::array<bool, 8> RayDescElementCoverage; |
| 3120 | + |
| 3121 | + for (auto const &write : metaDataKeyToValue.allocaWrites) { |
| 3122 | + // the whole point of the changes with this test is to separate vector |
| 3123 | + // writes into individual elements: |
| 3124 | + VERIFY_ARE_EQUAL(1, write.second.size); |
| 3125 | + auto findAlloca = metaDataKeyToValue.allocaDefinitions.find( |
| 3126 | + write.second.allocaDefMetadataKey); |
| 3127 | + if (findAlloca != metaDataKeyToValue.allocaDefinitions.end()) { |
| 3128 | + if (findAlloca->second.count == 4) { |
| 3129 | + RayPayloadElementCoverage[write.second.offset] = true; |
| 3130 | + } else if (findAlloca->second.count == 8) { |
| 3131 | + RayDescElementCoverage[write.second.offset] = true; |
| 3132 | + } |
| 3133 | + } |
| 3134 | + } |
| 3135 | + // Check that coverage for every element was emitted: |
| 3136 | + for (auto const &b : RayPayloadElementCoverage) |
| 3137 | + VERIFY_IS_TRUE(b); |
| 3138 | + for (auto const &b : RayDescElementCoverage) |
| 3139 | + VERIFY_IS_TRUE(b); |
| 3140 | +} |
0 commit comments