Initial creation of Secure HLSL spec and conformance tests (#996) * Initial creation of Secure HLSL spec and conformance tests * Initial creation of Secure HLSL spec and conformance tests * Move files to better place in source tree, add CMake build * Move files to better place in source tree, add CMake build * Remove VS solution files
diff --git a/tools/clang/tools/CMakeLists.txt b/tools/clang/tools/CMakeLists.txt index d332430..954f5e3 100644 --- a/tools/clang/tools/CMakeLists.txt +++ b/tools/clang/tools/CMakeLists.txt
@@ -34,3 +34,5 @@ add_subdirectory(dotnetc) add_subdirectory(dxlib-sample) # HLSL Change Ends + +add_subdirectory(SecureHLSL) \ No newline at end of file
diff --git a/tools/clang/tools/SecureHLSL/CMakeLists.txt b/tools/clang/tools/SecureHLSL/CMakeLists.txt new file mode 100644 index 0000000..57328c1 --- /dev/null +++ b/tools/clang/tools/SecureHLSL/CMakeLists.txt
@@ -0,0 +1 @@ +add_subdirectory(HLSLTestEngine)
diff --git a/tools/clang/tools/SecureHLSL/Conformance/FirstTest.hlsl b/tools/clang/tools/SecureHLSL/Conformance/FirstTest.hlsl new file mode 100644 index 0000000..4975801 --- /dev/null +++ b/tools/clang/tools/SecureHLSL/Conformance/FirstTest.hlsl
@@ -0,0 +1,10 @@ +RWByteAddressBuffer g_bab : register(u0); + +[numthreads(8,8,1)] +void main(uint GI : SV_GroupIndex) +{ + uint addr = GI * 4; + uint val = g_bab.Load(addr); + g_bab.Store(addr, val + 1); +} +
diff --git a/tools/clang/tools/SecureHLSL/Conformance/FirstTests.json b/tools/clang/tools/SecureHLSL/Conformance/FirstTests.json new file mode 100644 index 0000000..1b72319 --- /dev/null +++ b/tools/clang/tools/SecureHLSL/Conformance/FirstTests.json
@@ -0,0 +1,11 @@ +{ + "TestList": + [ + { + "name" : "FirstTest", + "source" : "FirstTest.hlsl", + "input" : [0, 1, 2, 3, 4, 5, 6, 7], + "output" : [1, 2, 3, 4, 5, 6, 7, 8] + } + ] +}
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/CMakeLists.txt b/tools/clang/tools/SecureHLSL/HLSLTestEngine/CMakeLists.txt new file mode 100644 index 0000000..cca525b --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/CMakeLists.txt
@@ -0,0 +1,24 @@ +# Copyright (C) Microsoft Corporation. All rights reserved. +# This file is distributed under the University of Illinois Open Source License. See LICENSE.TXT for details. +# Build HLSLTestEngine.exe as a test harness + +set(SOURCES + FenceWrapper.cpp + Main.cpp + MappedData.cpp + SimpleTextFile.cpp + ) + +include_directories(../../../UnitTests/HLSL) + +add_clang_executable(HLSLTestEngine ${SOURCES}) + +target_compile_definitions(HLSLTestEngine PRIVATE UNICODE) + +target_link_libraries(HLSLTestEngine d3d12) +target_link_libraries(HLSLTestEngine dxgi) +target_link_libraries(HLSLTestEngine runtimeobject) + +set_target_properties(HLSLTestEngine + PROPERTIES + LINKER_LANGUAGE CXX)
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/Common.h b/tools/clang/tools/SecureHLSL/HLSLTestEngine/Common.h new file mode 100644 index 0000000..1b78668 --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/Common.h
@@ -0,0 +1,25 @@ +#pragma once + +#include <stdio.h> +#include <windows.h> +#include <d3d12.h> +#include <d3dx12.h> +#include <dxgi1_4.h> +#include <wrl.h> +#include <vector> +#include <memory> +#include <stdint.h> +#include <DXC\Support\dxcapi.use.h> +#include <atlbase.h> +#include <atlconv.h> +#include <DXProgrammableCapture.h> +#include <Windows.Data.Json.h> +#include <Windows.Foundation.Collections.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Data::Json; + +#define IFT(x) do { hr = (x); if (FAILED(hr)) { throw hr; } } while (false)
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/FenceWrapper.cpp b/tools/clang/tools/SecureHLSL/HLSLTestEngine/FenceWrapper.cpp new file mode 100644 index 0000000..20e831b --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/FenceWrapper.cpp
@@ -0,0 +1,30 @@ +#include "FenceWrapper.h" + +FenceWrapper::FenceWrapper(ID3D12Device *pDevice) +{ + HRESULT hr; + + m_fenceValue = 1; + IFT(pDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_spFence))); + + m_fenceEvent.reset(::CreateEvent(nullptr, FALSE, FALSE, nullptr)); + if (m_fenceEvent == nullptr) + { + IFT(HRESULT_FROM_WIN32(GetLastError())); + } +} + +void FenceWrapper::WaitForSignal(ID3D12CommandQueue *pCQ) +{ + HRESULT hr; + + IFT(pCQ->Signal(m_spFence.Get(), m_fenceValue)); + + if (m_spFence->GetCompletedValue() < m_fenceValue) + { + IFT(m_spFence->SetEventOnCompletion(m_fenceValue, m_fenceEvent.get())); + ::WaitForSingleObject(m_fenceEvent.get(), INFINITE); + } + + m_fenceValue++; +}
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/FenceWrapper.h b/tools/clang/tools/SecureHLSL/HLSLTestEngine/FenceWrapper.h new file mode 100644 index 0000000..d81269e --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/FenceWrapper.h
@@ -0,0 +1,25 @@ +#pragma once + +#include "Common.h" + +struct HANDLECloser +{ + void operator()(HANDLE hToClose) + { + ::CloseHandle(hToClose); + } +}; + +class FenceWrapper +{ +public: + FenceWrapper() = delete; + FenceWrapper(ID3D12Device *pDevice); + + void WaitForSignal(ID3D12CommandQueue *pCQ); + +private: + std::unique_ptr<void, HANDLECloser> m_fenceEvent; + ComPtr<ID3D12Fence> m_spFence; + UINT64 m_fenceValue; +};
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/Main.cpp b/tools/clang/tools/SecureHLSL/HLSLTestEngine/Main.cpp new file mode 100644 index 0000000..96300ec --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/Main.cpp
@@ -0,0 +1,402 @@ +#include "Common.h" +#include "SimpleTextFile.h" +#include "FenceWrapper.h" +#include "MappedData.h" + +void SetDescriptorHeap(ID3D12GraphicsCommandList *pCommandList, ID3D12DescriptorHeap *pHeap) +{ + ID3D12DescriptorHeap *const pHeaps[1] = { pHeap }; + pCommandList->SetDescriptorHeaps(ARRAYSIZE(pHeaps), pHeaps); +} + +void ExecuteCommandList(ID3D12CommandQueue *pQueue, ID3D12CommandList *pList) +{ + ID3D12CommandList *ppCommandLists[] = { pList }; + pQueue->ExecuteCommandLists(ARRAYSIZE(ppCommandLists), ppCommandLists); +} + +void CreateComputeCommandQueue(ID3D12Device *pDevice, LPCWSTR pName, ID3D12CommandQueue **ppCommandQueue) +{ + HRESULT hr; + + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE; + IFT(pDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(ppCommandQueue))); + IFT((*ppCommandQueue)->SetName(pName)); +} + +void CreateRootSignatureFromDesc(ID3D12Device *pDevice, const D3D12_ROOT_SIGNATURE_DESC *pDesc, ID3D12RootSignature **pRootSig) +{ + HRESULT hr; + + ComPtr<ID3DBlob> spSignature; + ComPtr<ID3DBlob> spError; + IFT(D3D12SerializeRootSignature(pDesc, D3D_ROOT_SIGNATURE_VERSION_1, &spSignature, &spError)); + IFT(pDevice->CreateRootSignature(0, spSignature->GetBufferPointer(), spSignature->GetBufferSize(), IID_PPV_ARGS(pRootSig))); +} + +void CompileFromText(LPCSTR pText, LPCWSTR pEntryPoint, LPCWSTR pTargetProfile, ID3DBlob **ppBlob) +{ + HRESULT hr; + + dxc::DxcDllSupport support; + IFT(support.Initialize()); + + ComPtr<IDxcCompiler2> spCompiler; + IFT(support.CreateInstance(CLSID_DxcCompiler, spCompiler.ReleaseAndGetAddressOf())); + + ComPtr<IDxcLibrary> spLibrary; + IFT(support.CreateInstance(CLSID_DxcLibrary, spLibrary.ReleaseAndGetAddressOf())); + + ComPtr<IDxcBlobEncoding> spTextBlob; + IFT(spLibrary->CreateBlobWithEncodingFromPinned((LPBYTE)pText, (UINT32)strlen(pText), CP_UTF8, &spTextBlob)); + + const wchar_t* pszArgs = L"/Zi"; + + ComPtr<IDxcOperationResult> spResult; + IFT(spCompiler->Compile( + spTextBlob.Get(), + L"hlsl.hlsl", + pEntryPoint, + pTargetProfile, + &pszArgs, + 1, + nullptr, + 0, + nullptr, + &spResult + )); + + HRESULT resultCode = S_OK; + IFT(spResult->GetStatus(&resultCode)); + + if (FAILED(resultCode)) + { + ComPtr<IDxcBlobEncoding> spErrors; + IFT(spResult->GetErrorBuffer(&spErrors)); + } + + IFT(resultCode); + IFT(spResult->GetResult(reinterpret_cast<IDxcBlob **>(ppBlob))); +} + +void CreateComputePSO(ID3D12Device *pDevice, ID3D12RootSignature *pRootSignature, LPCSTR pszShader, ID3D12PipelineState **pspComputeState) +{ + HRESULT hr; + + // Load and compile shaders. + ComPtr<ID3DBlob> spComputeShader; + CompileFromText(pszShader, L"main", L"cs_6_0", spComputeShader.ReleaseAndGetAddressOf()); + + // Describe and create the compute pipeline state object (PSO). + D3D12_COMPUTE_PIPELINE_STATE_DESC computePsoDesc = {}; + computePsoDesc.pRootSignature = pRootSignature; + computePsoDesc.CS = CD3DX12_SHADER_BYTECODE(spComputeShader.Get()); + computePsoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_TOOL_DEBUG; + + IFT(pDevice->CreateComputePipelineState(&computePsoDesc, IID_PPV_ARGS(pspComputeState))); +} + +void RecordTransitionBarrier(ID3D12GraphicsCommandList *pCommandList, + ID3D12Resource *pResource, + D3D12_RESOURCE_STATES before, + D3D12_RESOURCE_STATES after) +{ + CD3DX12_RESOURCE_BARRIER barrier(CD3DX12_RESOURCE_BARRIER::Transition(pResource, before, after)); + + pCommandList->ResourceBarrier(1, &barrier); +} + +void CreateTestUavs(ID3D12Device *pDevice, + ID3D12GraphicsCommandList *pCommandList, + LPCVOID values, + UINT valueSizeInBytes, + ID3D12Resource **ppUavResource, + ID3D12Resource **ppReadBuffer, + ID3D12Resource **ppUploadResource) +{ + HRESULT hr; + + ComPtr<ID3D12Resource> spUavResource; + D3D12_HEAP_PROPERTIES defaultHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); + D3D12_RESOURCE_DESC bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(valueSizeInBytes, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); + IFT(pDevice->CreateCommittedResource( + &defaultHeapProperties, + D3D12_HEAP_FLAG_NONE, + &bufferDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_PPV_ARGS(&spUavResource))); + + ComPtr<ID3D12Resource> spUploadResource; + D3D12_HEAP_PROPERTIES uploadHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); + D3D12_RESOURCE_DESC uploadBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(valueSizeInBytes); + IFT(pDevice->CreateCommittedResource( + &uploadHeapProperties, + D3D12_HEAP_FLAG_NONE, + &uploadBufferDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&spUploadResource))); + + ComPtr<ID3D12Resource> spReadBuffer; + CD3DX12_HEAP_PROPERTIES readHeap(D3D12_HEAP_TYPE_READBACK); + CD3DX12_RESOURCE_DESC readDesc(CD3DX12_RESOURCE_DESC::Buffer(valueSizeInBytes)); + IFT(pDevice->CreateCommittedResource( + &readHeap, + D3D12_HEAP_FLAG_NONE, + &readDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_PPV_ARGS(&spReadBuffer))); + + D3D12_SUBRESOURCE_DATA transferData; + transferData.pData = values; + transferData.RowPitch = valueSizeInBytes; + transferData.SlicePitch = transferData.RowPitch; + + UpdateSubresources<1>(pCommandList, spUavResource.Get(), spUploadResource.Get(), 0, 0, 1, &transferData); + RecordTransitionBarrier(pCommandList, spUavResource.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + + *ppUavResource = spUavResource.Detach(); + *ppReadBuffer = spReadBuffer.Detach(); + *ppUploadResource = spUploadResource.Detach(); +} + +void JsonArrayToVector(IJsonArray* pArray, std::vector<uint32_t> &vec) +{ + HRESULT hr; + + ComPtr<IVector<IJsonValue *>> spInputListVector; + IFT(pArray->QueryInterface(IID_PPV_ARGS(&spInputListVector))); + + unsigned int inputSize = 0; + IFT(spInputListVector->get_Size(&inputSize)); + + vec.resize(inputSize); + + for (unsigned int inputIndex = 0; inputIndex < inputSize; inputIndex++) + { + double value = 0.0; + IFT(pArray->GetNumberAt(inputIndex, &value)); + vec[inputIndex] = static_cast<uint32_t>(value); + } +} + +int __cdecl main(void) +{ + HRESULT hr; + + try + { + // Initialize the Windows Runtime. + RoInitializeWrapper initialize(RO_INIT_MULTITHREADED); + + ComPtr<IJsonObjectStatics> spJsonStaticsFactory; + IFT(GetActivationFactory(HStringReference(RuntimeClass_Windows_Data_Json_JsonObject).Get(), &spJsonStaticsFactory)); + + // Read the test metadata file + SimpleTextFile jsonFile(L"..\\Conformance\\FirstTests.json"); + std::vector<wchar_t> jsonContents; + + // Convert to wide when reading + jsonFile.ReadFile(jsonContents); + + // Json object wants a Platform::String + ComPtr<IJsonObject> spTestListJson; + IFT(spJsonStaticsFactory->Parse(HStringReference(jsonContents.data()).Get(), &spTestListJson)); + + ComPtr<IJsonArray> spTestList; + IFT(spTestListJson->GetNamedArray(HStringReference(L"TestList").Get(), &spTestList)); + + dxc::DxcDllSupport support; + IFT(support.Initialize()); + + // Take the debug layer if we can get it + ComPtr<ID3D12Debug> spDebugController; + if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&spDebugController)))) + { + spDebugController->EnableDebugLayer(); + } + + ComPtr<IDXGIFactory4> spFactory; + IFT(CreateDXGIFactory1(IID_PPV_ARGS(&spFactory))); + + ComPtr<IDXGIAdapter> spAdapter; + IFT(spFactory->EnumWarpAdapter(IID_PPV_ARGS(&spAdapter))); + + ComPtr<ID3D12Device> spDevice; + IFT(D3D12CreateDevice(spAdapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&spDevice))); + + ComPtr<IDXGraphicsAnalysis> spGA; + DXGIGetDebugInterface1(0, IID_PPV_ARGS(&spGA)); + + if (spGA != nullptr) + { + spGA->BeginCapture(); + } + + static const int DispatchGroupX = 1; + static const int DispatchGroupY = 1; + static const int DispatchGroupZ = 1; + + ComPtr<ID3D12CommandQueue> spCommandQueue; + CreateComputeCommandQueue(spDevice.Get(), L"RunRWByteBufferComputeTest Command Queue", &spCommandQueue); + + // Describe and create a UAV descriptor heap. + D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; + heapDesc.NumDescriptors = 1; + heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + + ComPtr<ID3D12DescriptorHeap> spUavHeap; + IFT(spDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&spUavHeap))); + + // Create root signature. + ComPtr<ID3D12RootSignature> spRootSignature; + { + CD3DX12_DESCRIPTOR_RANGE ranges[1]; + ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 1, 0, 0, 0); + + CD3DX12_ROOT_PARAMETER rootParameters[1]; + rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_ALL); + + CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc; + rootSignatureDesc.Init(_countof(rootParameters), rootParameters, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_NONE); + + CreateRootSignatureFromDesc(spDevice.Get(), &rootSignatureDesc, &spRootSignature); + } + + ComPtr<IJsonObject> spFirstEntry; + IFT(spTestList->GetObjectAt(0, &spFirstEntry)); + + HString testName; + IFT(spFirstEntry->GetNamedString(HStringReference(L"name").Get(), testName.GetAddressOf())); + + // Load up the input data to the shader + ComPtr<IJsonArray> spInputList; + IFT(spFirstEntry->GetNamedArray(HStringReference(L"input").Get(), &spInputList)); + + std::vector<uint32_t> InputData; + JsonArrayToVector(spInputList.Get(), InputData); + + const UINT valueSizeInBytes = static_cast<UINT>(InputData.size() * sizeof(uint32_t)); + + // Load up the expected output + ComPtr<IJsonArray> spExpectedList; + IFT(spFirstEntry->GetNamedArray(HStringReference(L"output").Get(), &spExpectedList)); + + std::vector<uint32_t> ExpectedData; + JsonArrayToVector(spExpectedList.Get(), ExpectedData); + + HString hlslSource; + IFT(spFirstEntry->GetNamedString(HStringReference(L"source").Get(), hlslSource.GetAddressOf())); + + HString hlslSourceWithPath; + IFT(::WindowsConcatString(HStringReference(L"..\\Conformance\\").Get(), hlslSource.Get(), hlslSourceWithPath.GetAddressOf())); + + SimpleTextFile hlslFile(::WindowsGetStringRawBuffer(hlslSourceWithPath.Get(), nullptr)); + + std::vector<char> shaderSource; + hlslFile.ReadFile(shaderSource); + + // Create pipeline state object. + ComPtr<ID3D12PipelineState> spComputeState; + CreateComputePSO(spDevice.Get(), spRootSignature.Get(), shaderSource.data(), &spComputeState); + + // Create a command allocator and list for compute. + ComPtr<ID3D12CommandAllocator> spCommandAllocator; + IFT(spDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COMPUTE, IID_PPV_ARGS(&spCommandAllocator))); + + ComPtr<ID3D12GraphicsCommandList> spCommandList; + IFT(spDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COMPUTE, spCommandAllocator.Get(), spComputeState.Get(), IID_PPV_ARGS(&spCommandList))); + spCommandList->SetName(L"ExecutionTest::RunRWByteButterComputeTest Command List"); + + // Set up UAV resource. + ComPtr<ID3D12Resource> spUavResource; + ComPtr<ID3D12Resource> spReadBuffer; + ComPtr<ID3D12Resource> spUploadResource; + CreateTestUavs(spDevice.Get(), spCommandList.Get(), InputData.data(), valueSizeInBytes, &spUavResource, &spReadBuffer, &spUploadResource); + IFT(spUavResource->SetName(L"RunRWByteBufferComputeText UAV")); + IFT(spReadBuffer->SetName(L"RunRWByteBufferComputeText UAV Read Buffer")); + IFT(spUploadResource->SetName(L"RunRWByteBufferComputeText UAV Upload Buffer")); + + // Close the command list and execute it to perform the GPU setup. + spCommandList->Close(); + ExecuteCommandList(spCommandQueue.Get(), spCommandList.Get()); + + // Wait for the command list to be done + FenceWrapper FO(spDevice.Get()); + FO.WaitForSignal(spCommandQueue.Get()); + + IFT(spCommandAllocator->Reset()); + IFT(spCommandList->Reset(spCommandAllocator.Get(), spComputeState.Get())); + + // Run the compute shader and copy the results back to readable memory. + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + uavDesc.Format = DXGI_FORMAT_R32_TYPELESS; + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uavDesc.Buffer.FirstElement = 0; + uavDesc.Buffer.NumElements = (UINT)InputData.size(); + uavDesc.Buffer.StructureByteStride = 0; + uavDesc.Buffer.CounterOffsetInBytes = 0; + uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; + CD3DX12_CPU_DESCRIPTOR_HANDLE uavHandle(spUavHeap->GetCPUDescriptorHandleForHeapStart()); + spDevice->CreateUnorderedAccessView(spUavResource.Get(), nullptr, &uavDesc, uavHandle); + + SetDescriptorHeap(spCommandList.Get(), spUavHeap.Get()); + spCommandList->SetComputeRootSignature(spRootSignature.Get()); + + CD3DX12_GPU_DESCRIPTOR_HANDLE uavHandleGpu(spUavHeap->GetGPUDescriptorHandleForHeapStart()); + spCommandList->SetComputeRootDescriptorTable(0, uavHandleGpu); + + spCommandList->Dispatch(DispatchGroupX, DispatchGroupY, DispatchGroupZ); + RecordTransitionBarrier(spCommandList.Get(), spUavResource.Get(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE); + spCommandList->CopyResource(spReadBuffer.Get(), spUavResource.Get()); + spCommandList->Close(); + ExecuteCommandList(spCommandQueue.Get(), spCommandList.Get()); + FO.WaitForSignal(spCommandQueue.Get()); + + // Readback shader result + std::vector<uint32_t> OutputData; + OutputData.resize(InputData.size()); + + { + MappedData mappedData(spReadBuffer.Get(), valueSizeInBytes); + uint32_t *pData = static_cast<uint32_t *>(mappedData.GetData()); + memcpy(OutputData.data(), pData, valueSizeInBytes); + } + + FO.WaitForSignal(spCommandQueue.Get()); + + if (spGA != nullptr) + { + spGA->EndCapture(); + } + + PCWSTR pszTestName = ::WindowsGetStringRawBuffer(testName.Get(), nullptr); + + // Check that output matches expected + size_t check; + for (check = 0; check < ExpectedData.size(); check++) + { + if (ExpectedData[check] != OutputData[check]) + { + ::wprintf(L"Test %s Failed\n", pszTestName); + break; + } + } + + if (check == OutputData.size()) + { + ::wprintf(L"Test %s Passed\n", pszTestName); + } + } + catch (HRESULT hrFail) + { + ::wprintf(L"Error %x\n", hrFail); + } + + return 0; +}
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/MappedData.cpp b/tools/clang/tools/SecureHLSL/HLSLTestEngine/MappedData.cpp new file mode 100644 index 0000000..4a03b78 --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/MappedData.cpp
@@ -0,0 +1,51 @@ +#include "MappedData.h" + +MappedData::MappedData() : m_pData(nullptr), m_size(0) +{ +} + +MappedData::MappedData(ID3D12Resource *pResource, UINT32 sizeInBytes) : m_pData(nullptr), m_size(0) +{ + ExecuteMap(pResource, sizeInBytes); +} + +MappedData::~MappedData() +{ + Reset(); +} + +void *MappedData::GetData() +{ + return m_pData; +} + +UINT32 MappedData::GetSize() const +{ + return m_size; +} + +void MappedData::Reset() +{ + if (_spResource != nullptr) + { + _spResource->Unmap(0, nullptr); + _spResource.Reset(); + } + + m_pData = nullptr; +} + +void MappedData::ExecuteMap(ID3D12Resource *pResource, UINT32 sizeInBytes) +{ + HRESULT hr; + + Reset(); + + D3D12_RANGE ResourceRange; + ResourceRange.Begin = 0; + ResourceRange.End = sizeInBytes; + IFT(pResource->Map(0, &ResourceRange, &m_pData)); + + _spResource = pResource; + m_size = sizeInBytes; +}
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/MappedData.h b/tools/clang/tools/SecureHLSL/HLSLTestEngine/MappedData.h new file mode 100644 index 0000000..8f3dcd3 --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/MappedData.h
@@ -0,0 +1,23 @@ +#pragma once + +#include "Common.h" + +class MappedData +{ +public: + MappedData(); + MappedData(ID3D12Resource *pResource, UINT32 sizeInBytes); + ~MappedData(); + + void *GetData(); + UINT32 GetSize() const; + void Reset(); + +private: + void ExecuteMap(ID3D12Resource *pResource, UINT32 sizeInBytes); + +private: + ComPtr<ID3D12Resource> _spResource; + void *m_pData; + UINT32 m_size; +};
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/Readme.md b/tools/clang/tools/SecureHLSL/HLSLTestEngine/Readme.md new file mode 100644 index 0000000..d2224eb --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/Readme.md
@@ -0,0 +1,9 @@ +# HLSL Test Engine + +## How to build + +This project is meant to be used with Visual Studio 2017 15.4 and the Windows SDK version 10.0.16299.15. + +After building you can run the program from the HLSLTestEngine directory and it will find the Conformance test path and run the tests. + +This uses WARP for D3D12 rendering so that it will have consistent results on each machine.
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/SimpleTextFile.cpp b/tools/clang/tools/SecureHLSL/HLSLTestEngine/SimpleTextFile.cpp new file mode 100644 index 0000000..4904473 --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/SimpleTextFile.cpp
@@ -0,0 +1,86 @@ +#include "SimpleTextFile.h" + +SimpleTextFile::SimpleTextFile(const wchar_t* pszFilename) +{ + HRESULT hr; + + _hFile = ::CreateFile(pszFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (_hFile == nullptr) + { + IFT(E_FAIL); + } +} + +SimpleTextFile::~SimpleTextFile() +{ + if (_hFile != nullptr) + { + ::CloseHandle(_hFile); + } +} + +void SimpleTextFile::ReadFile(std::vector<char> &fileBytes) +{ + HRESULT hr; + + LARGE_INTEGER fileSize; + if (!::GetFileSizeEx(_hFile, &fileSize)) + { + IFT(E_FAIL); + } + + if (fileSize.HighPart != 0) + { + // Not expecting gigantic HLSL files + IFT(E_UNEXPECTED); + } + + // We know now how much we need to store the whole file + fileBytes.resize(fileSize.LowPart + 2); + + DWORD dwRead; + if (!::ReadFile(_hFile, fileBytes.data(), fileSize.LowPart, &dwRead, nullptr)) + { + IFT(E_FAIL); + } + + // Terminate file data so it can be cast to a string + fileBytes[fileSize.LowPart] = 0; + fileBytes[fileSize.LowPart + 1] = 0; +} + +void SimpleTextFile::ReadFile(std::vector<wchar_t> &fileBytes) +{ + HRESULT hr; + + // First read raw data + std::vector<char> rawFile; + ReadFile(rawFile); + + // Find out the needed buffer size + const int utf16Length = ::MultiByteToWideChar( + CP_UTF8, + MB_ERR_INVALID_CHARS, + rawFile.data(), + -1, + nullptr, + 0); + + if (utf16Length == 0) + { + IFT(E_FAIL); + } + + fileBytes.resize(utf16Length); + + if (!::MultiByteToWideChar( + CP_UTF8, + MB_ERR_INVALID_CHARS, + rawFile.data(), + -1, + fileBytes.data(), + static_cast<int>(fileBytes.size()))) + { + IFT(E_FAIL); + } +}
diff --git a/tools/clang/tools/SecureHLSL/HLSLTestEngine/SimpleTextFile.h b/tools/clang/tools/SecureHLSL/HLSLTestEngine/SimpleTextFile.h new file mode 100644 index 0000000..7a34ede --- /dev/null +++ b/tools/clang/tools/SecureHLSL/HLSLTestEngine/SimpleTextFile.h
@@ -0,0 +1,16 @@ +#pragma once + +#include "Common.h" + +class SimpleTextFile +{ +public: + SimpleTextFile(const wchar_t* pszFilename); + ~SimpleTextFile(); + + void ReadFile(std::vector<char> &fileBytes); + void ReadFile(std::vector<wchar_t> &fileBytes); + +private: + HANDLE _hFile; +};
diff --git a/tools/clang/tools/SecureHLSL/SecureHLSLSpec.md b/tools/clang/tools/SecureHLSL/SecureHLSLSpec.md new file mode 100644 index 0000000..57c4baf --- /dev/null +++ b/tools/clang/tools/SecureHLSL/SecureHLSLSpec.md
@@ -0,0 +1,76 @@ +Secure HLSL Specification + +Contents + +1. Introduction +2. Basics +3. Variables and Types +4. Operators and Expressions +5. Statements +6. Builtin identifiers +7. Grammar +8. Errors +9. Open Issues + +# Variables and Types + +All variables and functions must be declared before being referenced. Variables and functions are referenced with an Identifier. There is no concept of a default type - all variable declarations and function declarations must specify types. + +Identifier tokens are defined in 2.TODO. The grammar in section 7 shows the variety of declaration statement syntax. Multiple variables can be declared in a single statement, and declarations can initialize most types. + +Types can be split into three categories - basic types, object types and user defined types. + +## Basic Types + +Type | Description +--- | --- +void | Only valid when describing the return type of a function +bool | Boolean value that can only be true or false +int | Scalar 32 bit signed integer +uint | Scalar 32 bit unsigned integer +dword | Scalar 32 bit unsigned integer +float | Scalar 32 bit float +<type>*N* | Vector of *type* with dimension N (1 <= N <= 4) +<type>*N*x*M* | Matrix of *type* with N rows and M columns (1 <= N | M <= 4) + +There is no string type in Secure HLSL. + +## Object Types + +Type | Description +--- | --- +SamplerState | Texture sampler +SamplerComparisonState | Texture sampler with comparison +Texture*N*D | Texture with N dimensions + +## User Defined Types + +User defined types are defined with the struct keyword and allow the aggregation of basic types and other structures. The members can be arrays. + +## Arrays + +All types can be declared with arrays with positive nonzero integer size. Only single dimensional arrays are allowed. + +# Operators and Expressions + +## Array subscripting + +The square bracket operator [*index*] is used to access an element of an array. + +Accessing an index that is out of bounds for the declared array results in a zeroed result being returned. If the variable is a struct then +the members of the struct are all zeroed out. + +Accessing a texture or sampler array with an out of bounds index will result in a *null object* being returned. Any subsequent operations +done on this object result in a zeroed output being returned. + +# Open Issues + +- Should half / double / min16 / min10 be part of secure HLSL? +- Vector<type, size> vs <type>N? +- How many variants of vector / matrix declaration will be allowed? +- Interpolation modifiers? +- Do structs hide previous type names? +- Local struct definitions? +- Array access with non-int types? +- Multidimensional arrays? +- Is null object the way we want to handle these cases? \ No newline at end of file