Browse Source

Clear screen test works on D3D12

Panagiotis Christopoulos Charitos 1 year ago
parent
commit
01fcf7039f

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

@@ -188,9 +188,8 @@ void CommandBuffer::beginRenderPass(ConstWeakArray<RenderTarget> colorRts, Rende
 		D3D12_RENDER_PASS_RENDER_TARGET_DESC& desc = colorRtDescs[i];
 		D3D12_RENDER_PASS_RENDER_TARGET_DESC& desc = colorRtDescs[i];
 
 
 		desc = {};
 		desc = {};
-		desc.cpuDescriptor = static_cast<const TextureImpl&>(rt.m_textureView.getTexture())
-								 .getView(rt.m_textureView.getSubresource(), D3DTextureViewType::kRtv)
-								 .m_cpuHandle;
+		desc.cpuDescriptor =
+			static_cast<const TextureImpl&>(rt.m_textureView.getTexture()).getOrCreateRtv(rt.m_textureView.getSubresource()).m_cpuHandle;
 		desc.BeginningAccess.Type = convertLoadOp(rt.m_loadOperation);
 		desc.BeginningAccess.Type = convertLoadOp(rt.m_loadOperation);
 		memcpy(&desc.BeginningAccess.Clear.ClearValue.Color, &rt.m_clearValue.m_colorf[0], sizeof(F32) * 4);
 		memcpy(&desc.BeginningAccess.Clear.ClearValue.Color, &rt.m_clearValue.m_colorf[0], sizeof(F32) * 4);
 		desc.EndingAccess.Type = convertStoreOp(rt.m_storeOperation);
 		desc.EndingAccess.Type = convertStoreOp(rt.m_storeOperation);
@@ -201,7 +200,7 @@ void CommandBuffer::beginRenderPass(ConstWeakArray<RenderTarget> colorRts, Rende
 	{
 	{
 		dsDesc = {};
 		dsDesc = {};
 		dsDesc.cpuDescriptor = static_cast<const TextureImpl&>(depthStencilRt->m_textureView.getTexture())
 		dsDesc.cpuDescriptor = static_cast<const TextureImpl&>(depthStencilRt->m_textureView.getTexture())
-								   .getView(depthStencilRt->m_textureView.getSubresource(), D3DTextureViewType::kDsv)
+								   .getOrCreateDsv(depthStencilRt->m_textureView.getSubresource(), depthStencilRt->m_usage)
 								   .m_cpuHandle;
 								   .m_cpuHandle;
 
 
 		dsDesc.DepthBeginningAccess.Type = convertLoadOp(depthStencilRt->m_loadOperation);
 		dsDesc.DepthBeginningAccess.Type = convertLoadOp(depthStencilRt->m_loadOperation);

+ 1 - 1
AnKi/Gr/D3D/D3DCommandBuffer.h

@@ -34,7 +34,7 @@ public:
 	}
 	}
 
 
 private:
 private:
-	ID3D12GraphicsCommandList7* m_cmdList = nullptr; // Cache it.
+	ID3D12GraphicsCommandList6* m_cmdList = nullptr; // Cache it.
 	U32 m_commandCount = 0;
 	U32 m_commandCount = 0;
 
 
 	StackMemoryPool* m_fastPool = nullptr; // Cache it.
 	StackMemoryPool* m_fastPool = nullptr; // Cache it.

+ 1 - 0
AnKi/Gr/D3D/D3DCommandBufferFactory.cpp

@@ -47,6 +47,7 @@ void MicroCommandBuffer::reset()
 	// Command list should already be reset on submit
 	// Command list should already be reset on submit
 
 
 	m_cmdAllocator->Reset();
 	m_cmdAllocator->Reset();
+	m_cmdList->Reset(m_cmdAllocator, nullptr);
 
 
 	m_isSmallBatch = true;
 	m_isSmallBatch = true;
 }
 }

+ 2 - 7
AnKi/Gr/D3D/D3DCommandBufferFactory.h

@@ -81,7 +81,7 @@ public:
 		m_isSmallBatch = false;
 		m_isSmallBatch = false;
 	}
 	}
 
 
-	ID3D12GraphicsCommandList7& getCmdList() const
+	ID3D12GraphicsCommandList6& getCmdList() const
 	{
 	{
 		ANKI_ASSERT(m_cmdList);
 		ANKI_ASSERT(m_cmdList);
 		return *m_cmdList;
 		return *m_cmdList;
@@ -97,11 +97,6 @@ public:
 		return (m_cmdList->GetType() == D3D12_COMMAND_LIST_TYPE_COMPUTE) ? GpuQueueType::kCompute : GpuQueueType::kGeneral;
 		return (m_cmdList->GetType() == D3D12_COMMAND_LIST_TYPE_COMPUTE) ? GpuQueueType::kCompute : GpuQueueType::kGeneral;
 	}
 	}
 
 
-	void resetCmdList()
-	{
-		m_cmdList->Reset(m_cmdAllocator, nullptr);
-	}
-
 private:
 private:
 	static constexpr U32 kMaxRefObjectSearch = 16;
 	static constexpr U32 kMaxRefObjectSearch = 16;
 
 
@@ -114,7 +109,7 @@ private:
 	StackMemoryPool m_fastPool;
 	StackMemoryPool m_fastPool;
 
 
 	ID3D12CommandAllocator* m_cmdAllocator = nullptr;
 	ID3D12CommandAllocator* m_cmdAllocator = nullptr;
-	ID3D12GraphicsCommandList7* m_cmdList = nullptr;
+	ID3D12GraphicsCommandList6* m_cmdList = nullptr;
 
 
 	void reset();
 	void reset();
 
 

+ 6 - 5
AnKi/Gr/D3D/D3DCommon.h

@@ -8,6 +8,7 @@
 #include <AnKi/Gr/Common.h>
 #include <AnKi/Gr/Common.h>
 #include <AnKi/Util/Logger.h>
 #include <AnKi/Util/Logger.h>
 #include <AnKi/Gr/BackendCommon/Common.h>
 #include <AnKi/Gr/BackendCommon/Common.h>
+#include <AnKi/Util/System.h>
 #include <string>
 #include <string>
 #include <locale>
 #include <locale>
 #include <codecvt>
 #include <codecvt>
@@ -47,7 +48,7 @@ namespace anki {
 		HRESULT rez; \
 		HRESULT rez; \
 		if((rez = (x)) < 0) [[unlikely]] \
 		if((rez = (x)) < 0) [[unlikely]] \
 		{ \
 		{ \
-			ANKI_D3D_LOGF("D3D function failed (HRESULT: %d): %s", rez, #x); \
+			ANKI_D3D_LOGF("D3D function failed (HRESULT: %d message: %s): %s", rez, errorMessageToString(GetLastError()).cstr(), #x); \
 		} \
 		} \
 	} while(0)
 	} while(0)
 
 
@@ -57,12 +58,12 @@ namespace anki {
 		HRESULT rez; \
 		HRESULT rez; \
 		if((rez = (x)) < 0) [[unlikely]] \
 		if((rez = (x)) < 0) [[unlikely]] \
 		{ \
 		{ \
-			ANKI_D3D_LOGE("D3D function failed (HRESULT: %d): %s", rez, #x); \
+			ANKI_D3D_LOGE("D3D function failed (HRESULT: %d message: %s): %s", rez, errorMessageToString(GetLastError()).cstr(), #x); \
 			return Error::kFunctionFailed; \
 			return Error::kFunctionFailed; \
 		} \
 		} \
 	} while(0)
 	} while(0)
 
 
-enum class D3DTextureViewType
+enum class D3DTextureViewType : U8
 {
 {
 	kSrv,
 	kSrv,
 	kRtv,
 	kRtv,
@@ -74,7 +75,7 @@ enum class D3DTextureViewType
 };
 };
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(D3DTextureViewType)
 ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(D3DTextureViewType)
 
 
-enum class D3DTextureBufferType
+enum class D3DBufferViewType : U8
 {
 {
 	kCbv,
 	kCbv,
 	kSrv,
 	kSrv,
@@ -83,7 +84,7 @@ enum class D3DTextureBufferType
 	kCount,
 	kCount,
 	kFirst = 0
 	kFirst = 0
 };
 };
-ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(D3DTextureBufferType)
+ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(D3DBufferViewType)
 
 
 inline std::string ws2s(const std::wstring& wstr)
 inline std::string ws2s(const std::wstring& wstr)
 {
 {

+ 45 - 13
AnKi/Gr/D3D/D3DDescriptorHeap.h

@@ -20,9 +20,7 @@ class DescriptorHeapHandle
 {
 {
 public:
 public:
 	D3D12_CPU_DESCRIPTOR_HANDLE m_cpuHandle = {};
 	D3D12_CPU_DESCRIPTOR_HANDLE m_cpuHandle = {};
-#if ANKI_ASSERTIONS_ENABLED
-	DescriptorHeap* m_father = nullptr;
-#endif
+	D3D12_DESCRIPTOR_HEAP_TYPE m_heapType = D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES;
 
 
 	[[nodiscard]] Bool isCreated() const
 	[[nodiscard]] Bool isCreated() const
 	{
 	{
@@ -58,6 +56,7 @@ public:
 
 
 		m_freeDescriptorsHead = 0;
 		m_freeDescriptorsHead = 0;
 		m_descriptorSize = descriptorSize;
 		m_descriptorSize = descriptorSize;
+		m_type = type;
 		return Error::kNone;
 		return Error::kNone;
 	}
 	}
 
 
@@ -79,9 +78,8 @@ public:
 		idx = m_freeDescriptors[idx];
 		idx = m_freeDescriptors[idx];
 		DescriptorHeapHandle out;
 		DescriptorHeapHandle out;
 		out.m_cpuHandle.ptr = m_heapStart.ptr + PtrSize(idx) * m_descriptorSize;
 		out.m_cpuHandle.ptr = m_heapStart.ptr + PtrSize(idx) * m_descriptorSize;
-#if ANKI_ASSERTIONS_ENABLED
-		out.m_father = this;
-#endif
+		out.m_heapType = m_type;
+
 		return out;
 		return out;
 	}
 	}
 
 
@@ -89,14 +87,14 @@ public:
 	void free(DescriptorHeapHandle& handle)
 	void free(DescriptorHeapHandle& handle)
 	{
 	{
 		ANKI_ASSERT(m_heap);
 		ANKI_ASSERT(m_heap);
-		if(handle.m_cpuHandle.ptr == 0)
+		if(!handle.isCreated())
 		{
 		{
 			return;
 			return;
 		}
 		}
 
 
-		ANKI_ASSERT(handle.m_father == this);
+		ANKI_ASSERT(handle.m_heapType == m_type);
 		ANKI_ASSERT(handle.m_cpuHandle.ptr != 0 && handle.m_cpuHandle.ptr >= m_heapStart.ptr
 		ANKI_ASSERT(handle.m_cpuHandle.ptr != 0 && handle.m_cpuHandle.ptr >= m_heapStart.ptr
-					&& handle.m_cpuHandle.ptr < m_heapStart.ptr + PtrSize(m_descriptorSize) + m_freeDescriptors.getSize());
+					&& handle.m_cpuHandle.ptr < m_heapStart.ptr + m_descriptorSize * m_freeDescriptors.getSize());
 
 
 		const U16 idx = U16((handle.m_cpuHandle.ptr - m_heapStart.ptr) / m_descriptorSize);
 		const U16 idx = U16((handle.m_cpuHandle.ptr - m_heapStart.ptr) / m_descriptorSize);
 
 
@@ -110,6 +108,7 @@ public:
 private:
 private:
 	ID3D12DescriptorHeap* m_heap = nullptr;
 	ID3D12DescriptorHeap* m_heap = nullptr;
 	D3D12_CPU_DESCRIPTOR_HANDLE m_heapStart = {};
 	D3D12_CPU_DESCRIPTOR_HANDLE m_heapStart = {};
+	D3D12_DESCRIPTOR_HEAP_TYPE m_type = {};
 	U32 m_descriptorSize = 0;
 	U32 m_descriptorSize = 0;
 
 
 	GrDynamicArray<U16> m_freeDescriptors;
 	GrDynamicArray<U16> m_freeDescriptors;
@@ -118,12 +117,45 @@ private:
 	Mutex m_mtx;
 	Mutex m_mtx;
 };
 };
 
 
-class RtvDescriptorHeap : public DescriptorHeap, public MakeSingleton<RtvDescriptorHeap>
+/// An array of all descriptor heaps.
+class DescriptorHeaps : public MakeSingleton<DescriptorHeaps>
 {
 {
-};
+public:
+	Error init(const Array<U16, D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES>& descriptorCounts, ID3D12Device& dev)
+	{
+		const Array<D3D12_DESCRIPTOR_HEAP_FLAGS, D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES> flags = {
+			D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
+			D3D12_DESCRIPTOR_HEAP_FLAG_NONE};
 
 
-class CbvSrvUavDescriptorHeap : public DescriptorHeap, public MakeSingleton<CbvSrvUavDescriptorHeap>
-{
+		for(D3D12_DESCRIPTOR_HEAP_TYPE type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; type < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES;
+			type = D3D12_DESCRIPTOR_HEAP_TYPE(U32(type) + 1))
+		{
+			if(descriptorCounts[type])
+			{
+				ANKI_CHECK(m_heaps[type].init(type, flags[type], descriptorCounts[type], dev.GetDescriptorHandleIncrementSize(type)));
+			}
+		}
+
+		return Error::kNone;
+	}
+
+	/// @note Thread-safe.
+	DescriptorHeapHandle allocate(D3D12_DESCRIPTOR_HEAP_TYPE type)
+	{
+		return m_heaps[type].allocate();
+	}
+
+	/// @note Thread-safe.
+	void free(DescriptorHeapHandle& handle)
+	{
+		if(handle.isCreated())
+		{
+			m_heaps[handle.m_heapType].free(handle);
+		}
+	}
+
+private:
+	Array<DescriptorHeap, D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES> m_heaps;
 };
 };
 /// @}
 /// @}
 
 

+ 1 - 1
AnKi/Gr/D3D/D3DFence.h

@@ -25,7 +25,7 @@ public:
 
 
 	~MicroFence()
 	~MicroFence()
 	{
 	{
-		ANKI_ASSERT(!done());
+		ANKI_ASSERT(done());
 		if(!CloseHandle(m_event))
 		if(!CloseHandle(m_event))
 		{
 		{
 			ANKI_D3D_LOGE("CloseHandle() failed");
 			ANKI_D3D_LOGE("CloseHandle() failed");

+ 93 - 0
AnKi/Gr/D3D/D3DFrameGarbageCollector.cpp

@@ -0,0 +1,93 @@
+// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include <AnKi/Gr/D3D/D3DFrameGarbageCollector.h>
+
+namespace anki {
+
+FrameGarbageCollector::~FrameGarbageCollector()
+{
+	collectGarbage();
+	ANKI_ASSERT(m_frames.isEmpty());
+}
+
+void FrameGarbageCollector::endFrame(MicroFence* frameFence)
+{
+	LockGuard lock(m_mtx);
+
+	if(!m_frames.isEmpty() && !m_frames.getBack().m_fence.isCreated())
+	{
+		// Last frame is without a fence, asign the fence to not have it garbage collected
+		m_frames.getBack().m_fence.reset(frameFence);
+	}
+
+	collectGarbage();
+}
+
+FrameGarbageCollector::FrameGarbage& FrameGarbageCollector::getFrame()
+{
+	if(!m_frames.isEmpty() && !m_frames.getBack().m_fence.isCreated())
+	{
+		// Do nothing
+	}
+	else
+	{
+		FrameGarbage* newGarbage = newInstance<FrameGarbage>(GrMemoryPool::getSingleton());
+		m_frames.pushBack(newGarbage);
+	}
+
+	return m_frames.getBack();
+}
+
+void FrameGarbageCollector::newTextureGarbage(TextureGarbage* textureGarbage)
+{
+	LockGuard<Mutex> lock(m_mtx);
+	FrameGarbage& frame = getFrame();
+	frame.m_textureGarbage.pushBack(textureGarbage);
+}
+
+void FrameGarbageCollector::collectGarbage()
+{
+	if(m_frames.isEmpty()) [[likely]]
+	{
+		return;
+	}
+
+	IntrusiveList<FrameGarbage> newFrames;
+	while(!m_frames.isEmpty())
+	{
+		FrameGarbage& frame = *m_frames.popFront();
+
+		if(frame.m_fence.isCreated() && !frame.m_fence->done())
+		{
+			ANKI_ASSERT(!frame.m_textureGarbage.isEmpty());
+			newFrames.pushBack(&frame);
+			continue;
+		}
+
+		// Frame is done, dispose garbage and destroy it
+
+		// Dispose texture garbage
+		while(!frame.m_textureGarbage.isEmpty())
+		{
+			TextureGarbage* textureGarbage = frame.m_textureGarbage.popBack();
+
+			for(DescriptorHeapHandle& handle : textureGarbage->m_viewHandles)
+			{
+				DescriptorHeaps::getSingleton().free(handle);
+			}
+
+			safeRelease(textureGarbage->m_resource);
+
+			deleteInstance(GrMemoryPool::getSingleton(), textureGarbage);
+		}
+
+		deleteInstance(GrMemoryPool::getSingleton(), &frame);
+	}
+
+	m_frames = std::move(newFrames);
+}
+
+} // end namespace anki

+ 61 - 0
AnKi/Gr/D3D/D3DFrameGarbageCollector.h

@@ -0,0 +1,61 @@
+// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <AnKi/Gr/D3D/D3DDescriptorHeap.h>
+#include <AnKi/Gr/D3D/D3DFence.h>
+#include <AnKi/Util/List.h>
+
+namespace anki {
+
+/// @addtogroup directx
+/// @{
+
+class TextureGarbage : public IntrusiveListEnabled<TextureGarbage>
+{
+public:
+	GrDynamicArray<DescriptorHeapHandle> m_viewHandles;
+	ID3D12Resource* m_resource = nullptr;
+};
+
+/// This class gathers various garbages and disposes them when in some later frame where it is safe to do so. This is used on bindless textures and
+/// buffers where we have to wait until the frame where they were deleted is done.
+class FrameGarbageCollector : public MakeSingleton<FrameGarbageCollector>
+{
+public:
+	FrameGarbageCollector() = default;
+
+	FrameGarbageCollector(const FrameGarbageCollector&) = delete;
+
+	~FrameGarbageCollector();
+
+	FrameGarbageCollector& operator=(const FrameGarbageCollector&) = delete;
+
+	/// @note It's thread-safe.
+	void newTextureGarbage(TextureGarbage* textureGarbage);
+
+	/// Finalizes a frame.
+	/// @note It's thread-safe.
+	void endFrame(MicroFence* frameFence);
+
+private:
+	class FrameGarbage : public IntrusiveListEnabled<FrameGarbage>
+	{
+	public:
+		IntrusiveList<TextureGarbage> m_textureGarbage;
+		MicroFencePtr m_fence;
+	};
+
+	Mutex m_mtx;
+	IntrusiveList<FrameGarbage> m_frames;
+
+	void collectGarbage();
+
+	FrameGarbage& getFrame();
+};
+/// @}
+
+} // end namespace anki

+ 40 - 26
AnKi/Gr/D3D/D3DGrManager.cpp

@@ -5,6 +5,7 @@
 
 
 #include <AnKi/Gr/D3D/D3DGrManager.h>
 #include <AnKi/Gr/D3D/D3DGrManager.h>
 #include <AnKi/Gr/D3D/D3DDescriptorHeap.h>
 #include <AnKi/Gr/D3D/D3DDescriptorHeap.h>
+#include <AnKi/Gr/D3D/D3DFrameGarbageCollector.h>
 
 
 #include <AnKi/Gr/D3D/D3DAccelerationStructure.h>
 #include <AnKi/Gr/D3D/D3DAccelerationStructure.h>
 #include <AnKi/Gr/D3D/D3DTexture.h>
 #include <AnKi/Gr/D3D/D3DTexture.h>
@@ -202,6 +203,8 @@ void GrManager::swapBuffers()
 	crntFrame.m_presentFence = presentFence;
 	crntFrame.m_presentFence = presentFence;
 
 
 	self.m_crntFrame = (self.m_crntFrame + 1) % self.m_frames.getSize();
 	self.m_crntFrame = (self.m_crntFrame + 1) % self.m_frames.getSize();
+
+	FrameGarbageCollector::getSingleton().endFrame(presentFence.get());
 }
 }
 
 
 void GrManager::finish()
 void GrManager::finish()
@@ -290,12 +293,6 @@ void GrManager::submit(WeakArray<CommandBuffer*> cmdbs, WeakArray<Fence*> waitFe
 	// Submit command lists
 	// Submit command lists
 	self.m_queues[queueType]->ExecuteCommandLists(d3dCmdLists.getSize(), d3dCmdLists.getBegin());
 	self.m_queues[queueType]->ExecuteCommandLists(d3dCmdLists.getSize(), d3dCmdLists.getBegin());
 
 
-	// Rest command lists
-	for(CommandBuffer* cmdb : cmdbs)
-	{
-		static_cast<CommandBufferImpl&>(*cmdb).getMicroCommandBuffer().resetCmdList();
-	}
-
 	// Signal fence
 	// Signal fence
 	fence->gpuSignal(queueType);
 	fence->gpuSignal(queueType);
 
 
@@ -325,10 +322,10 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 
 
 		if(g_gpuValidationCVar.get())
 		if(g_gpuValidationCVar.get())
 		{
 		{
-			ComPtr<ID3D12Debug4> debugInterface4;
-			ANKI_D3D_CHECK(debugInterface->QueryInterface(IID_PPV_ARGS(&debugInterface4)));
+			ComPtr<ID3D12Debug1> debugInterface1;
+			ANKI_D3D_CHECK(debugInterface->QueryInterface(IID_PPV_ARGS(&debugInterface1)));
 
 
-			debugInterface4->SetEnableGPUBasedValidation(true);
+			debugInterface1->SetEnableGPUBasedValidation(true);
 		}
 		}
 
 
 		AddVectoredExceptionHandler(true, vexHandler);
 		AddVectoredExceptionHandler(true, vexHandler);
@@ -404,15 +401,22 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	ANKI_D3D_LOGI("Vendor identified as %s", &kGPUVendorStrings[m_capabilities.m_gpuVendor][0]);
 	ANKI_D3D_LOGI("Vendor identified as %s", &kGPUVendorStrings[m_capabilities.m_gpuVendor][0]);
 
 
 	// Create device
 	// Create device
-	ANKI_D3D_CHECK(D3D12CreateDevice(adapters[chosenPhysDevIdx].m_adapter.Get(), D3D_FEATURE_LEVEL_12_2, IID_PPV_ARGS(&m_device)));
+	ANKI_D3D_CHECK(D3D12CreateDevice(adapters[chosenPhysDevIdx].m_adapter.Get(), D3D_FEATURE_LEVEL_12_1, IID_PPV_ARGS(&m_device)));
 
 
 	if(g_validationCVar.get())
 	if(g_validationCVar.get())
 	{
 	{
 		ComPtr<ID3D12InfoQueue1> infoq;
 		ComPtr<ID3D12InfoQueue1> infoq;
-		m_device->QueryInterface(IID_PPV_ARGS(&infoq));
+		const HRESULT res = m_device->QueryInterface(IID_PPV_ARGS(&infoq));
 
 
-		ANKI_D3D_CHECK(
-			infoq->RegisterMessageCallback(d3dDebugMessageCallback, D3D12_MESSAGE_CALLBACK_FLAG_NONE, nullptr, &m_debugMessageCallbackCookie));
+		if(res == S_OK)
+		{
+			ANKI_D3D_CHECK(
+				infoq->RegisterMessageCallback(d3dDebugMessageCallback, D3D12_MESSAGE_CALLBACK_FLAG_NONE, nullptr, &m_debugMessageCallbackCookie));
+		}
+		else
+		{
+			ANKI_D3D_LOGW("ID3D12InfoQueue1 not supported");
+		}
 	}
 	}
 
 
 	// Create queues
 	// Create queues
@@ -425,24 +429,17 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 		ANKI_D3D_CHECK(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_queues[GpuQueueType::kCompute])));
 		ANKI_D3D_CHECK(m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_queues[GpuQueueType::kCompute])));
 	}
 	}
 
 
-	// Limits
-	const U32 rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
-	const U32 cbvSrvUavDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
-
 	// Other systems
 	// Other systems
-	RtvDescriptorHeap::allocateSingleton();
-	ANKI_CHECK(RtvDescriptorHeap::getSingleton().init(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, g_maxRtvDescriptors.get(),
-													  rtvDescriptorSize));
-
-	CbvSrvUavDescriptorHeap::allocateSingleton();
-	ANKI_CHECK(CbvSrvUavDescriptorHeap::getSingleton().init(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
-															g_maxCbvSrvUavDescriptors.get(), cbvSrvUavDescriptorSize));
+	const Array<U16, D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES> descrCounts = {g_maxCbvSrvUavDescriptors.get(), 0, g_maxRtvDescriptors.get(), 0};
+	DescriptorHeaps::allocateSingleton();
+	ANKI_CHECK(DescriptorHeaps::getSingleton().init(descrCounts, *m_device));
 
 
 	SwapchainFactory::allocateSingleton();
 	SwapchainFactory::allocateSingleton();
 	m_crntSwapchain = SwapchainFactory::getSingleton().newInstance();
 	m_crntSwapchain = SwapchainFactory::getSingleton().newInstance();
 
 
 	FenceFactory::allocateSingleton();
 	FenceFactory::allocateSingleton();
 	CommandBufferFactory::allocateSingleton();
 	CommandBufferFactory::allocateSingleton();
+	FrameGarbageCollector::allocateSingleton();
 
 
 	return Error::kNone;
 	return Error::kNone;
 }
 }
@@ -451,10 +448,17 @@ void GrManagerImpl::destroy()
 {
 {
 	ANKI_D3D_LOGI("Destroying D3D backend");
 	ANKI_D3D_LOGI("Destroying D3D backend");
 
 
+	waitAllQueues();
+
+	// Cleanup self
+	m_crntSwapchain.reset(nullptr);
+	m_frames = {};
+
+	// Destroy systems
 	CommandBufferFactory::freeSingleton();
 	CommandBufferFactory::freeSingleton();
-	RtvDescriptorHeap::freeSingleton();
-	CbvSrvUavDescriptorHeap::freeSingleton();
 	SwapchainFactory::freeSingleton();
 	SwapchainFactory::freeSingleton();
+	FrameGarbageCollector::freeSingleton();
+	DescriptorHeaps::freeSingleton();
 	FenceFactory::freeSingleton();
 	FenceFactory::freeSingleton();
 
 
 	safeRelease(m_queues[GpuQueueType::kGeneral]);
 	safeRelease(m_queues[GpuQueueType::kGeneral]);
@@ -473,4 +477,14 @@ void GrManagerImpl::destroy()
 	GrMemoryPool::freeSingleton();
 	GrMemoryPool::freeSingleton();
 }
 }
 
 
+void GrManagerImpl::waitAllQueues()
+{
+	for(GpuQueueType queueType : EnumIterable<GpuQueueType>())
+	{
+		MicroFencePtr fence = FenceFactory::getSingleton().newInstance();
+		fence->gpuSignal(queueType);
+		fence->clientWait(kMaxSecond);
+	}
+}
+
 } // end namespace anki
 } // end namespace anki

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

@@ -62,6 +62,8 @@ private:
 	U8 m_crntFrame = 0;
 	U8 m_crntFrame = 0;
 
 
 	void destroy();
 	void destroy();
+
+	void waitAllQueues();
 };
 };
 /// @}
 /// @}
 
 

+ 1 - 0
AnKi/Gr/D3D/D3DSwapchainFactory.cpp

@@ -58,6 +58,7 @@ Error MicroSwapchain::initInternal()
 	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
 	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
 	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
 	swapChainDesc.SampleDesc.Count = 1;
 	swapChainDesc.SampleDesc.Count = 1;
+	swapChainDesc.Flags |= (!g_vsyncCVar.get()) ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
 
 
 	ComPtr<IDXGISwapChain1> swapChain;
 	ComPtr<IDXGISwapChain1> swapChain;
 	// Swap chain needs the queue so that it can force a flush on it.
 	// Swap chain needs the queue so that it can force a flush on it.

+ 158 - 73
AnKi/Gr/D3D/D3DTexture.cpp

@@ -5,6 +5,7 @@
 
 
 #include <AnKi/Gr/Texture.h>
 #include <AnKi/Gr/Texture.h>
 #include <AnKi/Gr/D3D/D3DTexture.h>
 #include <AnKi/Gr/D3D/D3DTexture.h>
+#include <AnKi/Gr/D3D/D3DFrameGarbageCollector.h>
 
 
 namespace anki {
 namespace anki {
 
 
@@ -28,42 +29,29 @@ U32 Texture::getOrCreateBindlessTextureIndex(const TextureSubresourceDescriptor&
 
 
 TextureImpl::~TextureImpl()
 TextureImpl::~TextureImpl()
 {
 {
-	auto deleteView = [](TextureViewEntry& entry, D3DTextureViewType type) {
-		DescriptorHeap* heap = nullptr;
-		switch(type)
-		{
-		case D3DTextureViewType::kSrv:
-		case D3DTextureViewType::kUav:
-			heap = &CbvSrvUavDescriptorHeap::getSingleton();
-			break;
-		case D3DTextureViewType::kRtv:
-			heap = &RtvDescriptorHeap::getSingleton();
-			break;
-		case D3DTextureViewType::kDsv:
-			heap = nullptr;
-			break;
-		default:
-			ANKI_ASSERT(0);
-		}
+	TextureGarbage* garbage = anki::newInstance<TextureGarbage>(GrMemoryPool::getSingleton());
 
 
-		heap->free(entry.m_handle);
-	};
+	for(auto it : m_viewsMap)
+	{
+		garbage->m_viewHandles.emplaceBack(it.m_handle);
+	}
 
 
-	for(D3DTextureViewType c : EnumIterable<D3DTextureViewType>())
+	if(m_wholeTextureSrv.m_handle.isCreated())
 	{
 	{
-		for(TextureViewEntry& entry : m_singleSurfaceOrVolumeViews[c])
-		{
-			deleteView(entry, c);
-		}
+		garbage->m_viewHandles.emplaceBack(m_wholeTextureSrv.m_handle);
+	}
 
 
-		deleteView(m_wholeTextureViews[c], c);
+	if(m_firstSurfaceRtvOrDsv.m_handle.isCreated())
+	{
+		garbage->m_viewHandles.emplaceBack(m_firstSurfaceRtvOrDsv.m_handle);
 	}
 	}
 
 
-	const Bool external = !!(m_usage & TextureUsageBit::kPresent);
-	if(!external)
+	if(!isExternal())
 	{
 	{
-		safeRelease(m_resource);
+		garbage->m_resource = m_resource;
 	}
 	}
+
+	FrameGarbageCollector::getSingleton().newTextureGarbage(garbage);
 }
 }
 
 
 Error TextureImpl::initInternal(ID3D12Resource* external, const TextureInitInfo& init)
 Error TextureImpl::initInternal(ID3D12Resource* external, const TextureInitInfo& init)
@@ -97,73 +85,170 @@ Error TextureImpl::initInternal(ID3D12Resource* external, const TextureInitInfo&
 		ANKI_ASSERT(!"TODO");
 		ANKI_ASSERT(!"TODO");
 	}
 	}
 
 
-	const U32 faceCount = textureTypeIsCube(m_texType) ? 6 : 1;
-	U32 surfaceCount = 0;
-	if(m_texType != TextureType::k3D)
+	// Create the default views
+	if(!!(m_usage & TextureUsageBit::kAllFramebuffer))
 	{
 	{
-		surfaceCount = m_layerCount * m_mipCount * faceCount;
+		const TextureView tview(this, TextureSubresourceDescriptor::firstSurface());
+
+		if(m_aspect == DepthStencilAspectBit::kNone)
+		{
+			m_firstSurfaceRtvOrDsv.m_viewType = D3DTextureViewType::kRtv;
+		}
+		else
+		{
+			m_firstSurfaceRtvOrDsv.m_viewType = D3DTextureViewType::kDsv;
+			m_firstSurfaceRtvOrDsv.m_dsvReadOnly = false;
+		}
+
+		m_firstSurfaceRtvOrDsv.m_handle =
+			createDescriptorHeapHandle(tview.getSubresource(), m_firstSurfaceRtvOrDsv.m_viewType, m_firstSurfaceRtvOrDsv.m_dsvReadOnly);
+		m_firstSurfaceRtvOrDsvSubresource = tview.getSubresource();
 	}
 	}
 
 
-	// Create RTVs
-	if(!!(m_usage & TextureUsageBit::kAllFramebuffer))
+	if(!!(m_usage & TextureUsageBit::kAllSampled))
+	{
+		const TextureView tview(this, TextureSubresourceDescriptor::all());
+
+		m_wholeTextureSrv.m_viewType = D3DTextureViewType::kSrv;
+		m_wholeTextureSrv.m_handle = createDescriptorHeapHandle(tview.getSubresource(), m_firstSurfaceRtvOrDsv.m_viewType, false);
+
+		m_wholeTextureSrvSubresource = tview.getSubresource();
+	}
+
+	return Error::kNone;
+}
+
+const TextureImpl::View& TextureImpl::getOrCreateView(const TextureSubresourceDescriptor& subresource, D3DTextureViewType viewType,
+													  TextureUsageBit usage) const
+{
+	ANKI_ASSERT(subresource == TextureView(this, subresource).getSubresource() && "Should have been sanitized");
+
+	const Bool readOnlyDsv =
+		m_aspect != DepthStencilAspectBit::kNone && (usage & TextureUsageBit::kAllFramebuffer) == TextureUsageBit::kFramebufferRead;
+
+	if(viewType == D3DTextureViewType::kSrv && subresource == m_wholeTextureSrvSubresource)
+	{
+		return m_wholeTextureSrv;
+	}
+	else if((viewType == D3DTextureViewType::kRtv || viewType == D3DTextureViewType::kDsv) && subresource == m_firstSurfaceRtvOrDsvSubresource
+			&& m_firstSurfaceRtvOrDsv.m_dsvReadOnly == readOnlyDsv)
+	{
+		return m_firstSurfaceRtvOrDsv;
+	}
+
+	// Slow path
+
+	ANKI_BEGIN_PACKED_STRUCT
+	class
 	{
 	{
-		ANKI_ASSERT(m_texType != TextureType::k3D && m_texType != TextureType::k1D);
-		ANKI_ASSERT(!getFormatInfo(m_format).isDepthStencil() && "TODO");
+	public:
+		TextureSubresourceDescriptor m_subresource;
+		D3DTextureViewType m_type;
+		Bool m_readOnlyDsv;
+	} toHash = {subresource, viewType, readOnlyDsv};
+	ANKI_END_PACKED_STRUCT
 
 
-		m_singleSurfaceOrVolumeViews[D3DTextureViewType::kRtv].resize(surfaceCount);
+	const U64 hash = computeHash(&toHash, sizeof(toHash));
 
 
-		for(U32 layer = 0; layer < m_layerCount; ++layer)
+	View* view = nullptr;
+	{
+		RLockGuard lock(m_viewsMapMtx);
+
+		auto it = m_viewsMap.find(hash);
+		if(it != m_viewsMap.getEnd())
 		{
 		{
-			for(U32 face = 0; face < faceCount; ++face)
-			{
-				for(U32 mip = 0; mip < m_mipCount; ++mip)
-				{
-					D3D12_RENDER_TARGET_VIEW_DESC desc = {};
-
-					if(m_texType == TextureType::k2D)
-					{
-						desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
-						desc.Texture2D.MipSlice = mip;
-						desc.Texture2D.PlaneSlice = 0;
-					}
-					else
-					{
-						desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
-						desc.Texture2DArray.ArraySize = 1;
-						desc.Texture2DArray.FirstArraySlice = layer * m_layerCount + face;
-					}
-
-					DescriptorHeapHandle& handle =
-						m_singleSurfaceOrVolumeViews[D3DTextureViewType::kRtv][layer * faceCount * m_mipCount + face * m_mipCount + mip].m_handle;
-					handle = RtvDescriptorHeap::getSingleton().allocate();
-					getDevice().CreateRenderTargetView(m_resource, (external) ? nullptr : &desc, handle.m_cpuHandle);
-				}
-			}
+			view = &(*it);
 		}
 		}
 	}
 	}
 
 
-	return Error::kNone;
+	if(view)
+	{
+		// Found, return it
+		return *view;
+	}
+
+	WLockGuard lock(m_viewsMapMtx);
+
+	// Search again
+	auto it = m_viewsMap.find(hash);
+	if(it != m_viewsMap.getEnd())
+	{
+		return *it;
+	}
+
+	// Need to create it
+	View& nview = *m_viewsMap.emplace(hash, *view);
+	nview.m_viewType = viewType;
+	nview.m_handle = createDescriptorHeapHandle(subresource, viewType, readOnlyDsv);
+	nview.m_dsvReadOnly = readOnlyDsv;
+
+	return nview;
 }
 }
 
 
-const TextureImpl::TextureViewEntry& TextureImpl::getViewEntry(const TextureSubresourceDescriptor& subresource, D3DTextureViewType viewType) const
+DescriptorHeapHandle TextureImpl::createDescriptorHeapHandle(const TextureSubresourceDescriptor& subresource, D3DTextureViewType viewType,
+															 Bool readOnlyDsv) const
 {
 {
-	const TextureView view(this, subresource);
-	const U32 faceCount = textureTypeIsCube(m_texType) ? 6 : 1;
+	DescriptorHeapHandle handle;
 
 
-	const TextureViewEntry* entry;
 	if(viewType == D3DTextureViewType::kRtv)
 	if(viewType == D3DTextureViewType::kRtv)
 	{
 	{
-		ANKI_ASSERT(view.isGoodForRenderTarget());
-		const U32 idx = subresource.m_layer * faceCount * m_mipCount + subresource.m_face * m_mipCount + subresource.m_mipmap;
-		entry = &m_singleSurfaceOrVolumeViews[D3DTextureViewType::kRtv][idx];
+		ANKI_ASSERT(TextureView(this, subresource).isGoodForRenderTarget() && m_aspect == DepthStencilAspectBit::kNone);
+
+		D3D12_RENDER_TARGET_VIEW_DESC desc = {};
+
+		if(m_texType == TextureType::k2D)
+		{
+			desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
+			desc.Texture2D.MipSlice = subresource.m_mipmap;
+			desc.Texture2D.PlaneSlice = 0;
+		}
+		else
+		{
+			desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
+			desc.Texture2DArray.ArraySize = 1;
+			desc.Texture2DArray.FirstArraySlice = subresource.m_layer * m_layerCount + subresource.m_face;
+			desc.Texture2DArray.MipSlice = subresource.m_mipmap;
+		}
+
+		handle = DescriptorHeaps::getSingleton().allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+		getDevice().CreateRenderTargetView(m_resource, (isExternal()) ? nullptr : &desc, handle.m_cpuHandle);
+	}
+	else if(viewType == D3DTextureViewType::kDsv)
+	{
+		ANKI_ASSERT(TextureView(this, subresource).isGoodForRenderTarget() && m_aspect != DepthStencilAspectBit::kNone);
+
+		D3D12_DEPTH_STENCIL_VIEW_DESC desc = {};
+
+		desc.Format = DXGI_FORMAT_UNKNOWN; // TODO
+
+		if(readOnlyDsv)
+		{
+			desc.Flags |= D3D12_DSV_FLAG_READ_ONLY_DEPTH;
+			desc.Flags |= D3D12_DSV_FLAG_READ_ONLY_STENCIL;
+		}
+
+		if(m_texType == TextureType::k2D)
+		{
+			desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
+			desc.Texture2D.MipSlice = subresource.m_mipmap;
+		}
+		else
+		{
+			desc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
+			desc.Texture2DArray.ArraySize = 1;
+			desc.Texture2DArray.FirstArraySlice = subresource.m_layer * m_layerCount + subresource.m_face;
+			desc.Texture2DArray.MipSlice = subresource.m_mipmap;
+		}
+
+		handle = DescriptorHeaps::getSingleton().allocate(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
+		getDevice().CreateDepthStencilView(m_resource, &desc, handle.m_cpuHandle);
 	}
 	}
 	else
 	else
 	{
 	{
 		ANKI_ASSERT(!"TODO");
 		ANKI_ASSERT(!"TODO");
 	}
 	}
 
 
-	ANKI_ASSERT(entry);
-	return *entry;
+	return handle;
 }
 }
 
 
 void TextureImpl::computeResourceStates(TextureUsageBit usage, D3D12_RESOURCE_STATES& states) const
 void TextureImpl::computeResourceStates(TextureUsageBit usage, D3D12_RESOURCE_STATES& states) const

+ 31 - 9
AnKi/Gr/D3D/D3DTexture.h

@@ -7,6 +7,7 @@
 
 
 #include <AnKi/Gr/Texture.h>
 #include <AnKi/Gr/Texture.h>
 #include <AnKi/Gr/D3D/D3DDescriptorHeap.h>
 #include <AnKi/Gr/D3D/D3DDescriptorHeap.h>
+#include <AnKi/Util/HashMap.h>
 
 
 namespace anki {
 namespace anki {
 
 
@@ -36,9 +37,16 @@ public:
 		return initInternal(external, init);
 		return initInternal(external, init);
 	}
 	}
 
 
-	const DescriptorHeapHandle& getView(const TextureSubresourceDescriptor& subresource, D3DTextureViewType viewType) const
+	DescriptorHeapHandle getOrCreateRtv(const TextureSubresourceDescriptor& subresource) const
 	{
 	{
-		const TextureViewEntry& e = getViewEntry(subresource, viewType);
+		const View& e = getOrCreateView(subresource, D3DTextureViewType::kRtv, TextureUsageBit::kNone);
+		ANKI_ASSERT(e.m_handle.isCreated());
+		return e.m_handle;
+	}
+
+	DescriptorHeapHandle getOrCreateDsv(const TextureSubresourceDescriptor& subresource, TextureUsageBit usage) const
+	{
+		const View& e = getOrCreateView(subresource, D3DTextureViewType::kDsv, usage);
 		ANKI_ASSERT(e.m_handle.isCreated());
 		ANKI_ASSERT(e.m_handle.isCreated());
 		return e.m_handle;
 		return e.m_handle;
 	}
 	}
@@ -71,25 +79,39 @@ public:
 	}
 	}
 
 
 private:
 private:
-	class TextureViewEntry
+	class View
 	{
 	{
 	public:
 	public:
 		DescriptorHeapHandle m_handle;
 		DescriptorHeapHandle m_handle;
-
-		mutable U32 m_bindlessIndex = kMaxU32;
-		mutable SpinLock m_bindlessIndexLock;
+		U32 m_bindlessIndex = kMaxU32;
+		D3DTextureViewType m_viewType;
+		Bool m_dsvReadOnly = false;
 	};
 	};
 
 
 	ID3D12Resource* m_resource = nullptr;
 	ID3D12Resource* m_resource = nullptr;
 
 
-	Array<GrDynamicArray<TextureViewEntry>, U32(D3DTextureViewType::kCount)> m_singleSurfaceOrVolumeViews;
-	Array<TextureViewEntry, U32(D3DTextureViewType::kCount)> m_wholeTextureViews;
+	mutable GrHashMap<U64, View> m_viewsMap;
+	mutable RWMutex m_viewsMapMtx;
+
+	// Cache a few common views
+	View m_wholeTextureSrv;
+	View m_firstSurfaceRtvOrDsv;
+	TextureSubresourceDescriptor m_wholeTextureSrvSubresource = TextureSubresourceDescriptor::all();
+	TextureSubresourceDescriptor m_firstSurfaceRtvOrDsvSubresource = TextureSubresourceDescriptor::all();
 
 
 	Error initInternal(ID3D12Resource* external, const TextureInitInfo& init);
 	Error initInternal(ID3D12Resource* external, const TextureInitInfo& init);
 
 
-	const TextureViewEntry& getViewEntry(const TextureSubresourceDescriptor& subresource, D3DTextureViewType viewType) const;
+	const View& getOrCreateView(const TextureSubresourceDescriptor& subresource, D3DTextureViewType viewType, TextureUsageBit usage) const;
 
 
 	void computeResourceStates(TextureUsageBit usage, D3D12_RESOURCE_STATES& states) const;
 	void computeResourceStates(TextureUsageBit usage, D3D12_RESOURCE_STATES& states) const;
+
+	DescriptorHeapHandle createDescriptorHeapHandle(const TextureSubresourceDescriptor& subresource, D3DTextureViewType viewType,
+													Bool readOnlyDsv) const;
+
+	Bool isExternal() const
+	{
+		return !!(m_usage & TextureUsageBit::kPresent);
+	}
 };
 };
 /// @}
 /// @}
 
 

+ 3 - 0
AnKi/Gr/Texture.h

@@ -202,6 +202,8 @@ public:
 
 
 	DepthStencilAspectBit m_depthStencilAspect = DepthStencilAspectBit::kNone;
 	DepthStencilAspectBit m_depthStencilAspect = DepthStencilAspectBit::kNone;
 
 
+	U8 _m_padding[2] = {0, 0};
+
 	constexpr TextureSubresourceDescriptor(const TextureSubresourceDescriptor&) = default;
 	constexpr TextureSubresourceDescriptor(const TextureSubresourceDescriptor&) = default;
 
 
 	constexpr TextureSubresourceDescriptor& operator=(const TextureSubresourceDescriptor&) = default;
 	constexpr TextureSubresourceDescriptor& operator=(const TextureSubresourceDescriptor&) = default;
@@ -278,6 +280,7 @@ private:
 		, m_allSurfacesOrVolumes(allSurfs)
 		, m_allSurfacesOrVolumes(allSurfs)
 		, m_depthStencilAspect(aspect)
 		, m_depthStencilAspect(aspect)
 	{
 	{
+		static_assert(sizeof(TextureSubresourceDescriptor) == 8, "Because it may get hashed");
 	}
 	}
 };
 };
 
 

+ 2 - 2
AnKi/Gr/Vulkan/VkFrameGarbageCollector.h

@@ -43,8 +43,8 @@ public:
 	VkAccelerationStructureKHR m_asHandle = VK_NULL_HANDLE;
 	VkAccelerationStructureKHR m_asHandle = VK_NULL_HANDLE;
 };
 };
 
 
-/// This class gathers various garbages and disposes them when in some later frame where it is safe to do so. This is
-/// used on bindless textures and buffers where we have to wait until the frame where they were deleted is done.
+/// This class gathers various garbages and disposes them when in some later frame where it is safe to do so. This is used on bindless textures and
+/// buffers where we have to wait until the frame where they were deleted is done.
 class FrameGarbageCollector
 class FrameGarbageCollector
 {
 {
 public:
 public:

+ 20 - 0
AnKi/Util/System.cpp

@@ -227,4 +227,24 @@ void postMain()
 	Logger::freeSingleton();
 	Logger::freeSingleton();
 }
 }
 
 
+#if ANKI_OS_WINDOWS
+String errorMessageToString(DWORD errorMessageID)
+{
+	if(errorMessageID == 0)
+	{
+		return "No error";
+	}
+
+	LPSTR messageBuffer = nullptr;
+
+	const PtrSize size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
+										errorMessageID, ANKI_MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, nullptr);
+
+	String message(messageBuffer, messageBuffer + size);
+	LocalFree(messageBuffer);
+
+	return message;
+}
+#endif
+
 } // end namespace anki
 } // end namespace anki

+ 5 - 0
AnKi/Util/System.h

@@ -51,6 +51,11 @@ void cleanupGetAndroidCommandLineArguments(void* ptr);
 
 
 /// Some common code to be called before main.
 /// Some common code to be called before main.
 void preMainInit();
 void preMainInit();
+
+#if ANKI_OS_WINDOWS
+/// Convert windows errors (from GetLastError) to strings.
+String errorMessageToString(DWORD errorMessageID);
+#endif
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 29 - 0
AnKi/Util/Win32Minimal.h

@@ -8,6 +8,8 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <cstdarg>
+
 // Minimal Windows.h declaration. Only what's needed.
 // Minimal Windows.h declaration. Only what's needed.
 extern "C" {
 extern "C" {
 
 
@@ -17,6 +19,8 @@ extern "C" {
 	struct name##__; \
 	struct name##__; \
 	typedef struct name##__* name
 	typedef struct name##__* name
 
 
+#define ANKI_MAKELANGID(p, s) ((((WORD)(s)) << 10) | (WORD)(p))
+
 // Types
 // Types
 typedef void VOID;
 typedef void VOID;
 typedef int BOOL;
 typedef int BOOL;
@@ -50,6 +54,8 @@ typedef HINSTANCE HMODULE;
 typedef wchar_t WCHAR;
 typedef wchar_t WCHAR;
 typedef const WCHAR* PCWSTR;
 typedef const WCHAR* PCWSTR;
 typedef WCHAR *NWPSTR, *LPWSTR, *PWSTR;
 typedef WCHAR *NWPSTR, *LPWSTR, *PWSTR;
+typedef const void* LPCVOID;
+typedef HANDLE HLOCAL;
 ANKI_DECLARE_HANDLE(HWND);
 ANKI_DECLARE_HANDLE(HWND);
 
 
 typedef struct _SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
 typedef struct _SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
@@ -120,6 +126,9 @@ ANKI_WINBASEAPI BOOL ANKI_WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput,
 ANKI_WINBASEAPI VOID ANKI_WINAPI GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
 ANKI_WINBASEAPI VOID ANKI_WINAPI GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);
 ANKI_WINBASEAPI DWORD ANKI_WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize);
 ANKI_WINBASEAPI DWORD ANKI_WINAPI GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize);
 ANKI_WINBASEAPI int ANKI_WINAPI MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
 ANKI_WINBASEAPI int ANKI_WINAPI MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
+ANKI_WINBASEAPI DWORD ANKI_WINAPI FormatMessageA(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize,
+												 va_list* Arguments);
+ANKI_WINBASEAPI HLOCAL ANKI_WINAPI LocalFree(HLOCAL hMem);
 
 
 #undef ANKI_WINBASEAPI
 #undef ANKI_WINBASEAPI
 #undef ANKI_DECLARE_HANDLE
 #undef ANKI_DECLARE_HANDLE
@@ -158,6 +167,16 @@ constexpr WORD MB_OK = 0x00000000L;
 constexpr WORD MB_ICONWARNING = 0x00000030L;
 constexpr WORD MB_ICONWARNING = 0x00000030L;
 constexpr WORD MB_ICONERROR = 0x00000010L;
 constexpr WORD MB_ICONERROR = 0x00000010L;
 
 
+constexpr DWORD FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
+constexpr DWORD FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
+constexpr DWORD FORMAT_MESSAGE_FROM_STRING = 0x00000400;
+constexpr DWORD FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
+constexpr DWORD FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
+constexpr DWORD FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
+constexpr DWORD FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF;
+constexpr DWORD LANG_NEUTRAL = 0x00;
+constexpr DWORD SUBLANG_DEFAULT = 0x01;
+
 // Types
 // Types
 typedef union _LARGE_INTEGER
 typedef union _LARGE_INTEGER
 {
 {
@@ -457,4 +476,14 @@ inline int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
 	return ::MessageBoxA(hWnd, lpText, lpCaption, uType);
 	return ::MessageBoxA(hWnd, lpText, lpCaption, uType);
 }
 }
 
 
+inline DWORD FormatMessageA(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, va_list* Arguments)
+{
+	return ::FormatMessageA(dwFlags, lpSource, dwMessageId, dwLanguageId, lpBuffer, nSize, Arguments);
+}
+
+inline HLOCAL LocalFree(HLOCAL hMem)
+{
+	return ::LocalFree(hMem);
+}
+
 } // end namespace anki
 } // end namespace anki