|
14 | 14 | #endif
|
15 | 15 |
|
16 | 16 | #include <algorithm>
|
| 17 | +#include <array> |
17 | 18 | #include <cassert>
|
18 | 19 | #include <cfloat>
|
19 | 20 | #include <map>
|
@@ -149,6 +150,8 @@ class PixTest : public ::testing::Test {
|
149 | 150 | TEST_METHOD(DebugInstrumentation_TextOutput)
|
150 | 151 | TEST_METHOD(DebugInstrumentation_BlockReport)
|
151 | 152 |
|
| 153 | + TEST_METHOD(DebugInstrumentation_VectorAllocaWrite_Structs) |
| 154 | + |
152 | 155 | dxc::DxcDllSupport m_dllSupport;
|
153 | 156 | VersionSupportInfo m_ver;
|
154 | 157 |
|
@@ -442,6 +445,7 @@ class PixTest : public ::testing::Test {
|
442 | 445 | CComPtr<IDxcBlob> RunDxilPIXDXRInvocationsLog(IDxcBlob *blob);
|
443 | 446 | void TestPixUAVCase(char const *hlsl, wchar_t const *model,
|
444 | 447 | wchar_t const *entry);
|
| 448 | + std::string Disassemble(IDxcBlob *pProgram); |
445 | 449 | };
|
446 | 450 |
|
447 | 451 | bool PixTest::InitSupport() {
|
@@ -1151,6 +1155,16 @@ static bool FindStructMemberFromStore(llvm::StoreInst *S,
|
1151 | 1155 | return false;
|
1152 | 1156 | }
|
1153 | 1157 |
|
| 1158 | +std::string PixTest::Disassemble(IDxcBlob *pProgram) { |
| 1159 | + CComPtr<IDxcCompiler> pCompiler; |
| 1160 | + CComPtr<IDxcOperationResult> pResult; |
| 1161 | + CComPtr<IDxcBlobEncoding> pSource; |
| 1162 | + VERIFY_SUCCEEDED(CreateCompiler(m_dllSupport, &pCompiler)); |
| 1163 | + CComPtr<IDxcBlobEncoding> pDisassembly; |
| 1164 | + VERIFY_SUCCEEDED(pCompiler->Disassemble(pProgram, &pDisassembly)); |
| 1165 | + return BlobToUtf8(pDisassembly); |
| 1166 | +} |
| 1167 | + |
1154 | 1168 | // This function lives in lib\DxilPIXPasses\DxilAnnotateWithVirtualRegister.cpp
|
1155 | 1169 | // Declared here so we can test it.
|
1156 | 1170 | uint32_t CountStructMembers(llvm::Type const *pType);
|
@@ -3023,3 +3037,172 @@ float4 main() : SV_Target {
|
3023 | 3037 | VERIFY_IS_TRUE(foundDoubleAssignment);
|
3024 | 3038 | VERIFY_IS_TRUE(found32BitAllocaStore);
|
3025 | 3039 | }
|
| 3040 | + |
| 3041 | +std::string ExtractBracedSubstring(std::string const &line) { |
| 3042 | + auto open = line.find('{'); |
| 3043 | + auto close = line.find('}'); |
| 3044 | + if (open != std::string::npos && close != std::string::npos && |
| 3045 | + open + 1 < close) { |
| 3046 | + return line.substr(open + 1, close - open - 1); |
| 3047 | + } |
| 3048 | + return {}; |
| 3049 | +} |
| 3050 | + |
| 3051 | +int ExtractMetaInt32Value(std::string const &token) { |
| 3052 | + auto findi32 = token.find("i32 "); |
| 3053 | + if (findi32 != std::string_view::npos) { |
| 3054 | + return atoi( |
| 3055 | + std::string(token.data() + findi32 + 4, token.length() - (findi32 + 4)) |
| 3056 | + .c_str()); |
| 3057 | + } |
| 3058 | + return -1; |
| 3059 | +} |
| 3060 | + |
| 3061 | +std::vector<std::string> Split(std::string str, char delimeter) { |
| 3062 | + std::vector<std::string> lines; |
| 3063 | + |
| 3064 | + auto const *p = str.data(); |
| 3065 | + auto const *justPastPreviousDelimiter = p; |
| 3066 | + while (p < str.data() + str.length()) { |
| 3067 | + if (*p == delimeter) { |
| 3068 | + lines.emplace_back(std::string(justPastPreviousDelimiter, |
| 3069 | + p - justPastPreviousDelimiter)); |
| 3070 | + justPastPreviousDelimiter = p + 1; |
| 3071 | + p = justPastPreviousDelimiter; |
| 3072 | + } else { |
| 3073 | + p++; |
| 3074 | + } |
| 3075 | + } |
| 3076 | + |
| 3077 | + lines.emplace_back( |
| 3078 | + std::string(justPastPreviousDelimiter, p - justPastPreviousDelimiter)); |
| 3079 | + |
| 3080 | + return lines; |
| 3081 | +} |
| 3082 | + |
| 3083 | +struct MetadataAllocaDefinition { |
| 3084 | + int base; |
| 3085 | + int count; |
| 3086 | +}; |
| 3087 | +using AllocaDefinitions = std::map<int, MetadataAllocaDefinition>; |
| 3088 | +struct MetadataAllocaWrite { |
| 3089 | + int allocaDefMetadataKey; |
| 3090 | + int offset; |
| 3091 | + int size; |
| 3092 | +}; |
| 3093 | +using AllocaWrites = std::map<int, MetadataAllocaWrite>; |
| 3094 | + |
| 3095 | +struct AllocaMetadata { |
| 3096 | + AllocaDefinitions allocaDefinitions; |
| 3097 | + AllocaWrites allocaWrites; |
| 3098 | + std::vector<int> allocaWritesMetaKeys; |
| 3099 | +}; |
| 3100 | + |
| 3101 | +AllocaMetadata |
| 3102 | +FindAllocaRelatedMetadata(std::vector<std::string> const &lines) { |
| 3103 | + |
| 3104 | + const char *allocaMetaDataAssignment = "= !{i32 1, "; |
| 3105 | + const char *allocaRegWRiteAssignment = "= !{i32 2, !"; |
| 3106 | + const char *allocaRegWriteTag = "!pix-alloca-reg-write !"; |
| 3107 | + |
| 3108 | + AllocaMetadata ret; |
| 3109 | + for (auto const &line : lines) { |
| 3110 | + if (line[0] == '!') { |
| 3111 | + auto key = atoi(std::string(line.data() + 1, line.length() - 1).c_str()); |
| 3112 | + if (key != -1) { |
| 3113 | + if (line.find(allocaMetaDataAssignment) != std::string::npos) { |
| 3114 | + std::string bitInBraces = ExtractBracedSubstring(line); |
| 3115 | + if (bitInBraces != "") { |
| 3116 | + auto tokens = Split(bitInBraces, ','); |
| 3117 | + if (tokens.size() == 3) { |
| 3118 | + auto value0 = ExtractMetaInt32Value(tokens[1]); |
| 3119 | + auto value1 = ExtractMetaInt32Value(tokens[2]); |
| 3120 | + if (value0 != -1 && value1 != -1) { |
| 3121 | + MetadataAllocaDefinition def; |
| 3122 | + def.base = value0; |
| 3123 | + def.count = value1; |
| 3124 | + ret.allocaDefinitions[key] = def; |
| 3125 | + } |
| 3126 | + } |
| 3127 | + } |
| 3128 | + } else if (line.find(allocaRegWRiteAssignment) != std::string::npos) { |
| 3129 | + std::string bitInBraces = ExtractBracedSubstring(line); |
| 3130 | + if (bitInBraces != "") { |
| 3131 | + auto tokens = Split(bitInBraces, ','); |
| 3132 | + if (tokens.size() == 4 && tokens[1][1] == '!') { |
| 3133 | + auto allocaKey = atoi(tokens[1].c_str() + 2); |
| 3134 | + auto value0 = ExtractMetaInt32Value(tokens[2]); |
| 3135 | + auto value1 = ExtractMetaInt32Value(tokens[3]); |
| 3136 | + if (value0 != -1 && value1 != -1) { |
| 3137 | + MetadataAllocaWrite aw; |
| 3138 | + aw.allocaDefMetadataKey = allocaKey; |
| 3139 | + aw.size = value0; |
| 3140 | + aw.offset = value1; |
| 3141 | + ret.allocaWrites[key] = aw; |
| 3142 | + } |
| 3143 | + } |
| 3144 | + } |
| 3145 | + } |
| 3146 | + } |
| 3147 | + } else { |
| 3148 | + auto findAw = line.find(allocaRegWriteTag); |
| 3149 | + if (findAw != std::string::npos) { |
| 3150 | + ret.allocaWritesMetaKeys.push_back( |
| 3151 | + atoi(line.c_str() + findAw + strlen(allocaRegWriteTag))); |
| 3152 | + } |
| 3153 | + } |
| 3154 | + } |
| 3155 | + return ret; |
| 3156 | +} |
| 3157 | + |
| 3158 | +TEST_F(PixTest, DebugInstrumentation_VectorAllocaWrite_Structs) { |
| 3159 | + const char *source = R"x( |
| 3160 | +RaytracingAccelerationStructure Scene : register(t0, space0); |
| 3161 | +struct RayPayload |
| 3162 | +{ |
| 3163 | + float4 color; |
| 3164 | +}; |
| 3165 | +RWStructuredBuffer<float> UAV: register(u0); |
| 3166 | +[shader("raygeneration")] |
| 3167 | +void RaygenInternalName() |
| 3168 | +{ |
| 3169 | + RayDesc ray; |
| 3170 | + ray.Origin = float3(UAV[0], UAV[1],UAV[3]); |
| 3171 | + ray.Direction = float3(4.4,5.5,6.6); |
| 3172 | + ray.TMin = 0.001; |
| 3173 | + ray.TMax = 10000.0; |
| 3174 | + RayPayload payload = { float4(0, 1, 0, 1) }; |
| 3175 | + TraceRay(Scene, RAY_FLAG_CULL_BACK_FACING_TRIANGLES, ~0, 0, 1, 0, ray, payload); |
| 3176 | +})x"; |
| 3177 | + |
| 3178 | + auto compiled = Compile(m_dllSupport, source, L"lib_6_6", {L"-Od"}); |
| 3179 | + auto output = RunDebugPass(compiled); |
| 3180 | + auto disassembly = Disassemble(output.blob); |
| 3181 | + auto lines = Split(disassembly, '\n'); |
| 3182 | + auto metaDataKeyToValue = FindAllocaRelatedMetadata(lines); |
| 3183 | + // To validate that the RayDesc and RayPayload instances were fully covered, |
| 3184 | + // check that there are alloca writes that cover all of them. RayPayload |
| 3185 | + // has four elements, and RayDesc has eight. |
| 3186 | + std::array<bool, 4> RayPayloadElementCoverage; |
| 3187 | + std::array<bool, 8> RayDescElementCoverage; |
| 3188 | + |
| 3189 | + for (auto const &write : metaDataKeyToValue.allocaWrites) { |
| 3190 | + // the whole point of the changes with this test is to separate vector |
| 3191 | + // writes into individual elements: |
| 3192 | + VERIFY_ARE_EQUAL(1, write.second.size); |
| 3193 | + auto findAlloca = metaDataKeyToValue.allocaDefinitions.find( |
| 3194 | + write.second.allocaDefMetadataKey); |
| 3195 | + if (findAlloca != metaDataKeyToValue.allocaDefinitions.end()) { |
| 3196 | + if (findAlloca->second.count == 4) { |
| 3197 | + RayPayloadElementCoverage[write.second.offset] = true; |
| 3198 | + } else if (findAlloca->second.count == 8) { |
| 3199 | + RayDescElementCoverage[write.second.offset] = true; |
| 3200 | + } |
| 3201 | + } |
| 3202 | + } |
| 3203 | + // Check that coverage for every element was emitted: |
| 3204 | + for (auto const &b : RayPayloadElementCoverage) |
| 3205 | + VERIFY_IS_TRUE(b); |
| 3206 | + for (auto const &b : RayDescElementCoverage) |
| 3207 | + VERIFY_IS_TRUE(b); |
| 3208 | +} |
0 commit comments