Browse Source

Add code for dispatch graph

Panagiotis Christopoulos Charitos 1 year ago
parent
commit
693b9dbcaf

+ 7 - 7
AnKi/Core/CVarSet.cpp

@@ -33,22 +33,22 @@ void CVarSet::registerCVar(CVar* cvar)
 	m_cvars.pushBack(cvar);
 }
 
-Error CVarSet::setFromCommandLineArguments(U32 cmdLineArgsCount, char* cmdLineArgs[])
+Error CVarSet::setMultiple(ConstWeakArray<const Char*> arr)
 {
-	for(U i = 0; i < cmdLineArgsCount; ++i)
+	for(U i = 0; i < arr.getSize(); ++i)
 	{
-		ANKI_ASSERT(cmdLineArgs[i]);
-		const CString varName = cmdLineArgs[i];
+		ANKI_ASSERT(arr[i]);
+		const CString varName = arr[i];
 
 		// Get the value string
 		++i;
-		if(i >= cmdLineArgsCount)
+		if(i >= arr.getSize())
 		{
 			ANKI_CORE_LOGE("Expecting a command line argument after %s", varName.cstr());
 			return Error::kUserData;
 		}
-		ANKI_ASSERT(cmdLineArgs[i]);
-		const CString value = cmdLineArgs[i];
+		ANKI_ASSERT(arr[i]);
+		const CString value = arr[i];
 
 		// Find the CVar
 		CVar* foundCVar = nullptr;

+ 14 - 1
AnKi/Core/CVarSet.h

@@ -10,6 +10,7 @@
 #include <AnKi/Util/String.h>
 #include <AnKi/Util/Singleton.h>
 #include <AnKi/Util/Enum.h>
+#include <AnKi/Util/WeakArray.h>
 #include <AnKi/Math/Functions.h>
 
 namespace anki {
@@ -215,7 +216,19 @@ public:
 
 	Error saveToFile(CString filename) const;
 
-	Error setFromCommandLineArguments(U32 cmdLineArgsCount, char* cmdLineArgs[]);
+	Error setFromCommandLineArguments(U32 cmdLineArgsCount, Char* cmdLineArgs[])
+	{
+		ConstWeakArray<const Char*> arr(cmdLineArgs, cmdLineArgsCount);
+		return setMultiple(arr);
+	}
+
+	template<U32 kTCount>
+	Error setMultiple(Array<const Char*, kTCount> arr)
+	{
+		return setMultiple(ConstWeakArray<const Char*>(arr.getBegin(), kTCount));
+	}
+
+	Error setMultiple(ConstWeakArray<const Char*> arr);
 
 private:
 	IntrusiveList<CVar> m_cvars;

+ 4 - 2
AnKi/Gr/CMakeLists.txt

@@ -101,13 +101,15 @@ if(ANKI_DLSS)
 	endif()
 endif()
 
-# Copy Agility SDK files to Binaries
+# Copy DX DLLs next to the binaries
 if(DIRECTX)
 	file(GLOB FILES_TO_COPY "${CMAKE_CURRENT_SOURCE_DIR}/../../ThirdParty/AgilitySdk/bin/x64/*.dll")
 	file(GLOB FILES_TO_COPY2 "${CMAKE_CURRENT_SOURCE_DIR}/../../ThirdParty/AgilitySdk/bin/x64/*.pdb")
 	file(GLOB FILES_TO_COPY3 "${CMAKE_CURRENT_SOURCE_DIR}/../../ThirdParty/Pix/bin/x64/*.dll")
+	file(GLOB FILES_TO_COPY4 "${CMAKE_CURRENT_SOURCE_DIR}/../../ThirdParty/Warp/bin/x64/*.dll")
+	file(GLOB FILES_TO_COPY5 "${CMAKE_CURRENT_SOURCE_DIR}/../../ThirdParty/Warp/bin/x64/*.pdb")
 
-	list(APPEND FILES_TO_COPY ${FILES_TO_COPY2} ${FILES_TO_COPY3})
+	list(APPEND FILES_TO_COPY ${FILES_TO_COPY2} ${FILES_TO_COPY3} ${FILES_TO_COPY4} ${FILES_TO_COPY5})
 
 	foreach(FILE_TO_COPY IN LISTS FILES_TO_COPY)
 		add_custom_command(TARGET AnKiGr POST_BUILD

+ 8 - 0
AnKi/Gr/CommandBuffer.h

@@ -98,6 +98,12 @@ public:
 		: GrBaseInitInfo(name)
 	{
 	}
+
+	CommandBufferInitInfo(CommandBufferFlag flags, CString name = {})
+		: GrBaseInitInfo(name)
+		, m_flags(flags)
+	{
+	}
 };
 
 /// Maps to HLSL register(X#, S)
@@ -336,6 +342,8 @@ public:
 
 	void dispatchComputeIndirect(const BufferView& argBuffer);
 
+	void dispatchGraph(const BufferView& scratchBuffer, const void* records, U32 recordCount, U32 recordStride);
+
 	/// Trace rays.
 	///
 	/// The 1st thing in the sbtBuffer is the ray gen shader group handle:

+ 6 - 3
AnKi/Gr/Common.h

@@ -227,6 +227,9 @@ public:
 
 	/// Has access to barycentrics.
 	Bool m_barycentrics = false;
+
+	/// WorkGraphs
+	Bool m_workGraphs = false;
 };
 ANKI_END_PACKED_STRUCT
 
@@ -556,7 +559,7 @@ enum class ShaderType : U16
 	kMiss,
 	kIntersection,
 	kCallable,
-	kWorkgraph,
+	kWorkGraph,
 
 	kCount,
 	kFirst = 0,
@@ -584,7 +587,7 @@ enum class ShaderTypeBit : U16
 	kMiss = 1 << 11,
 	kIntersection = 1 << 12,
 	kCallable = 1 << 13,
-	kWorkgraph = 1 << 14,
+	kWorkGraph = 1 << 14,
 
 	kNone = 0,
 	kAllGraphics = kVertex | kTessellationControl | kTessellationEvaluation | kGeometry | kTask | kMesh | kFragment,
@@ -592,7 +595,7 @@ enum class ShaderTypeBit : U16
 	kAllModernGeometry = kTask | kMesh,
 	kAllRayTracing = kRayGen | kAnyHit | kClosestHit | kMiss | kIntersection | kCallable,
 	kAllHit = kAnyHit | kClosestHit,
-	kAll = kAllGraphics | kCompute | kAllRayTracing | kWorkgraph,
+	kAll = kAllGraphics | kCompute | kAllRayTracing | kWorkGraph,
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(ShaderTypeBit)
 

+ 2 - 2
AnKi/Gr/D3D/D3DBuffer.cpp

@@ -96,7 +96,7 @@ Error BufferImpl::init(const BufferInitInfo& inf)
 		heapProperties.Type = D3D12_HEAP_TYPE_CUSTOM;
 		heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE;
 
-		if(getGrManagerImpl().getD3DCapabilities().m_rebar)
+		if(getGrManagerImpl().getD3DCapabilities().m_rebar && getGrManagerImpl().getDeviceCapabilities().m_discreteGpu)
 		{
 			heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_L1;
 		}
@@ -137,7 +137,7 @@ Error BufferImpl::init(const BufferInitInfo& inf)
 	D3D12_RESOURCE_DESC resourceDesc = {};
 	resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
 	resourceDesc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
-	resourceDesc.Width = getAlignedRoundUp(m_size, 256) + 256; // Align to 256 and add padding because of CBV requirements
+	resourceDesc.Width = getAlignedRoundUp(256, m_size) + 256; // Align to 256 and add padding because of CBV requirements
 	resourceDesc.Height = 1;
 	resourceDesc.DepthOrArraySize = 1;
 	resourceDesc.MipLevels = 1;

+ 42 - 4
AnKi/Gr/D3D/D3DCommandBuffer.cpp

@@ -296,20 +296,29 @@ void CommandBuffer::bindShaderProgram(ShaderProgram* prog)
 
 	self.commandCommon();
 
-	const ShaderProgramImpl& progImpl = static_cast<const ShaderProgramImpl&>(*prog);
-	const Bool isCompute = !(progImpl.getShaderTypes() & ShaderTypeBit::kAllGraphics);
+	self.m_mcmdb->pushObjectRef(prog);
 
-	self.m_descriptors.bindRootSignature(progImpl.m_rootSignature, isCompute);
+	const ShaderProgramImpl& progImpl = static_cast<const ShaderProgramImpl&>(*prog);
+	const Bool isCompute = !!(progImpl.getShaderTypes() & ShaderTypeBit::kCompute);
+	const Bool isGraphics = !!(progImpl.getShaderTypes() & ShaderTypeBit::kAllGraphics);
+	const Bool isWg = !!(progImpl.getShaderTypes() & ShaderTypeBit::kWorkGraph);
 
-	self.m_mcmdb->pushObjectRef(prog);
+	self.m_descriptors.bindRootSignature(progImpl.m_rootSignature, isCompute || isWg);
 
 	if(isCompute)
 	{
 		self.m_cmdList->SetPipelineState(progImpl.m_compute.m_pipelineState);
+		self.m_wgProg = nullptr;
+	}
+	else if(isWg)
+	{
+		self.m_wgProg = &progImpl;
 	}
 	else
 	{
+		ANKI_ASSERT(isGraphics);
 		self.m_graphicsState.bindShaderProgram(prog);
+		self.m_wgProg = nullptr;
 	}
 
 	// Shader program means descriptors so bind the descriptor heaps
@@ -856,6 +865,35 @@ void CommandBuffer::popDebugMarker()
 	}
 }
 
+void CommandBuffer::dispatchGraph(const BufferView& scratchBuffer, const void* records, U32 recordCount, U32 recordStride)
+{
+	ANKI_D3D_SELF(CommandBufferImpl);
+
+	ANKI_ASSERT(records && recordStride >= sizeof(U32) * 3);
+	ANKI_ASSERT(self.m_wgProg);
+	ANKI_ASSERT(self.m_wgProg->getWorkGraphMemoryRequirements() == scratchBuffer.getRange());
+
+	self.dispatchCommon();
+
+	// Setup program
+	D3D12_SET_PROGRAM_DESC setProg = {};
+	setProg.Type = D3D12_PROGRAM_TYPE_WORK_GRAPH;
+	setProg.WorkGraph.ProgramIdentifier = self.m_wgProg->m_workGraph.m_progIdentifier;
+	setProg.WorkGraph.Flags = D3D12_SET_WORK_GRAPH_FLAG_INITIALIZE;
+	setProg.WorkGraph.BackingMemory = {.StartAddress = scratchBuffer.getBuffer().getGpuAddress() + scratchBuffer.getOffset(),
+									   .SizeInBytes = scratchBuffer.getRange()};
+	self.m_cmdList->SetProgram(&setProg);
+
+	// Dispatch the graph
+	D3D12_DISPATCH_GRAPH_DESC dispDesc = {};
+	dispDesc.Mode = D3D12_DISPATCH_MODE_NODE_CPU_INPUT;
+	dispDesc.NodeCPUInput.EntrypointIndex = 0; // just one entrypoint in this graph
+	dispDesc.NodeCPUInput.NumRecords = recordCount;
+	dispDesc.NodeCPUInput.RecordStrideInBytes = recordStride;
+	dispDesc.NodeCPUInput.pRecords = records;
+	self.m_cmdList->DispatchGraph(&dispDesc);
+}
+
 CommandBufferImpl::~CommandBufferImpl()
 {
 }

+ 2 - 0
AnKi/Gr/D3D/D3DCommandBuffer.h

@@ -53,6 +53,8 @@ private:
 	DynamicArray<QueryHandle, MemoryPoolPtrWrapper<StackMemoryPool>> m_timestampQueries;
 	DynamicArray<QueryHandle, MemoryPoolPtrWrapper<StackMemoryPool>> m_pipelineQueries;
 
+	const ShaderProgramImpl* m_wgProg = nullptr;
+
 	Bool m_descriptorHeapsBound = false;
 	Bool m_debugMarkersEnabled = false;
 	Bool m_lineWidthWarningAlreadyShown = false;

+ 4 - 2
AnKi/Gr/D3D/D3DCommon.h

@@ -20,12 +20,14 @@
 #include <windows.h>
 #include <d3d12.h>
 #include <d3dx12/d3dx12_pipeline_state_stream.h>
+#include <d3dx12/d3dx12_state_object.h>
 #include <dxgi1_6.h>
 #include <dxgidebug.h>
 #include <D3Dcompiler.h>
 #include <DirectXMath.h>
 #include <wrl.h>
 #include <pix3.h>
+#include <atlcomcli.h>
 #include <AnKi/Util/CleanupWindows.h>
 
 using Microsoft::WRL::ComPtr;
@@ -76,8 +78,8 @@ void invokeDred();
 	} while(0)
 
 // Globaly define the versions of some D3D objects
-using D3D12GraphicsCommandListX = ID3D12GraphicsCommandList9;
-using ID3D12DeviceX = ID3D12Device2;
+using D3D12GraphicsCommandListX = ID3D12GraphicsCommandList10;
+using ID3D12DeviceX = ID3D12Device14;
 
 enum class D3DTextureViewType : U8
 {

+ 14 - 1
AnKi/Gr/D3D/D3DGrManager.cpp

@@ -44,6 +44,7 @@ static NumericCVar<U8> g_deviceCVar(CVarSubsystem::kGr, "Device", 0, 0, 16, "Cho
 static BoolCVar g_rayTracingCVar(CVarSubsystem::kGr, "RayTracing", false, "Try enabling ray tracing");
 static BoolCVar g_dredCVar(CVarSubsystem::kGr, "Dred", false, "Enable DRED");
 static BoolCVar g_vrsCVar(CVarSubsystem::kGr, "Vrs", false, "Enable or not VRS");
+static BoolCVar g_workGraphcsCVar(CVarSubsystem::kGr, "WorkGraphs", false, "Enable or not WorkGraphs");
 
 static void NTAPI d3dDebugMessageCallback([[maybe_unused]] D3D12_MESSAGE_CATEGORY category, D3D12_MESSAGE_SEVERITY severity,
 										  [[maybe_unused]] D3D12_MESSAGE_ID id, LPCSTR pDescription, [[maybe_unused]] void* pContext)
@@ -359,7 +360,7 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 
 	// Create device
 	ComPtr<ID3D12Device> dev;
-	ANKI_D3D_CHECK(D3D12CreateDevice(adapters[chosenPhysDevIdx].m_adapter.Get(), D3D_FEATURE_LEVEL_12_2, IID_PPV_ARGS(&dev)));
+	ANKI_D3D_CHECK(D3D12CreateDevice(adapters[chosenPhysDevIdx].m_adapter.Get(), D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS(&dev)));
 	ANKI_D3D_CHECK(dev->QueryInterface(IID_PPV_ARGS(&m_device)));
 
 	if(g_validationCVar.get())
@@ -409,6 +410,18 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 		ANKI_D3D_CHECK(m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS16, &options16, sizeof(options16)));
 		D3D12_FEATURE_DATA_ARCHITECTURE architecture = {.NodeIndex = 0};
 		ANKI_D3D_CHECK(m_device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &architecture, sizeof(architecture)));
+		D3D12_FEATURE_DATA_D3D12_OPTIONS21 options21;
+		ANKI_D3D_CHECK(m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS21, &options21, sizeof(options21)));
+
+		if(g_workGraphcsCVar.get() && options21.WorkGraphsTier == D3D12_WORK_GRAPHS_TIER_NOT_SUPPORTED)
+		{
+			ANKI_D3D_LOGW("WorkGraphs can't be enabled. They not supported");
+		}
+		else if(g_workGraphcsCVar.get() && options21.WorkGraphsTier != D3D12_WORK_GRAPHS_TIER_NOT_SUPPORTED)
+		{
+			ANKI_D3D_LOGV("WorkGraphs supported");
+			m_capabilities.m_workGraphs = true;
+		}
 
 		m_d3dCapabilities.m_rebar = options16.GPUUploadHeapSupported;
 		if(!m_d3dCapabilities.m_rebar)

+ 44 - 0
AnKi/Gr/D3D/D3DShaderProgram.cpp

@@ -39,6 +39,7 @@ Buffer& ShaderProgram::getShaderGroupHandlesGpuBuffer() const
 ShaderProgramImpl::~ShaderProgramImpl()
 {
 	safeRelease(m_compute.m_pipelineState);
+	safeRelease(m_workGraph.m_stateObject);
 
 	deleteInstance(GrMemoryPool::getSingleton(), m_graphics.m_pipelineFactory);
 }
@@ -62,6 +63,10 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 			}
 		}
 	}
+	else if(inf.m_workGraphShader)
+	{
+		m_shaders.emplaceBack(inf.m_workGraphShader);
+	}
 	else
 	{
 		ANKI_ASSERT(!"TODO");
@@ -77,6 +82,7 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 	const Bool isGraphicsProg = !!(m_shaderTypes & ShaderTypeBit::kAllGraphics);
 	const Bool isComputeProg = !!(m_shaderTypes & ShaderTypeBit::kCompute);
 	const Bool isRtProg = !!(m_shaderTypes & ShaderTypeBit::kAllRayTracing);
+	const Bool isWorkGraph = !!(m_shaderTypes & ShaderTypeBit::kWorkGraph);
 
 	// Link reflection
 	ShaderReflection refl;
@@ -127,6 +133,44 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 		ANKI_D3D_CHECK(getDevice().CreateComputePipelineState(&desc, IID_PPV_ARGS(&m_compute.m_pipelineState)));
 	}
 
+	// Create the shader object if workgraph
+	if(isWorkGraph)
+	{
+		const WChar* wgName = L"main";
+
+		const ShaderImpl& shaderImpl = static_cast<const ShaderImpl&>(*m_shaders[0]);
+
+		// Init sub-objects
+		CD3DX12_STATE_OBJECT_DESC stateObj(D3D12_STATE_OBJECT_TYPE_EXECUTABLE);
+
+		auto lib = stateObj.CreateSubobject<CD3DX12_DXIL_LIBRARY_SUBOBJECT>();
+		CD3DX12_SHADER_BYTECODE libCode(shaderImpl.m_binary.getBegin(), shaderImpl.m_binary.getSizeInBytes());
+		lib->SetDXILLibrary(&libCode);
+
+		auto rootSigSubObj = stateObj.CreateSubobject<CD3DX12_GLOBAL_ROOT_SIGNATURE_SUBOBJECT>();
+		rootSigSubObj->SetRootSignature(&m_rootSignature->getD3DRootSignature());
+
+		auto wgSubObj = stateObj.CreateSubobject<CD3DX12_WORK_GRAPH_SUBOBJECT>();
+		wgSubObj->IncludeAllAvailableNodes(); // Auto populate the graph
+		wgSubObj->SetProgramName(wgName);
+
+		// Create state obj
+		ANKI_D3D_CHECK(getDevice().CreateStateObject(stateObj, IID_PPV_ARGS(&m_workGraph.m_stateObject)));
+
+		// Create misc
+		ComPtr<ID3D12StateObjectProperties1> spSOProps;
+		ANKI_D3D_CHECK(m_workGraph.m_stateObject->QueryInterface(IID_PPV_ARGS(&spSOProps)));
+		m_workGraph.m_progIdentifier = spSOProps->GetProgramIdentifier(wgName);
+
+		ComPtr<ID3D12WorkGraphProperties> spWGProps;
+		ANKI_D3D_CHECK(m_workGraph.m_stateObject->QueryInterface(IID_PPV_ARGS(&spWGProps)));
+		const UINT wgIndex = spWGProps->GetWorkGraphIndex(wgName);
+		D3D12_WORK_GRAPH_MEMORY_REQUIREMENTS memReqs;
+		spWGProps->GetWorkGraphMemoryRequirements(wgIndex, &memReqs);
+
+		m_workGraphScratchBufferSize = memReqs.MaxSizeInBytes;
+	}
+
 	// Get shader sizes and a few other things
 	for(const ShaderPtr& s : m_shaders)
 	{

+ 7 - 0
AnKi/Gr/D3D/D3DShaderProgram.h

@@ -35,6 +35,13 @@ public:
 		ID3D12PipelineState* m_pipelineState = nullptr;
 	} m_compute;
 
+	class
+	{
+	public:
+		ID3D12StateObject* m_stateObject = nullptr;
+		D3D12_PROGRAM_IDENTIFIER m_progIdentifier = {};
+	} m_workGraph;
+
 	ShaderProgramImpl(CString name)
 		: ShaderProgram(name)
 	{

+ 4 - 6
AnKi/Gr/ShaderProgram.cpp

@@ -106,17 +106,15 @@ Bool ShaderProgramInitInfo::isValid() const
 		rtMask |= localRtMask;
 	}
 
-	if(!!rtMask && (!!graphicsMask || compute))
+	if(!!rtMask && m_rayTracingShaders.m_maxRecursionDepth == 0)
 	{
 		return false;
 	}
 
-	if(!graphicsMask && !compute && !rtMask)
-	{
-		return false;
-	}
+	const Bool workGraph = m_workGraphShader != nullptr;
 
-	if(!!rtMask && m_rayTracingShaders.m_maxRecursionDepth == 0)
+	const U32 options = !!graphicsMask + compute + !!rtMask + workGraph;
+	if(options != 1)
 	{
 		return false;
 	}

+ 11 - 0
AnKi/Gr/ShaderProgram.h

@@ -44,6 +44,9 @@ public:
 	/// Option 3
 	RayTracingShaders m_rayTracingShaders;
 
+	/// Option 4
+	Shader* m_workGraphShader = nullptr;
+
 	ShaderProgramInitInfo(CString name = {})
 		: GrBaseInitInfo(name)
 	{
@@ -98,9 +101,17 @@ public:
 		return m_refl;
 	}
 
+	PtrSize getWorkGraphMemoryRequirements() const
+	{
+		ANKI_ASSERT(m_workGraphScratchBufferSize);
+		return m_workGraphScratchBufferSize;
+	}
+
 protected:
 	Array<U32, U32(ShaderType::kCount)> m_shaderBinarySizes = {};
 
+	PtrSize m_workGraphScratchBufferSize = 0;
+
 	ShaderTypeBit m_shaderTypes = ShaderTypeBit::kNone;
 
 	ShaderReflection m_refl;

+ 6 - 0
AnKi/Gr/Vulkan/VkCommandBuffer.cpp

@@ -674,6 +674,12 @@ void CommandBuffer::dispatchComputeIndirect(const BufferView& argBuffer)
 	vkCmdDispatchIndirect(self.m_handle, static_cast<BufferImpl&>(argBuffer.getBuffer()).getHandle(), argBuffer.getOffset());
 }
 
+void CommandBuffer::dispatchGraph([[maybe_unused]] const BufferView& scratchBuffer, [[maybe_unused]] const void* records,
+								  [[maybe_unused]] U32 recordCount, [[maybe_unused]] U32 recordStride)
+{
+	ANKI_ASSERT(!"Not supported");
+}
+
 void CommandBuffer::traceRays(const BufferView& sbtBuffer, U32 sbtRecordSize32, U32 hitGroupSbtRecordCount, U32 rayTypeCount, U32 width, U32 height,
 							  U32 depth)
 {

+ 1 - 1
AnKi/ShaderCompiler/Dxc.cpp

@@ -48,7 +48,7 @@ static CString profile(ShaderType shaderType)
 	case ShaderType::kMiss:
 	case ShaderType::kIntersection:
 	case ShaderType::kCallable:
-	case ShaderType::kWorkgraph:
+	case ShaderType::kWorkGraph:
 		return "lib_6_8";
 		break;
 	default:

+ 126 - 119
AnKi/ShaderCompiler/ShaderCompiler.cpp

@@ -398,32 +398,17 @@ Error doReflectionDxil(ConstWeakArray<U8> dxil, ShaderType type, ShaderReflectio
 		}
 	}
 
-	const Bool isRayTracing = type >= ShaderType::kFirstRayTracing && type <= ShaderType::kLastRayTracing;
-	if(isRayTracing)
-	{
-		// TODO: Skip for now. RT shaders require explicity register()
-		return Error::kNone;
-	}
+	const Bool isLib = (type >= ShaderType::kFirstRayTracing && type <= ShaderType::kLastRayTracing) || type == ShaderType::kWorkGraph;
 
 	ComPtr<IDxcUtils> utils;
 	ANKI_REFL_CHECK(g_DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&utils)));
 
 	ComPtr<ID3D12ShaderReflection> dxRefl;
 	ComPtr<ID3D12LibraryReflection> libRefl;
-	ID3D12FunctionReflection* funcRefl = nullptr;
+	ShaderCompilerDynamicArray<ID3D12FunctionReflection*> funcReflections;
 	D3D12_SHADER_DESC shaderDesc = {};
-	U32 bindingCount = 0;
-
-	if(!isRayTracing)
-	{
-		const DxcBuffer buff = {dxil.getBegin(), dxil.getSizeInBytes(), 0};
-		ANKI_REFL_CHECK(utils->CreateReflection(&buff, IID_PPV_ARGS(&dxRefl)));
 
-		ANKI_REFL_CHECK(dxRefl->GetDesc(&shaderDesc));
-
-		bindingCount = shaderDesc.BoundResources;
-	}
-	else
+	if(isLib)
 	{
 		const DxcBuffer buff = {dxil.getBegin(), dxil.getSizeInBytes(), 0};
 		ANKI_REFL_CHECK(utils->CreateReflection(&buff, IID_PPV_ARGS(&libRefl)));
@@ -431,131 +416,153 @@ Error doReflectionDxil(ConstWeakArray<U8> dxil, ShaderType type, ShaderReflectio
 		D3D12_LIBRARY_DESC libDesc = {};
 		libRefl->GetDesc(&libDesc);
 
-		if(libDesc.FunctionCount != 1)
+		if(libDesc.FunctionCount == 0)
 		{
-			errorStr.sprintf("Expecting 1 in D3D12_LIBRARY_DESC::FunctionCount");
+			errorStr.sprintf("Expecting at least 1 in D3D12_LIBRARY_DESC::FunctionCount");
 			return Error::kUserData;
 		}
 
-		funcRefl = libRefl->GetFunctionByIndex(0);
+		funcReflections.resize(libDesc.FunctionCount);
+		for(U32 i = 0; i < libDesc.FunctionCount; ++i)
+		{
 
-		D3D12_FUNCTION_DESC funcDesc;
-		ANKI_REFL_CHECK(funcRefl->GetDesc(&funcDesc));
+			funcReflections[i] = libRefl->GetFunctionByIndex(i);
+		}
+	}
+	else
+	{
+		const DxcBuffer buff = {dxil.getBegin(), dxil.getSizeInBytes(), 0};
+		ANKI_REFL_CHECK(utils->CreateReflection(&buff, IID_PPV_ARGS(&dxRefl)));
 
-		bindingCount = funcDesc.BoundResources;
+		ANKI_REFL_CHECK(dxRefl->GetDesc(&shaderDesc));
 	}
 
-	for(U32 i = 0; i < bindingCount; ++i)
+	for(U32 ifunc = 0; ifunc < ((isLib) ? funcReflections.getSize() : 1); ++ifunc)
 	{
-		D3D12_SHADER_INPUT_BIND_DESC bindDesc;
-		if(dxRefl.Get() != nullptr)
+		U32 bindingCount;
+		if(isLib)
 		{
-			ANKI_REFL_CHECK(dxRefl->GetResourceBindingDesc(i, &bindDesc));
+			D3D12_FUNCTION_DESC funcDesc;
+			ANKI_REFL_CHECK(funcReflections[ifunc]->GetDesc(&funcDesc));
+			bindingCount = funcDesc.BoundResources;
 		}
 		else
 		{
-			ANKI_REFL_CHECK(funcRefl->GetResourceBindingDesc(i, &bindDesc));
+			bindingCount = shaderDesc.BoundResources;
 		}
 
-		ShaderReflectionBinding akBinding;
+		for(U32 i = 0; i < bindingCount; ++i)
+		{
+			D3D12_SHADER_INPUT_BIND_DESC bindDesc;
+			if(isLib)
+			{
+				ANKI_REFL_CHECK(funcReflections[ifunc]->GetResourceBindingDesc(i, &bindDesc));
+			}
+			else
+			{
+				ANKI_REFL_CHECK(dxRefl->GetResourceBindingDesc(i, &bindDesc));
+			}
 
-		akBinding.m_type = DescriptorType::kCount;
-		akBinding.m_flags = DescriptorFlag::kNone;
-		akBinding.m_arraySize = U16(bindDesc.BindCount);
-		akBinding.m_registerBindingPoint = bindDesc.BindPoint;
+			ShaderReflectionBinding akBinding;
 
-		if(bindDesc.Type == D3D_SIT_CBUFFER)
-		{
-			// ConstantBuffer
+			akBinding.m_type = DescriptorType::kCount;
+			akBinding.m_flags = DescriptorFlag::kNone;
+			akBinding.m_arraySize = U16(bindDesc.BindCount);
+			akBinding.m_registerBindingPoint = bindDesc.BindPoint;
 
-			if(bindDesc.Space == 3000 && bindDesc.BindPoint == 0)
+			if(bindDesc.Type == D3D_SIT_CBUFFER)
 			{
-				// It's push/root constants
+				// ConstantBuffer
 
-				ID3D12ShaderReflectionConstantBuffer* cbuffer =
-					(dxRefl.Get()) ? dxRefl->GetConstantBufferByName(bindDesc.Name) : funcRefl->GetConstantBufferByName(bindDesc.Name);
-				D3D12_SHADER_BUFFER_DESC desc;
-				ANKI_REFL_CHECK(cbuffer->GetDesc(&desc));
-				refl.m_descriptor.m_pushConstantsSize = desc.Size;
+				if(bindDesc.Space == 3000 && bindDesc.BindPoint == 0)
+				{
+					// It's push/root constants
 
-				continue;
+					ID3D12ShaderReflectionConstantBuffer* cbuffer =
+						(isLib) ? funcReflections[ifunc]->GetConstantBufferByName(bindDesc.Name) : dxRefl->GetConstantBufferByName(bindDesc.Name);
+					D3D12_SHADER_BUFFER_DESC desc;
+					ANKI_REFL_CHECK(cbuffer->GetDesc(&desc));
+					refl.m_descriptor.m_pushConstantsSize = desc.Size;
+
+					continue;
+				}
+
+				akBinding.m_type = DescriptorType::kUniformBuffer;
+				akBinding.m_flags = DescriptorFlag::kRead;
+			}
+			else if(bindDesc.Type == D3D_SIT_TEXTURE && bindDesc.Dimension != D3D_SRV_DIMENSION_BUFFER)
+			{
+				// Texture2D etc
+				akBinding.m_type = DescriptorType::kTexture;
+				akBinding.m_flags = DescriptorFlag::kRead;
+			}
+			else if(bindDesc.Type == D3D_SIT_TEXTURE && bindDesc.Dimension == D3D_SRV_DIMENSION_BUFFER)
+			{
+				// Buffer
+				akBinding.m_type = DescriptorType::kTexelBuffer;
+				akBinding.m_flags = DescriptorFlag::kRead;
+			}
+			else if(bindDesc.Type == D3D_SIT_SAMPLER)
+			{
+				// SamplerState
+				akBinding.m_type = DescriptorType::kSampler;
+				akBinding.m_flags = DescriptorFlag::kRead;
+			}
+			else if(bindDesc.Type == D3D_SIT_UAV_RWTYPED && bindDesc.Dimension == D3D_SRV_DIMENSION_BUFFER)
+			{
+				// RWBuffer
+				akBinding.m_type = DescriptorType::kTexelBuffer;
+				akBinding.m_flags = DescriptorFlag::kReadWrite;
+			}
+			else if(bindDesc.Type == D3D_SIT_UAV_RWTYPED && bindDesc.Dimension != D3D_SRV_DIMENSION_BUFFER)
+			{
+				// RWTexture2D etc
+				akBinding.m_type = DescriptorType::kTexture;
+				akBinding.m_flags = DescriptorFlag::kReadWrite;
+			}
+			else if(bindDesc.Type == D3D_SIT_BYTEADDRESS)
+			{
+				// ByteAddressBuffer
+				akBinding.m_type = DescriptorType::kStorageBuffer;
+				akBinding.m_flags = DescriptorFlag::kRead | DescriptorFlag::kByteAddressBuffer;
+				akBinding.m_d3dStructuredBufferStride = sizeof(U32);
+			}
+			else if(bindDesc.Type == D3D_SIT_UAV_RWBYTEADDRESS)
+			{
+				// RWByteAddressBuffer
+				akBinding.m_type = DescriptorType::kStorageBuffer;
+				akBinding.m_flags = DescriptorFlag::kReadWrite | DescriptorFlag::kByteAddressBuffer;
+				akBinding.m_d3dStructuredBufferStride = sizeof(U32);
+			}
+			else if(bindDesc.Type == D3D_SIT_RTACCELERATIONSTRUCTURE)
+			{
+				// RaytracingAccelerationStructure
+				akBinding.m_type = DescriptorType::kAccelerationStructure;
+				akBinding.m_flags = DescriptorFlag::kRead;
+			}
+			else if(bindDesc.Type == D3D_SIT_STRUCTURED)
+			{
+				// StructuredBuffer
+				akBinding.m_type = DescriptorType::kStorageBuffer;
+				akBinding.m_flags = DescriptorFlag::kRead;
+				akBinding.m_d3dStructuredBufferStride = U16(bindDesc.NumSamples);
+			}
+			else if(bindDesc.Type == D3D_SIT_UAV_RWSTRUCTURED)
+			{
+				// RWStructuredBuffer
+				akBinding.m_type = DescriptorType::kStorageBuffer;
+				akBinding.m_flags = DescriptorFlag::kReadWrite;
+				akBinding.m_d3dStructuredBufferStride = U16(bindDesc.NumSamples);
+			}
+			else
+			{
+				errorStr.sprintf("Unrecognized type for binding: %s", bindDesc.Name);
+				return Error::kUserData;
 			}
 
-			akBinding.m_type = DescriptorType::kUniformBuffer;
-			akBinding.m_flags = DescriptorFlag::kRead;
-		}
-		else if(bindDesc.Type == D3D_SIT_TEXTURE && bindDesc.Dimension != D3D_SRV_DIMENSION_BUFFER)
-		{
-			// Texture2D etc
-			akBinding.m_type = DescriptorType::kTexture;
-			akBinding.m_flags = DescriptorFlag::kRead;
-		}
-		else if(bindDesc.Type == D3D_SIT_TEXTURE && bindDesc.Dimension == D3D_SRV_DIMENSION_BUFFER)
-		{
-			// Buffer
-			akBinding.m_type = DescriptorType::kTexelBuffer;
-			akBinding.m_flags = DescriptorFlag::kRead;
-		}
-		else if(bindDesc.Type == D3D_SIT_SAMPLER)
-		{
-			// SamplerState
-			akBinding.m_type = DescriptorType::kSampler;
-			akBinding.m_flags = DescriptorFlag::kRead;
-		}
-		else if(bindDesc.Type == D3D_SIT_UAV_RWTYPED && bindDesc.Dimension == D3D_SRV_DIMENSION_BUFFER)
-		{
-			// RWBuffer
-			akBinding.m_type = DescriptorType::kTexelBuffer;
-			akBinding.m_flags = DescriptorFlag::kReadWrite;
-		}
-		else if(bindDesc.Type == D3D_SIT_UAV_RWTYPED && bindDesc.Dimension != D3D_SRV_DIMENSION_BUFFER)
-		{
-			// RWTexture2D etc
-			akBinding.m_type = DescriptorType::kTexture;
-			akBinding.m_flags = DescriptorFlag::kReadWrite;
-		}
-		else if(bindDesc.Type == D3D_SIT_BYTEADDRESS)
-		{
-			// ByteAddressBuffer
-			akBinding.m_type = DescriptorType::kStorageBuffer;
-			akBinding.m_flags = DescriptorFlag::kRead | DescriptorFlag::kByteAddressBuffer;
-			akBinding.m_d3dStructuredBufferStride = sizeof(U32);
-		}
-		else if(bindDesc.Type == D3D_SIT_UAV_RWBYTEADDRESS)
-		{
-			// RWByteAddressBuffer
-			akBinding.m_type = DescriptorType::kStorageBuffer;
-			akBinding.m_flags = DescriptorFlag::kReadWrite | DescriptorFlag::kByteAddressBuffer;
-			akBinding.m_d3dStructuredBufferStride = sizeof(U32);
-		}
-		else if(bindDesc.Type == D3D_SIT_RTACCELERATIONSTRUCTURE)
-		{
-			// RaytracingAccelerationStructure
-			akBinding.m_type = DescriptorType::kAccelerationStructure;
-			akBinding.m_flags = DescriptorFlag::kRead;
-		}
-		else if(bindDesc.Type == D3D_SIT_STRUCTURED)
-		{
-			// StructuredBuffer
-			akBinding.m_type = DescriptorType::kStorageBuffer;
-			akBinding.m_flags = DescriptorFlag::kRead;
-			akBinding.m_d3dStructuredBufferStride = U16(bindDesc.NumSamples);
-		}
-		else if(bindDesc.Type == D3D_SIT_UAV_RWSTRUCTURED)
-		{
-			// RWStructuredBuffer
-			akBinding.m_type = DescriptorType::kStorageBuffer;
-			akBinding.m_flags = DescriptorFlag::kReadWrite;
-			akBinding.m_d3dStructuredBufferStride = U16(bindDesc.NumSamples);
+			refl.m_descriptor.m_bindings[bindDesc.Space][refl.m_descriptor.m_bindingCounts[bindDesc.Space]] = akBinding;
+			++refl.m_descriptor.m_bindingCounts[bindDesc.Space];
 		}
-		else
-		{
-			errorStr.sprintf("Unrecognized type for binding: %s", bindDesc.Name);
-			return Error::kUserData;
-		}
-
-		refl.m_descriptor.m_bindings[bindDesc.Space][refl.m_descriptor.m_bindingCounts[bindDesc.Space]] = akBinding;
-		++refl.m_descriptor.m_bindingCounts[bindDesc.Space];
 	}
 
 	for(U32 i = 0; i < kMaxDescriptorSets; ++i)

+ 1 - 1
AnKi/Shaders/GBufferGeneric.ankiprog

@@ -714,7 +714,7 @@ FragOut main(
 #if ANKI_ANY_HIT_SHADER
 
 #	if REALLY_ALPHA_TEST
-[[vk::shader_record_ext]] ConstantBuffer<GpuSceneRenderableInstance> g_gpuSceneRenderable;
+[[vk::shader_record_ext]] ConstantBuffer<GpuSceneRenderableInstance> g_gpuSceneRenderable : register(b0); // TODO that won't work on D3D
 #	endif
 
 [shader("anyhit")] void main(inout RayPayload payload, in Barycentrics barycentrics)

+ 6 - 113
Tests/Gr/Gr.cpp

@@ -20,101 +20,6 @@
 
 using namespace anki;
 
-const U kWidth = 1024;
-const U kHeight = 768;
-
-static void commonInit()
-{
-	DefaultMemoryPool::allocateSingleton(allocAligned, nullptr);
-	ShaderCompilerMemoryPool::allocateSingleton(allocAligned, nullptr);
-	g_windowWidthCVar.set(kWidth);
-	g_windowHeightCVar.set(kHeight);
-	g_validationCVar.set(true);
-	g_vsyncCVar.set(false);
-	g_debugMarkersCVar.set(true);
-	initWindow();
-	ANKI_TEST_EXPECT_NO_ERR(Input::allocateSingleton().init());
-	initGrManager();
-}
-
-static void commonDestroy()
-{
-	GrManager::freeSingleton();
-	Input::freeSingleton();
-	NativeWindow::freeSingleton();
-	ShaderCompilerMemoryPool::freeSingleton();
-	DefaultMemoryPool::freeSingleton();
-}
-
-template<typename T>
-static BufferPtr createBuffer(const BufferInitInfo& buffInit_, T initialValue, CString name = {})
-{
-	BufferInitInfo buffInit = buffInit_;
-	if(!name.isEmpty())
-	{
-		buffInit.setName(name);
-	}
-
-	BufferPtr buff = GrManager::getSingleton().newBuffer(buffInit);
-
-	T* inData = static_cast<T*>(buff->map(0, kMaxPtrSize, BufferMapAccessBit::kWrite));
-	const T* endData = inData + (buffInit.m_size / sizeof(T));
-	for(; inData < endData; ++inData)
-	{
-		*inData = initialValue;
-	}
-	buff->unmap();
-
-	return buff;
-};
-
-template<typename T>
-TexturePtr createTexture2d(const TextureInitInfo& texInit, T initialValue)
-{
-	BufferInitInfo buffInit;
-	buffInit.m_mapAccess = BufferMapAccessBit::kWrite;
-	buffInit.m_size = texInit.m_height * texInit.m_width * getFormatInfo(texInit.m_format).m_texelSize;
-	buffInit.m_usage = BufferUsageBit::kTransferSource;
-
-	BufferPtr staging = GrManager::getSingleton().newBuffer(buffInit);
-
-	T* inData = static_cast<T*>(staging->map(0, kMaxPtrSize, BufferMapAccessBit::kWrite));
-	const T* endData = inData + (buffInit.m_size / sizeof(T));
-	for(; inData < endData; ++inData)
-	{
-		*inData = initialValue;
-	}
-	staging->unmap();
-
-	TexturePtr tex = GrManager::getSingleton().newTexture(texInit);
-
-	CommandBufferInitInfo cmdbInit;
-	cmdbInit.m_flags |= CommandBufferFlag::kSmallBatch;
-
-	CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(cmdbInit);
-
-	cmdb->copyBufferToTexture(BufferView(staging.get()), TextureView(tex.get(), TextureSubresourceDesc::all()));
-	cmdb->endRecording();
-
-	FencePtr fence;
-	GrManager::getSingleton().submit(cmdb.get(), {}, &fence);
-	fence->clientWait(kMaxSecond);
-
-	return tex;
-};
-
-template<typename T>
-void validateBuffer(BufferPtr buff, T value)
-{
-	const T* inData = static_cast<T*>(buff->map(0, kMaxPtrSize, BufferMapAccessBit::kRead));
-	const T* endData = inData + (buff->getSize() / sizeof(T));
-	for(; inData < endData; ++inData)
-	{
-		ANKI_TEST_EXPECT_EQ(*inData, value);
-	}
-	buff->unmap();
-}
-
 ANKI_TEST(Gr, GrManager)
 {
 	g_validationCVar.set(true);
@@ -344,10 +249,6 @@ void main()
 		texInit.m_width = texInit.m_height = 1;
 		texInit.m_format = Format::kR32G32B32A32_Sfloat;
 
-		BufferInitInfo buffInit;
-		buffInit.m_mapAccess = BufferMapAccessBit::kReadWrite;
-		buffInit.m_size = sizeof(Vec4);
-
 		ShaderPtr compShader = createShader(kSrc, ShaderType::kCompute);
 
 		ShaderProgramInitInfo progInit("Program");
@@ -357,22 +258,15 @@ void main()
 		const Vec4 kMagicVec(1.0f, 2.0f, 3.0f, 4.0f);
 		const Vec4 kInvalidVec(1.0f, 2.0f, 3.0f, 4.0f);
 
-		buffInit.m_usage = BufferUsageBit::kAllStorage;
-		buffInit.m_size = sizeof(kMagicVec) * 2;
-		BufferPtr structured = createBuffer(buffInit, kMagicVec, "structured");
+		const Array<Vec4, 2> data = {kMagicVec, kMagicVec};
+		BufferPtr structured = createBuffer(BufferUsageBit::kAllStorage, ConstWeakArray<Vec4>(data), "structured");
 
 		texInit.m_usage = TextureUsageBit::kSampledCompute | TextureUsageBit::kTransferDestination;
 		TexturePtr tex = createTexture2d(texInit, kMagicVec * 2.0f);
 
-		buffInit.m_usage = BufferUsageBit::kAllTexel;
-		BufferPtr buff = createBuffer(buffInit, kMagicVec * 2.0f, "buff");
-
-		buffInit.m_usage = BufferUsageBit::kAllStorage;
-		buffInit.m_size = sizeof(kInvalidVec);
-		BufferPtr rwstructured = createBuffer(buffInit, kInvalidVec, "rwstructured");
-
-		buffInit.m_usage = BufferUsageBit::kAllTexel;
-		BufferPtr rwbuff = createBuffer(buffInit, kInvalidVec, "rwbuff");
+		BufferPtr buff = createBuffer(BufferUsageBit::kAllTexel, kMagicVec * 2.0f, "buff");
+		BufferPtr rwstructured = createBuffer(BufferUsageBit::kAllStorage, kInvalidVec, "rwstructured");
+		BufferPtr rwbuff = createBuffer(BufferUsageBit::kAllTexel, kInvalidVec, "rwbuff");
 
 		Array<TexturePtr, 3> rwtex;
 
@@ -381,8 +275,7 @@ void main()
 		rwtex[1] = createTexture2d(texInit, kInvalidVec);
 		rwtex[2] = createTexture2d(texInit, kInvalidVec);
 
-		buffInit.m_usage = BufferUsageBit::kUniformCompute;
-		BufferPtr consts = createBuffer(buffInit, kMagicVec * 3.0f, "consts");
+		BufferPtr consts = createBuffer(BufferUsageBit::kUniformCompute, kMagicVec * 3.0f, "consts");
 
 		SamplerInitInfo samplInit;
 		SamplerPtr sampler = GrManager::getSingleton().newSampler(samplInit);

+ 140 - 5
Tests/Gr/GrCommon.h

@@ -20,9 +20,9 @@ inline ShaderPtr createShader(CString src, ShaderType type)
 	ShaderCompilerString errorLog;
 
 #if ANKI_GR_BACKEND_VULKAN
-	const Error err = compileHlslToSpirv(header, type, false, bin, errorLog);
+	Error err = compileHlslToSpirv(header, type, false, true, bin, errorLog);
 #else
-	const Error err = compileHlslToDxil(header, type, false, bin, errorLog);
+	Error err = compileHlslToDxil(header, type, false, true, bin, errorLog);
 #endif
 	if(err)
 	{
@@ -31,12 +31,16 @@ inline ShaderPtr createShader(CString src, ShaderType type)
 	ANKI_TEST_EXPECT_NO_ERR(err);
 
 	ShaderReflection refl;
-	ShaderCompilerString errorStr;
 #if ANKI_GR_BACKEND_VULKAN
-	ANKI_TEST_EXPECT_NO_ERR(doReflectionSpirv(WeakArray(bin.getBegin(), bin.getSize()), type, refl, errorStr));
+	err = doReflectionSpirv(WeakArray(bin.getBegin(), bin.getSize()), type, refl, errorLog);
 #else
-	ANKI_TEST_EXPECT_NO_ERR(doReflectionDxil(bin, type, refl, errorStr));
+	err = doReflectionDxil(bin, type, refl, errorLog);
 #endif
+	if(err)
+	{
+		ANKI_TEST_LOGE("Reflection error:\n%s", errorLog.cstr());
+	}
+	ANKI_TEST_EXPECT_NO_ERR(err);
 
 	ShaderInitInfo initInf(type, bin);
 	initInf.m_reflection = refl;
@@ -58,4 +62,135 @@ inline ShaderProgramPtr createVertFragProg(CString vert, CString frag)
 	return prog;
 }
 
+const U kWidth = 1024;
+const U kHeight = 768;
+
+inline void commonInit()
+{
+	DefaultMemoryPool::allocateSingleton(allocAligned, nullptr);
+	ShaderCompilerMemoryPool::allocateSingleton(allocAligned, nullptr);
+	g_windowWidthCVar.set(kWidth);
+	g_windowHeightCVar.set(kHeight);
+	g_validationCVar.set(true);
+	g_vsyncCVar.set(false);
+	g_debugMarkersCVar.set(true);
+
+	initWindow();
+	ANKI_TEST_EXPECT_NO_ERR(Input::allocateSingleton().init());
+	initGrManager();
+}
+
+inline void commonDestroy()
+{
+	GrManager::freeSingleton();
+	Input::freeSingleton();
+	NativeWindow::freeSingleton();
+	ShaderCompilerMemoryPool::freeSingleton();
+	DefaultMemoryPool::freeSingleton();
+}
+
+template<typename T>
+inline BufferPtr createBuffer(BufferUsageBit usage, ConstWeakArray<T> data, CString name = {})
+{
+	BufferInitInfo buffInit;
+	if(!name.isEmpty())
+	{
+		buffInit.setName(name);
+	}
+
+	buffInit.m_mapAccess = BufferMapAccessBit::kWrite;
+	buffInit.m_usage = usage | BufferUsageBit::kTransferSource;
+	buffInit.m_size = data.getSizeInBytes();
+
+	BufferPtr buff = GrManager::getSingleton().newBuffer(buffInit);
+
+	T* inData = static_cast<T*>(buff->map(0, kMaxPtrSize, BufferMapAccessBit::kWrite));
+
+	for(U32 i = 0; i < data.getSize(); ++i)
+	{
+		inData[i] = data[i];
+	}
+
+	buff->unmap();
+
+	return buff;
+};
+
+template<typename T>
+inline BufferPtr createBuffer(BufferUsageBit usage, T data, CString name = {})
+{
+	ConstWeakArray<T> arr(&data, 1);
+	return createBuffer(usage, arr, name);
+}
+
+template<typename T>
+inline TexturePtr createTexture2d(const TextureInitInfo& texInit, T initialValue)
+{
+	BufferInitInfo buffInit;
+	buffInit.m_mapAccess = BufferMapAccessBit::kWrite;
+	buffInit.m_size = texInit.m_height * texInit.m_width * getFormatInfo(texInit.m_format).m_texelSize;
+	buffInit.m_usage = BufferUsageBit::kTransferSource;
+
+	BufferPtr staging = GrManager::getSingleton().newBuffer(buffInit);
+
+	T* inData = static_cast<T*>(staging->map(0, kMaxPtrSize, BufferMapAccessBit::kWrite));
+	const T* endData = inData + (buffInit.m_size / sizeof(T));
+	for(; inData < endData; ++inData)
+	{
+		*inData = initialValue;
+	}
+	staging->unmap();
+
+	TexturePtr tex = GrManager::getSingleton().newTexture(texInit);
+
+	CommandBufferInitInfo cmdbInit;
+	cmdbInit.m_flags |= CommandBufferFlag::kSmallBatch;
+
+	CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(cmdbInit);
+
+	cmdb->copyBufferToTexture(BufferView(staging.get()), TextureView(tex.get(), TextureSubresourceDesc::all()));
+	cmdb->endRecording();
+
+	FencePtr fence;
+	GrManager::getSingleton().submit(cmdb.get(), {}, &fence);
+	fence->clientWait(kMaxSecond);
+
+	return tex;
+};
+
+template<typename T>
+inline void validateBuffer(BufferPtr buff, T value)
+{
+	BufferPtr tmpBuff;
+
+	if(!!(buff->getMapAccess() & BufferMapAccessBit::kRead))
+	{
+		tmpBuff = buff;
+	}
+	else
+	{
+		BufferInitInfo buffInit;
+		buffInit.m_mapAccess = BufferMapAccessBit::kRead;
+		buffInit.m_size = buff->getSize();
+		buffInit.m_usage = BufferUsageBit::kTransferDestination;
+		tmpBuff = GrManager::getSingleton().newBuffer(buffInit);
+
+		CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(CommandBufferInitInfo(CommandBufferFlag::kSmallBatch));
+		cmdb->copyBufferToBuffer(BufferView(buff.get()), BufferView(tmpBuff.get()));
+		cmdb->endRecording();
+
+		FencePtr fence;
+		GrManager::getSingleton().submit(cmdb.get(), {}, &fence);
+		fence->clientWait(kMaxSecond);
+	}
+
+	const T* inData = static_cast<T*>(tmpBuff->map(0, kMaxPtrSize, BufferMapAccessBit::kRead));
+	const T* endData = inData + (tmpBuff->getSize() / sizeof(T));
+	for(; inData < endData; ++inData)
+	{
+		ANKI_TEST_EXPECT_EQ(*inData, value);
+	}
+	tmpBuff->unmap();
+}
+
 } // end namespace anki

+ 102 - 1
Tests/Gr/GrWorkGraphs.cpp

@@ -7,6 +7,107 @@
 #include <Tests/Gr/GrCommon.h>
 #include <AnKi/Gr.h>
 
-ANKI_TEST(Gr, WorkGraphsHelloWorld)
+ANKI_TEST(Gr, WorkGraphHelloWorld)
 {
+	// CVarSet::getSingleton().setMultiple(Array<const Char*, 2>{"Device", "2"});
+	commonInit();
+
+	{
+		const Char* kSrc = R"(
+struct FirstNodeRecord
+{
+	uint3 m_gridSize : SV_DispatchGrid;
+	uint m_value;
+};
+
+struct SecondNodeRecord
+{
+	uint3 m_gridSize : SV_DispatchGrid;
+	uint m_value;
+};
+
+struct ThirdNodeRecord
+{
+	uint m_value;
+};
+
+RWStructuredBuffer<uint> g_buff : register(u0);
+
+[Shader("node")] [NodeLaunch("broadcasting")] [NodeMaxDispatchGrid(16, 1, 1)] [NumThreads(16, 1, 1)]
+void main(DispatchNodeInputRecord<FirstNodeRecord> inp, [MaxRecords(2)] NodeOutput<SecondNodeRecord> secondNode, uint svGroupIndex : SV_GroupIndex)
+{
+	GroupNodeOutputRecords<SecondNodeRecord> rec = secondNode.GetGroupNodeOutputRecords(2);
+
+	if(svGroupIndex < 2)
+	{
+		rec[svGroupIndex].m_gridSize = uint3(16, 1, 1);
+		rec[svGroupIndex].m_value = inp.Get().m_value;
+	}
+
+	rec.OutputComplete();
+}
+
+[Shader("node")] [NodeLaunch("broadcasting")] [NumThreads(16, 1, 1)] [NodeMaxDispatchGrid(16, 1, 1)]
+void secondNode(DispatchNodeInputRecord<SecondNodeRecord> inp, [MaxRecords(32)] NodeOutput<ThirdNodeRecord> thirdNode,
+				uint svGroupIndex : SV_GROUPINDEX)
+{
+	GroupNodeOutputRecords<ThirdNodeRecord> recs = thirdNode.GetGroupNodeOutputRecords(32);
+
+	recs[svGroupIndex * 2 + 0].m_value = inp.Get().m_value;
+	recs[svGroupIndex * 2 + 1].m_value = inp.Get().m_value;
+
+	recs.OutputComplete();
+}
+
+[Shader("node")] [NodeLaunch("coalescing")] [NumThreads(16, 1, 1)]
+void thirdNode([MaxRecords(32)] GroupNodeInputRecords<ThirdNodeRecord> inp, uint svGroupIndex : SV_GroupIndex)
+{
+	if (svGroupIndex * 2 < inp.Count())
+		InterlockedAdd(g_buff[0], inp[svGroupIndex * 2].m_value);
+
+	if (svGroupIndex * 2 + 1 < inp.Count())
+		InterlockedAdd(g_buff[0], inp[svGroupIndex * 2 + 1].m_value);
+}
+)";
+
+		ShaderPtr shader = createShader(kSrc, ShaderType::kWorkGraph);
+
+		ShaderProgramInitInfo progInit;
+		progInit.m_workGraphShader = shader.get();
+		ShaderProgramPtr prog = GrManager::getSingleton().newShaderProgram(progInit);
+
+		BufferPtr counterBuff = createBuffer(BufferUsageBit::kAllStorage | BufferUsageBit::kTransferSource, 0u, "CounterBuffer");
+
+		BufferInitInfo scratchInit("scratch");
+		scratchInit.m_size = prog->getWorkGraphMemoryRequirements();
+		scratchInit.m_usage = BufferUsageBit::kAllStorage;
+		BufferPtr scratchBuff = GrManager::getSingleton().newBuffer(scratchInit);
+
+		struct FirstNodeRecord
+		{
+			UVec3 m_gridSize;
+			U32 m_value;
+		};
+
+		Array<FirstNodeRecord, 2> records;
+		for(U32 i = 0; i < records.getSize(); ++i)
+		{
+			records[i].m_gridSize = UVec3(4, 1, 1);
+			records[i].m_value = (i + 1) * 10;
+		}
+
+		CommandBufferPtr cmdb = GrManager::getSingleton().newCommandBuffer(CommandBufferInitInfo(CommandBufferFlag::kSmallBatch));
+		cmdb->bindShaderProgram(prog.get());
+		cmdb->bindStorageBuffer(ANKI_REG(u0), BufferView(counterBuff.get()));
+		cmdb->dispatchGraph(BufferView(scratchBuff.get()), records.getBegin(), records.getSize(), sizeof(records[0]));
+		cmdb->endRecording();
+
+		FencePtr fence;
+		GrManager::getSingleton().submit(cmdb.get(), {}, &fence);
+		fence->clientWait(kMaxSecond);
+
+		validateBuffer(counterBuff, 122880);
+	}
+
+	commonDestroy();
 }

+ 2 - 2
Tests/ShaderCompiler/ShaderProgramCompiler.cpp

@@ -138,7 +138,7 @@ void main()
 	taskManager.m_pool = &pool;
 
 	ShaderBinary* binary;
-	ANKI_TEST_EXPECT_NO_ERR(compileShaderProgram("test.glslp", true, fsystem, nullptr, &taskManager, {}, binary));
+	ANKI_TEST_EXPECT_NO_ERR(compileShaderProgram("test.glslp", true, true, fsystem, nullptr, &taskManager, {}, binary));
 
 #if 1
 	ShaderCompilerString dis;
@@ -333,7 +333,7 @@ void main()
 	taskManager.m_pool = &pool;
 
 	ShaderBinary* binary;
-	ANKI_TEST_EXPECT_NO_ERR(compileShaderProgram("test.glslp", true, fsystem, nullptr, &taskManager, {}, binary));
+	ANKI_TEST_EXPECT_NO_ERR(compileShaderProgram("test.glslp", true, true, fsystem, nullptr, &taskManager, {}, binary));
 
 #if 1
 	ShaderCompilerString dis;

+ 180 - 0
ThirdParty/Warp/LICENSE.TXT

@@ -0,0 +1,180 @@
+MICROSOFT SOFTWARE LICENSE TERMS
+
+MICROSOFT DIRECT3D WARP
+
+Purpose – for testing purposes only.
+ 
+IF YOU LIVE IN (OR ARE A BUSINESS WITH A PRINCIPAL PLACE OF BUSINESS IN) THE UNITED 
+STATES, PLEASE READ THE “BINDING ARBITRATION AND CLASS ACTION WAIVER” SECTION 
+BELOW. IT AFFECTS HOW DISPUTES ARE RESOLVED.
+ 
+These license terms are an agreement between you and Microsoft Corporation (or one of its affiliates). They 
+apply to the software named above and any Microsoft services or software updates (except to the extent such 
+services or updates are accompanied by new or additional terms, in which case those different terms apply 
+prospectively and do not alter your or Microsoft’s rights relating to pre-updated software or services). IF YOU 
+COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU 
+ACCEPT THESE TERMS.
+
+1.	INSTALLATION AND USE RIGHTS.
+
+a)	General. You may install and use any number of copies of the software to develop and test your 
+applications, solely for the purpose stated above and otherwise for your internal business purposes, 
+and solely for use on Windows.
+
+2.	DATA. 
+
+a)	Data Collection. The software may collect information about you and your use of the software, and 
+send that to Microsoft. Microsoft may use this information to provide services and improve our 
+products and services. You may opt-out of many of these scenarios, but not all, as described in the 
+product documentation.  There are also some features in the software that may enable you to collect 
+data from users of your applications. If you use these features to enable data collection in your 
+applications, you must comply with applicable law, including providing appropriate notices to users of 
+your applications. You can learn more about data collection and use in the help documentation and 
+the privacy statement at https://aka.ms/privacy. Your use of the software operates as your consent to 
+these practices.
+
+b)	Processing of Personal Data. To the extent Microsoft is a processor or subprocessor of personal 
+data in connection with the software, Microsoft makes the commitments in the European Union 
+General Data Protection Regulation Terms of the Online Services Terms to all customers effective May 
+25, 2018, at https://docs.microsoft.com/en-us/legal/gdpr.
+
+3.	SCOPE OF LICENSE. The software is licensed, not sold. Microsoft reserves all other rights. Unless 
+applicable law gives you more rights despite this limitation, you will not (and have no right to):
+
+a)	work around any technical limitations in the software that only allow you to use it in certain ways;
+
+b)	reverse engineer, decompile or disassemble the software, or otherwise attempt to derive the source 
+code for the software, except and to the extent required by third party licensing terms governing use 
+of certain open source components that may be included in the software;
+
+c)	remove, minimize, block, or modify any notices of Microsoft or its suppliers in the software;
+
+d)	use the software in any way that is against the law or to create or propagate malware; or
+
+e)	share, publish, distribute, or lease the software, provide the software as a stand-alone offering for 
+others to use, or transfer the software or this agreement to any third party.
+
+4.	EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and 
+regulations that apply to the software, which include restrictions on destinations, end users, and end use. 
+For further information on export restrictions, visit https://aka.ms/exporting.
+
+5.	SUPPORT SERVICES. Microsoft is not obligated under this agreement to provide any support services for 
+the software. Any support provided is “as is”, “with all faults”, and without warranty of any kind. 
+
+6.	UPDATES. The software may periodically check for updates, and download and install them for you. You 
+may obtain updates only from Microsoft or authorized sources. Microsoft may need to update your system 
+to provide you with updates. You agree to receive these automatic updates without any additional notice. 
+Updates may not include or support all existing software features, services, or peripheral devices.
+
+7.	BINDING ARBITRATION AND CLASS ACTION WAIVER. This Section applies if you live in (or, if 
+a business, your principal place of business is in) the United States.  If you and Microsoft have a 
+dispute, you and Microsoft agree to try for 60 days to resolve it informally. If you and Microsoft can’t, you 
+and Microsoft agree to binding individual arbitration before the American Arbitration 
+Association under the Federal Arbitration Act (“FAA”), and not to sue in court in front of a judge or 
+jury. Instead, a neutral arbitrator will decide. Class action lawsuits, class-wide arbitrations, private 
+attorney-general actions, and any other proceeding where someone acts in a representative 
+capacity are not allowed; nor is combining individual proceedings without the consent of all 
+parties. The complete Arbitration Agreement contains more terms and is at https://aka.ms/arb-
+agreement-4. You and Microsoft agree to these terms.
+
+8.	ENTIRE AGREEMENT. This agreement, and any other terms Microsoft may provide for supplements, 
+updates, or third-party applications, is the entire agreement for the software.
+
+9.	APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES. If you acquired the software in the United 
+States or Canada, the laws of the state or province where you live (or, if a business, where your principal 
+place of business is located) govern the interpretation of this agreement, claims for its breach, and all 
+other claims (including consumer protection, unfair competition, and tort claims), regardless of conflict of 
+laws principles, except that the FAA governs everything related to arbitration. If you acquired the software 
+in any other country, its laws apply, except that the FAA governs everything related to arbitration. If U.S. 
+federal jurisdiction exists, you and Microsoft consent to exclusive jurisdiction and venue in the federal court 
+in King County, Washington for all disputes heard in court (excluding arbitration). If not, you and Microsoft 
+consent to exclusive jurisdiction and venue in the Superior Court of King County, Washington for all 
+disputes heard in court (excluding arbitration).
+
+10.	CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You 
+may have other rights, including consumer rights, under the laws of your state, province, or country. 
+Separate and apart from your relationship with Microsoft, you may also have rights with respect to the 
+party from which you acquired the software. This agreement does not change those other rights if the 
+laws of your state, province, or country do not permit it to do so. For example, if you acquired the 
+software in one of the below regions, or mandatory country law applies, then the following provisions 
+apply to you:
+
+a)	Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this 
+agreement is intended to affect those rights.
+
+b)	Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the 
+automatic update feature, disconnecting your device from the Internet (if and when you re-connect to 
+the Internet, however, the software will resume checking for and installing updates), or uninstalling 
+the software. The product documentation, if any, may also specify how to turn off updates for your 
+specific device or software.
+
+c)	Germany and Austria.
+
+i.	Warranty. The properly licensed software will perform substantially as described in any Microsoft 
+materials that accompany the software. However, Microsoft gives no contractual guarantee in 
+relation to the licensed software.
+
+ii.	Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the 
+Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable 
+according to the statutory law.
+
+Subject to the foregoing clause ii., Microsoft will only be liable for slight negligence if Microsoft is in 
+breach of such material contractual obligations, the fulfillment of which facilitate the due performance 
+of this agreement, the breach of which would endanger the purpose of this agreement and the 
+compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases 
+of slight negligence, Microsoft will not be liable for slight negligence.
+
+11.	DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS IS.” YOU BEAR THE RISK OF 
+USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. 
+TO THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED 
+WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND 
+NON-INFRINGEMENT.
+
+12.	LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING 
+DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM 
+MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT 
+RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, 
+INDIRECT OR INCIDENTAL DAMAGES.
+
+This limitation applies to (a) anything related to the software, services, content (including 
+code) on third party Internet sites, or third party applications; and (b) claims for breach of 
+contract, warranty, guarantee, or condition; strict liability, negligence, or other tort; or any 
+other claim; in each case to the extent permitted by applicable law.
+It also applies even if Microsoft knew or should have known about the possibility of the 
+damages. The above limitation or exclusion may not apply to you because your state, 
+province, or country may not allow the exclusion or limitation of incidental, consequential, or 
+other damages.
+
+Please note: As this software is distributed in Canada, some of the clauses in this agreement are 
+provided below in French.
+
+Remarque: Ce logiciel étant distribué au Canada, certaines des clauses dans ce contrat sont 
+fournies ci-dessous en français.
+
+EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute 
+utilisation de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre 
+garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la 
+protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le 
+droit locale, les garanties implicites de qualité marchande, d’adéquation à un usage particulier et 
+d’absence de contrefaçon sont exclues.
+
+LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES 
+DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de 
+dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune 
+indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou 
+accessoires et pertes de bénéfices.
+
+Cette limitation concerne:
+•	tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur 
+des sites Internet tiers ou dans des programmes tiers; et
+•	les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité 
+stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur.
+Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un 
+tel dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les 
+dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou 
+l’exclusion ci-dessus ne s’appliquera pas à votre égard.
+
+EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir 
+d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que 
+vous confèrent les lois de votre pays si celles-ci ne le permettent pas.
+

+ 118 - 0
ThirdParty/Warp/README.md

@@ -0,0 +1,118 @@
+# Direct3D WARP NuGet Package
+
+This package contains a copy of D3D10Warp.dll with the latest features and fixes for testing and development purposes. 
+
+The included **LICENSE.txt** applies to all files under `build/native/bin/`.
+
+## Changelog
+
+### Version 1.0.12
+
+New features:
+
+- Now including arm64 binaries
+
+Bug fixes:
+
+- Fix mesh shaders on Win11 retail D3D12
+- Fix accessing static const arrays in generic programs
+- Fix arm64 instruction encoding which could cause random crashes in shader execution
+- Fix state objects exporting the same non-library shader with multiple names/aliases
+- Fix handling of zeroing used by lifetime markers on older shader models
+- Fix handling of lifetime markers in node shaders
+- Handle a null global root signature in a work graph
+- Fix mesh -> PS linkage for primitive outputs that were not marked nointerpolate in the PS input
+- Fix back-to-back DispatchGraph without a barrier to correctly share the backing memory
+- Fix a race condition in BVH builds which could cause corrupted acceleration structures
+
+### Version 1.0.11
+
+New features:
+
+- Shader model 6.8 support
+- Work graphs support
+- ExecuteIndirect tier 1.1 support
+
+### Version 1.0.10.1
+
+- Fix to support empty ray tracing acceleration structures.
+- Miscellaneous bug fixes.
+
+### Version 1.0.9
+
+New features: 
+
+- Sampler feedback support
+
+Bug fixes:
+- Improve handling of GetDimensions shader instructions in control flow or with dynamic resource handles
+- Fix JIT optimizations that can produce wrong results
+- Fix assignment of variable inside of a loop which was uninitialized before entering
+- Fix ExecuteIndirect to not modify the app-provided arg buffer
+- Fix root signature visibility for amplification and mesh shaders
+- Handle DXIL initializers for non-constant global variables
+- Fix uninitialized memory causing potential issues with amplification shaders
+- Fix deadlock from using the same shader across multiple queues with out-of-order waits
+- Some fixes for handling of tiled resources
+
+### Version 1.0.8
+
+- DirectX Raytracing (DXR) 1.1 support.
+
+### Version 1.0.7
+
+- Miscellaneous fixes for 16- and 64-bit shader operations.
+
+### Version 1.0.6
+
+- Miscellaneous fixes and improvements to non-uniform control flow in DXIL shader compilation.
+- Fixed casting between block-compressed and uncompressed texture formats.
+- Improved conformance of exp2 implementation. 
+
+### Version 1.0.5
+
+- Improved support for non-uniform constant buffer reads. 
+- Miscellaneous fp16 conformance fixes. 
+- Inverted height support.
+
+### Version 1.0.4
+
+- Miscellaneous sampling conformance fixes. 
+
+### Version 1.0.3
+
+New features: 
+
+- Mesh shader support. 
+
+Bug fixes: 
+
+- Fix a synchronization issue related to null/null aliasing barrier sequences.
+- Fix a compatibility issue with older D3D12 Agility SDK versions lacking enhanced barrier support.
+
+### Version 1.0.2
+
+- Expanded support for 64-bit conversion operations. 
+- Fixes issue where static samplers could incorrectly use non-normalized coordinates.
+
+### Version 1.0.1
+
+New features:
+
+- Support for Shader Model 6.3-6.7 required features, including:
+  - 64-bit loads, stores, and atomics on all available types of resources.
+  - Descriptor heap indexing, aka Shader Model 6.6 dynamic resources.
+  - Quad ops and sampling derivatives in compute shaders.
+  - Advanced texture ops, featuring integer texture point sampling, raw gather, dynamic texel offsets, and MSAA UAVs.
+
+- Support for many new Direct3D 12 Agility SDK features, including:
+  - Enhanced barriers. 
+  - Relaxed resource format casting. 
+  - Independent front/back stencil refs and masks.
+  - Triangle fans.
+  - Relaxed buffer/texture copy pitches.
+  - Independent D3D12 devices via ID3D12DeviceFactory::CreateDevice
+
+Bug fixes:
+
+- Various conformance and stability fixes.

BIN
ThirdParty/Warp/bin/x64/d3d10warp.dll


BIN
ThirdParty/Warp/bin/x64/d3d10warp.pdb