瀏覽代碼

ExecutionTest for Writable MSAA (#4387)

Writes sample values to a RWTexture2DMS that are the sample index plus 1
in the shader. It copies that data to an output UAV in a separate shader
then reads it back and verifies that it matches.

Incidentally cleans up some of the DX12 utility functions and comments
Greg Roth 3 年之前
父節點
當前提交
9e56b51775
共有 1 個文件被更改,包括 271 次插入10 次删除
  1. 271 10
      tools/clang/unittests/HLSL/ExecutionTest.cpp

+ 271 - 10
tools/clang/unittests/HLSL/ExecutionTest.cpp

@@ -307,6 +307,7 @@ public:
   TEST_METHOD(ComputeSampleTest);
   TEST_METHOD(ATOProgOffset);
   TEST_METHOD(ATOSampleCmpLevelTest);
+  TEST_METHOD(ATOWriteMSAATest);
   TEST_METHOD(AtomicsTest);
   TEST_METHOD(Atomics64Test);
   TEST_METHOD(AtomicsRawHeap64Test);
@@ -864,12 +865,14 @@ public:
                                      CD3DX12_DESCRIPTOR_RANGE *resRanges, UINT resCt,
                                      CD3DX12_DESCRIPTOR_RANGE *sampRanges = nullptr, UINT sampCt = 0,
                                      D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) {
+    UINT paramCt = 0;
     CD3DX12_ROOT_PARAMETER rootParameters[2];
-    rootParameters[0].InitAsDescriptorTable(resCt, resRanges, D3D12_SHADER_VISIBILITY_ALL);
-    rootParameters[1].InitAsDescriptorTable(sampCt, sampRanges, D3D12_SHADER_VISIBILITY_ALL);
+    rootParameters[paramCt++].InitAsDescriptorTable(resCt, resRanges, D3D12_SHADER_VISIBILITY_ALL);
+    if (sampCt)
+      rootParameters[paramCt++].InitAsDescriptorTable(sampCt, sampRanges, D3D12_SHADER_VISIBILITY_ALL);
 
     CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
-    rootSignatureDesc.Init(_countof(rootParameters), rootParameters, 0, nullptr, flags);
+    rootSignatureDesc.Init(paramCt, rootParameters, 0, nullptr, flags);
     CreateRootSignatureFromDesc(pDevice, &rootSignatureDesc, pRootSig);
   }
 
@@ -1062,6 +1065,14 @@ public:
       uavDesc.Texture2D.MipSlice = 0;
       uavDesc.Texture2D.PlaneSlice = 0;
       break;
+    case D3D12_UAV_DIMENSION_TEXTURE2DARRAY:
+      uavDesc.Texture2DArray.MipSlice = 0;
+      uavDesc.Texture2DArray.PlaneSlice = 0;
+      uavDesc.Texture2DArray.FirstArraySlice = 0;
+      uavDesc.Texture2DArray.ArraySize = numElements;
+      break;
+    default:
+      break;
     }
     pDevice->CreateUnorderedAccessView(pResource, nullptr, &uavDesc, baseHandle);
     baseHandle.Offset(descriptorSize);
@@ -1069,7 +1080,7 @@ public:
 
   void CreateRawUAV(ID3D12Device *pDevice, CD3DX12_CPU_DESCRIPTOR_HANDLE &heapStart,
                     UINT numElements, const CComPtr<ID3D12Resource> pResource) {
-    CreateUAV(pDevice, heapStart, DXGI_FORMAT_R32_TYPELESS, D3D12_UAV_DIMENSION_BUFFER, numElements, 0, pResource);
+    CreateUAV(pDevice, heapStart, DXGI_FORMAT_R32_TYPELESS, D3D12_UAV_DIMENSION_BUFFER, numElements, 0/*stride*/, pResource);
   }
 
   void CreateStructUAV(ID3D12Device *pDevice, CD3DX12_CPU_DESCRIPTOR_HANDLE &heapStart,
@@ -1079,17 +1090,27 @@ public:
 
   void CreateTypedUAV(ID3D12Device *pDevice, CD3DX12_CPU_DESCRIPTOR_HANDLE &heapStart,
                       UINT numElements, DXGI_FORMAT format, const CComPtr<ID3D12Resource> pResource) {
-    CreateUAV(pDevice, heapStart, format, D3D12_UAV_DIMENSION_BUFFER, numElements, 0, pResource);
+    CreateUAV(pDevice, heapStart, format, D3D12_UAV_DIMENSION_BUFFER, numElements, 0/*stride*/, pResource);
   }
 
   void CreateTex1DUAV(ID3D12Device *pDevice, CD3DX12_CPU_DESCRIPTOR_HANDLE &heapStart,
-                      UINT numElements, DXGI_FORMAT format, const CComPtr<ID3D12Resource> pResource) {
-    CreateUAV(pDevice, heapStart, format, D3D12_UAV_DIMENSION_TEXTURE1D, numElements, 0, pResource);
+                      DXGI_FORMAT format, const CComPtr<ID3D12Resource> pResource) {
+    CreateUAV(pDevice, heapStart, format, D3D12_UAV_DIMENSION_TEXTURE1D, 0/*numElements*/, 0/*stride*/, pResource);
   }
 
   void CreateTex2DUAV(ID3D12Device *pDevice, CD3DX12_CPU_DESCRIPTOR_HANDLE &heapStart,
-                 UINT numElements, DXGI_FORMAT format, const CComPtr<ID3D12Resource> pResource) {
-    CreateUAV(pDevice, heapStart, format, D3D12_UAV_DIMENSION_TEXTURE2D, numElements, 0, pResource);
+                      DXGI_FORMAT format, const CComPtr<ID3D12Resource> pResource) {
+    CreateUAV(pDevice, heapStart, format, D3D12_UAV_DIMENSION_TEXTURE2D, 0/*numElements*/, 0/*stride*/, pResource);
+  }
+
+  void CreateTex2DArrayUAV(ID3D12Device *pDevice, CD3DX12_CPU_DESCRIPTOR_HANDLE &heapStart,
+                           UINT numElements, DXGI_FORMAT format, const CComPtr<ID3D12Resource> pResource) {
+    CreateUAV(pDevice, heapStart, format, D3D12_UAV_DIMENSION_TEXTURE2DARRAY, numElements, 0/*stride*/, pResource);
+  }
+
+  void CreateTex2DMSUAV(ID3D12Device *pDevice, CD3DX12_CPU_DESCRIPTOR_HANDLE &heapStart,
+                        DXGI_FORMAT format, const CComPtr<ID3D12Resource> pResource) {
+    CreateUAV(pDevice, heapStart, format, (D3D12_UAV_DIMENSION)6 /*D3D12_UAV_DIMENSION_TEXTURE2DMS*/, 0 /*numElements*/, 0/*stride*/, pResource);
   }
 
   // Create Samplers for <pDevice> given the filter and border color information provided
@@ -1284,6 +1305,30 @@ public:
 #endif
   }
 
+  bool DoesDeviceSupportAdvancedTexOps(ID3D12Device *pDevice) {
+#if defined(NTDDI_WIN10_CU) && WDK_NTDDI_VERSION >= NTDDI_WIN10_CU
+    D3D12_FEATURE_DATA_D3D12_OPTIONS13 O13;
+    if (FAILED(pDevice->CheckFeatureSupport((D3D12_FEATURE)D3D12_FEATURE_D3D12_OPTIONS13, &O13, sizeof(O13))))
+      return false;
+    return O13.AdvancedTextureOpsSupported != FALSE;
+#else
+    UNREFERENCED_PARAMETER(pDevice);
+    return false;
+#endif
+  }
+
+  bool DoesDeviceSupportWritableMSAA(ID3D12Device *pDevice) {
+#if defined(NTDDI_WIN10_CU) && WDK_NTDDI_VERSION >= NTDDI_WIN10_CU
+    D3D12_FEATURE_DATA_D3D12_OPTIONS13 O13;
+    if (FAILED(pDevice->CheckFeatureSupport((D3D12_FEATURE)D3D12_FEATURE_D3D12_OPTIONS13, &O13, sizeof(O13))))
+      return false;
+    return O13.WriteableMSAATexturesSupported != FALSE;
+#else
+    UNREFERENCED_PARAMETER(pDevice);
+    return false;
+#endif
+  }
+
 #ifndef _HLK_CONF
   void DXBCFromText(LPCSTR pText, LPCWSTR pEntryPoint, LPCWSTR pTargetProfile, ID3DBlob **ppBlob) {
     CW2A pEntryPointA(pEntryPoint, CP_UTF8);
@@ -3645,6 +3690,222 @@ TEST_F(ExecutionTest, ComputeSampleTest) {
   }
 }
 
+TEST_F(ExecutionTest, ATOWriteMSAATest) {
+  WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
+
+  //  #define WRITEMSAA_FALLBACK
+
+  CComPtr<ID3D12Device> pDevice;
+#ifdef WRITEMSAA_FALLBACK
+  D3D_SHADER_MODEL sm = D3D_SHADER_MODEL_6_6;
+#else
+  D3D_SHADER_MODEL sm = D3D_SHADER_MODEL_6_7;
+#endif
+  if (!CreateDevice(&pDevice, sm))
+      return;
+
+  if (DoesDeviceSupportAdvancedTexOps(pDevice)) {
+    WEX::Logging::Log::Comment(L"Device does not support Advanced Texture Operations.");
+    WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped);
+  }
+
+  if (DoesDeviceSupportWritableMSAA(pDevice)) {
+    WEX::Logging::Log::Comment(L"Device does not support Writable MSAA.");
+    WEX::Logging::Log::Result(WEX::Logging::TestResults::Skipped);
+  }
+
+  static const char pWriteShader[] =
+    "#define SAMPLES 4\n"
+    "RWStructuredBuffer<float> g_out : register(u0);\n"
+    "#if  __SHADER_TARGET_MAJOR > 6 || (__SHADER_TARGET_MAJOR == 6 && __SHADER_TARGET_MINOR >= 7)\n"
+    "RWTexture2DMS<float, 4> g_texms : register(u1);\n"
+    "#else\n"
+    "RWTexture2DArray<float> g_texms : register(u1);\n"
+    "#endif\n"
+    "[NumThreads(32, 32, 1)]\n"
+    "void main(uint3 id : SV_GroupThreadID) {\n"
+    "  for(uint i = 0; i < SAMPLES; i++) {\n"
+    "#if  __SHADER_TARGET_MAJOR > 6 || (__SHADER_TARGET_MAJOR == 6 && __SHADER_TARGET_MINOR >= 7)\n"
+    "    g_texms.sample[i][id.xy] = id.x*id.y*(i+1);\n"
+    "#else\n"
+    "    g_texms[uint3(id.xy, i)] = id.x*id.y*(i+1);\n"
+    "#endif\n"
+    "  }\n"
+    "}";
+
+  static const char pCopyShader[] =
+    "#define SAMPLES 4\n"
+    "RWStructuredBuffer<float> g_out : register(u0);\n"
+    "#if  __SHADER_TARGET_MAJOR > 6 || (__SHADER_TARGET_MAJOR == 6 && __SHADER_TARGET_MINOR >= 7)\n"
+    "RWTexture2DMS<float, 4> g_texms : register(u1);\n"
+    "#else\n"
+    "RWTexture2DArray<float> g_texms : register(u1);\n"
+    "#endif\n"
+    "[NumThreads(32, 32, 1)]\n"
+    "  void main(uint3 id : SV_GroupThreadID) {\n"
+    "  for(uint i = 0; i < SAMPLES; i++) {\n"
+    "#if  __SHADER_TARGET_MAJOR > 6 || (__SHADER_TARGET_MAJOR == 6 && __SHADER_TARGET_MINOR >= 7)\n"
+    "    g_out[i*32*32 + id.y*32 + id.x] = g_texms.sample[i][id.xy];\n"
+    "#else\n"
+    "    g_out[i*32*32 + id.y*32 + id.x] = g_texms[uint3(id.xy, i)];\n"
+    "#endif\n"
+    "  }"
+    "}";
+
+  static const int NumThreadsX = 32;
+  static const int NumThreadsY = 32;
+
+#ifdef WRITEMSAA_FALLBACK
+  static const int NumSamples = 4;
+  static const int ArraySize = 4;
+#else
+  static const int NumSamples = 4;
+  static const int ArraySize = 1;
+#endif
+  static const int ThreadsPerGroup = NumThreadsX * NumThreadsY;
+  const size_t valueSize = NumSamples * ThreadsPerGroup;
+  const size_t valueSizeInBytes =  valueSize * sizeof(float);
+
+  static const int DispatchGroupX = 1;
+  static const int DispatchGroupY = 1;
+  static const int DispatchGroupZ = 1;
+
+  CComPtr<ID3D12CommandQueue> pCommandQueue;
+  CComPtr<ID3D12CommandAllocator> pCommandAllocator;
+  FenceObj FO;
+
+  CreateComputeCommandQueue(pDevice, L"WriteMSAA Queue", &pCommandQueue);
+  InitFenceObj(pDevice, &FO);
+
+  // Create root signature.
+  CComPtr<ID3D12RootSignature> pRootSignature;
+  CD3DX12_DESCRIPTOR_RANGE ranges[2];
+  ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 0);
+  ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 1, 0);
+
+  CreateRootSignatureFromRanges(pDevice, &pRootSignature, ranges, 2);
+
+  VERIFY_SUCCEEDED(pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COMPUTE, IID_PPV_ARGS(&pCommandAllocator)));
+
+  // Create command list and resources
+  CComPtr<ID3D12GraphicsCommandList> pCommandList;
+  VERIFY_SUCCEEDED(pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COMPUTE,
+                                              pCommandAllocator, nullptr, IID_PPV_ARGS(&pCommandList)));
+
+  // Set up Output Resource
+  CComPtr<ID3D12Resource> pOutputResource;
+  CComPtr<ID3D12Resource> pOutputReadBuffer;
+  CComPtr<ID3D12Resource> pOutputUploadResource;
+
+  float outVals[valueSize];
+  int ix = 0;
+  for (int i = 0; i < NumSamples; i++)
+    for (int j = 0; j < NumThreadsY; j++)
+      for (int k = 0; k < NumThreadsX; k++)
+        outVals[ix++] = (float)ix + 5;
+  CreateTestUavs(pDevice, pCommandList, outVals, sizeof(outVals), &pOutputResource,
+                 &pOutputUploadResource, &pOutputReadBuffer);
+
+  // Set up texture Resource.
+  CComPtr<ID3D12Resource> pUavResource;
+  CComPtr<ID3D12Resource> pUploadResource;
+  float values[valueSize];
+  memset(values, 0xc, valueSizeInBytes);
+
+
+#ifdef WRITEMSAA_FALLBACK
+  int numsamp = 1;
+#else
+  int numsamp = NumSamples;
+#endif
+
+  D3D12_RESOURCE_DESC tex2dDesc = CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_R32_FLOAT,
+                                   NumThreadsX, NumThreadsY, ArraySize, 0, numsamp, 0,
+                                   D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);
+  CreateTestResources(pDevice, pCommandList, values, valueSizeInBytes, tex2dDesc,
+                      &pUavResource, &pUploadResource);
+
+  // Close the command list and execute it to perform the resource uploads
+  pCommandList->Close();
+  ID3D12CommandList *ppCommandLists[] = { pCommandList };
+  pCommandQueue->ExecuteCommandLists(1, ppCommandLists);
+  WaitForSignal(pCommandQueue, FO);
+
+  // Create shaders
+#ifdef WRITEMSAA_FALLBACK
+  const wchar_t *target = L"cs_6_6";
+#else
+  const wchar_t *target = L"cs_6_7";
+#endif
+
+  CComPtr<ID3D12PipelineState> pWritePSO;
+  CreateComputePSO(pDevice, pRootSignature, pWriteShader, target, &pWritePSO);
+  CComPtr<ID3D12PipelineState> pCopyPSO;
+  CreateComputePSO(pDevice, pRootSignature, pCopyShader, target, &pCopyPSO);
+
+  // Reset commandlist to write PSO
+  VERIFY_SUCCEEDED(pCommandList->Reset(pCommandAllocator, pWritePSO));
+
+  // Describe and create a UAV descriptor heap.
+  CComPtr<ID3D12DescriptorHeap> pUavHeap;
+  D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
+  heapDesc.NumDescriptors = 2;
+  heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
+  heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+  VERIFY_SUCCEEDED(pDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&pUavHeap)));
+
+  CD3DX12_CPU_DESCRIPTOR_HANDLE cpuHandle(pUavHeap->GetCPUDescriptorHandleForHeapStart());
+  CreateStructUAV(pDevice, cpuHandle, valueSize, sizeof(float), pOutputResource);
+#ifdef WRITEMSAA_FALLBACK
+  CreateTex2DArrayUAV(pDevice, cpuHandle, NumSamples, DXGI_FORMAT_R32_FLOAT, pUavResource);
+#else
+  CreateTex2DMSUAV(pDevice, cpuHandle, DXGI_FORMAT_R32_FLOAT, pUavResource);
+#endif
+
+  // Set Heaps, Rootsignature and table
+  ID3D12DescriptorHeap *const pHeaps[1] = { pUavHeap };
+  pCommandList->SetDescriptorHeaps(1, pHeaps);
+  pCommandList->SetComputeRootSignature(pRootSignature);
+  pCommandList->SetComputeRootDescriptorTable(0, pUavHeap->GetGPUDescriptorHandleForHeapStart());
+
+  // dispatch and close write shader
+  pCommandList->Dispatch(DispatchGroupX, DispatchGroupY, DispatchGroupZ);
+  pCommandList->Close();
+
+  pCommandQueue->ExecuteCommandLists(1, ppCommandLists);
+  WaitForSignal(pCommandQueue, FO);
+
+  // Create copy command list
+  VERIFY_SUCCEEDED(pCommandList->Reset(pCommandAllocator, pCopyPSO));
+
+  // Set Rootsignature and descriptor tables
+  SetDescriptorHeap(pCommandList, pUavHeap);
+  pCommandList->SetComputeRootSignature(pRootSignature);
+
+  pCommandList->SetComputeRootDescriptorTable(0, pUavHeap->GetGPUDescriptorHandleForHeapStart());
+
+  // Run Copy shader and copy the results back to readable memory
+  pCommandList->Dispatch(DispatchGroupX, DispatchGroupY, DispatchGroupZ);
+
+  CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(pOutputResource,
+                                        D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
+  pCommandList->ResourceBarrier(1, &barrier);
+  pCommandList->CopyResource(pOutputReadBuffer, pOutputResource);
+
+  pCommandList->Close();
+
+  pCommandQueue->ExecuteCommandLists(1, ppCommandLists);
+  WaitForSignal(pCommandQueue, FO);
+
+  MappedData mappedData(pOutputReadBuffer, valueSize*sizeof(float));
+  float *pData = (float *)mappedData.data();
+  ix = 0;
+  for (int i = 0; i < NumSamples; i++)
+    for (int j = 0; j < NumThreadsY; j++)
+      for (int k = 0; k < NumThreadsX; k++)
+        VERIFY_ARE_EQUAL(pData[ix++], j*k*(i+1));
+}
+
 // Used to determine how an out of bounds offset should be converted
 #define CLAMPOFFSET(offset) ((offset<<28)>>28)
 
@@ -8673,7 +8934,7 @@ void ExecutionTest::RunResourceTest(ID3D12Device *pDevice, const char *pShader,
   CreateRawUAV(pDevice, baseHandle, valueSize, pUAVResources[0]);
   CreateStructUAV(pDevice, baseHandle, valueSize, sizeof(float), pUAVResources[1]);
   CreateTypedUAV(pDevice, baseHandle, valueSize, DXGI_FORMAT_R32_FLOAT, pUAVResources[2]);
-  CreateTex1DUAV(pDevice, baseHandle, valueSize, DXGI_FORMAT_R32_FLOAT, pUAVResources[3]);
+  CreateTex1DUAV(pDevice, baseHandle, DXGI_FORMAT_R32_FLOAT, pUAVResources[3]);
 
   D3D12_FILTER filters[] = {D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT, D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT};
   float borderColors[] = {30.0, 31.0};