diff --git a/Juliet/Juliet.vcxproj b/Juliet/Juliet.vcxproj
index 7ca703f..58320fa 100644
--- a/Juliet/Juliet.vcxproj
+++ b/Juliet/Juliet.vcxproj
@@ -136,6 +136,7 @@
+
@@ -150,6 +151,8 @@
+
+
@@ -232,6 +235,8 @@
+
+
diff --git a/Juliet/include/Core/Common/CoreUtils.h b/Juliet/include/Core/Common/CoreUtils.h
index f769282..8d24753 100644
--- a/Juliet/include/Core/Common/CoreUtils.h
+++ b/Juliet/include/Core/Common/CoreUtils.h
@@ -33,4 +33,6 @@ namespace Juliet
*Byte++ = 0;
}
}
+
+#define MemCopy memcpy
} // namespace Juliet
diff --git a/Juliet/include/Core/Common/NonNullPtr.h b/Juliet/include/Core/Common/NonNullPtr.h
index 1883c82..52836fb 100644
--- a/Juliet/include/Core/Common/NonNullPtr.h
+++ b/Juliet/include/Core/Common/NonNullPtr.h
@@ -105,4 +105,7 @@ namespace Juliet
private:
Type* InternalPtr;
};
+
+ template
+ NonNullPtr(T) -> NonNullPtr;
} // namespace Juliet
diff --git a/Juliet/include/Core/Memory/Allocator.h b/Juliet/include/Core/Memory/Allocator.h
index 94ce63f..a1708bc 100644
--- a/Juliet/include/Core/Memory/Allocator.h
+++ b/Juliet/include/Core/Memory/Allocator.h
@@ -5,7 +5,7 @@
namespace Juliet
{
// Uninitialized allocation
- JULIET_API void* Malloc(size_t nb_elem, size_t elem_size);
+ JULIET_API void* Malloc(size_t elem_size);
// Initialized to 0 allocation
JULIET_API void* Calloc(size_t nb_elem, size_t elem_size);
JULIET_API void* Realloc(void* memory, size_t newSize);
diff --git a/Juliet/include/Graphics/Graphics.h b/Juliet/include/Graphics/Graphics.h
index 28b3c0c..8389115 100644
--- a/Juliet/include/Graphics/Graphics.h
+++ b/Juliet/include/Graphics/Graphics.h
@@ -45,6 +45,25 @@ namespace Juliet
Count
};
+ enum struct SwapChainComposition : uint8
+ {
+ SDR,
+ SDR_LINEAR,
+ HDR_EXTENDED_LINEAR,
+ HDR10_ST2084
+ };
+
+ // PresentMode from highest to lowest latency
+ // Vsync prevents tearing. Enqueue ready images.
+ // Mailbox prevents tearing. When image is ready, replace any pending image
+ // Immediate replace current image as soon as possible. Can cause tearing
+ enum struct PresentMode : uint8
+ {
+ VSync,
+ Mailbox,
+ Immediate
+ };
+
// Opaque types
struct CommandList;
diff --git a/Juliet/include/Graphics/Texture.h b/Juliet/include/Graphics/Texture.h
new file mode 100644
index 0000000..1e724e5
--- /dev/null
+++ b/Juliet/include/Graphics/Texture.h
@@ -0,0 +1,180 @@
+#pragma once
+#include
+
+namespace Juliet
+{
+ enum struct TextureFormat : uint8
+ {
+ Invalid,
+
+ /* Unsigned Normalized Float Color Formats */
+ A8_UNORM,
+ R8_UNORM,
+ R8G8_UNORM,
+ R8G8B8A8_UNORM,
+ R16_UNORM,
+ R16G16_UNORM,
+ R16G16B16A16_UNORM,
+ R10G10B10A2_UNORM,
+ B5G6R5_UNORM,
+ B5G5R5A1_UNORM,
+ B4G4R4A4_UNORM,
+ B8G8R8A8_UNORM,
+ /* Compressed Unsigned Normalized Float Color Formats */
+ BC1_RGBA_UNORM,
+ BC2_RGBA_UNORM,
+ BC3_RGBA_UNORM,
+ BC4_R_UNORM,
+ BC5_RG_UNORM,
+ BC7_RGBA_UNORM,
+ /* Compressed Signed Float Color Formats */
+ BC6H_RGB_FLOAT,
+ /* Compressed Unsigned Float Color Formats */
+ BC6H_RGB_UFLOAT,
+ /* Signed Normalized Float Color Formats */
+ R8_SNORM,
+ R8G8_SNORM,
+ R8G8B8A8_SNORM,
+ R16_SNORM,
+ R16G16_SNORM,
+ R16G16B16A16_SNORM,
+ /* Signed Float Color Formats */
+ R16_FLOAT,
+ R16G16_FLOAT,
+ R16G16B16A16_FLOAT,
+ R32_FLOAT,
+ R32G32_FLOAT,
+ R32G32B32A32_FLOAT,
+ /* Unsigned Float Color Formats */
+ R11G11B10_UFLOAT,
+ /* Unsigned Integer Color Formats */
+ R8_UINT,
+ R8G8_UINT,
+ R8G8B8A8_UINT,
+ R16_UINT,
+ R16G16_UINT,
+ R16G16B16A16_UINT,
+ R32_UINT,
+ R32G32_UINT,
+ R32G32B32A32_UINT,
+ /* Signed Integer Color Formats */
+ R8_INT,
+ R8G8_INT,
+ R8G8B8A8_INT,
+ R16_INT,
+ R16G16_INT,
+ R16G16B16A16_INT,
+ R32_INT,
+ R32G32_INT,
+ R32G32B32A32_INT,
+ /* SRGB Unsigned Normalized Color Formats */
+ R8G8B8A8_UNORM_SRGB,
+ B8G8R8A8_UNORM_SRGB,
+ /* Compressed SRGB Unsigned Normalized Color Formats */
+ BC1_RGBA_UNORM_SRGB,
+ BC2_RGBA_UNORM_SRGB,
+ BC3_RGBA_UNORM_SRGB,
+ BC7_RGBA_UNORM_SRGB,
+ /* Depth Formats */
+ D16_UNORM,
+ D24_UNORM,
+ D32_FLOAT,
+ D24_UNORM_S8_UINT,
+ D32_FLOAT_S8_UINT,
+ /* Compressed ASTC Normalized Float Color Formats*/
+ ASTC_4x4_UNORM,
+ ASTC_5x4_UNORM,
+ ASTC_5x5_UNORM,
+ ASTC_6x5_UNORM,
+ ASTC_6x6_UNORM,
+ ASTC_8x5_UNORM,
+ ASTC_8x6_UNORM,
+ ASTC_8x8_UNORM,
+ ASTC_10x5_UNORM,
+ ASTC_10x6_UNORM,
+ ASTC_10x8_UNORM,
+ ASTC_10x10_UNORM,
+ ASTC_12x10_UNORM,
+ ASTC_12x12_UNORM,
+ /* Compressed SRGB ASTC Normalized Float Color Formats*/
+ ASTC_4x4_UNORM_SRGB,
+ ASTC_5x4_UNORM_SRGB,
+ ASTC_5x5_UNORM_SRGB,
+ ASTC_6x5_UNORM_SRGB,
+ ASTC_6x6_UNORM_SRGB,
+ ASTC_8x5_UNORM_SRGB,
+ ASTC_8x6_UNORM_SRGB,
+ ASTC_8x8_UNORM_SRGB,
+ ASTC_10x5_UNORM_SRGB,
+ ASTC_10x6_UNORM_SRGB,
+ ASTC_10x8_UNORM_SRGB,
+ ASTC_10x10_UNORM_SRGB,
+ ASTC_12x10_UNORM_SRGB,
+ ASTC_12x12_UNORM_SRGB,
+ /* Compressed ASTC Signed Float Color Formats*/
+ ASTC_4x4_FLOAT,
+ ASTC_5x4_FLOAT,
+ ASTC_5x5_FLOAT,
+ ASTC_6x5_FLOAT,
+ ASTC_6x6_FLOAT,
+ ASTC_8x5_FLOAT,
+ ASTC_8x6_FLOAT,
+ ASTC_8x8_FLOAT,
+ ASTC_10x5_FLOAT,
+ ASTC_10x6_FLOAT,
+ ASTC_10x8_FLOAT,
+ ASTC_10x10_FLOAT,
+ ASTC_12x10_FLOAT,
+ ASTC_12x12_FLOAT
+ };
+
+ enum struct TextureUsageFlag : uint8
+ {
+ None = 0,
+ Sampler = 1 << 0, // Textures supports sampling
+ ColorTarget = 1 << 1, // Texture is color render target
+ DepthStencilTarget = 1 << 2, // Texture is depth stencil target
+ GraphicsStorageRead = 1 << 3, // Support Storage read at graphics stage
+ ComputeStorageRead = 1 << 4, // Support Storage read at compute stage
+ ComputeStorageWrite = 1 << 5, // Support Storage Write at compute stage
+ ComputeStorageSimultaneousReadWrite =
+ 1 << 6, // Supports reads and writes in the same compute shader. Not equivalent to ComputeStorageRead | ComputeStorageWrite
+ };
+
+ enum struct TextureType : uint8
+ {
+ Texture_2D,
+ Texture_2DArray,
+ Texture_3D,
+ Texture_3DArray,
+ Texture_Cube,
+ Texture_CubeArray,
+ };
+
+ enum struct TextureSampleCount : uint8
+ {
+ One,
+ Two,
+ Four,
+ Eight,
+ };
+
+ // Create Information structs
+ struct TextureCreateInfo
+ {
+ TextureType Type;
+ TextureFormat Format;
+ TextureUsageFlag Flags;
+ TextureSampleCount SampleCount;
+
+ uint32 Width;
+ uint32 Height;
+ union
+ {
+ uint32 LayerCount;
+ uint32 Depth;
+ }; // LayerCount is used in 2d array textures and Depth for 3d textures
+ uint32 MipLevelCount;
+ };
+
+} // namespace Juliet
diff --git a/Juliet/src/Core/Memory/Allocator.cpp b/Juliet/src/Core/Memory/Allocator.cpp
index e8e4856..42a7644 100644
--- a/Juliet/src/Core/Memory/Allocator.cpp
+++ b/Juliet/src/Core/Memory/Allocator.cpp
@@ -1,3 +1,4 @@
+#include
#include
#include
diff --git a/Juliet/src/Graphics/D3D12/D3D12Common.cpp b/Juliet/src/Graphics/D3D12/D3D12Common.cpp
new file mode 100644
index 0000000..ea5aa58
--- /dev/null
+++ b/Juliet/src/Graphics/D3D12/D3D12Common.cpp
@@ -0,0 +1,168 @@
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+namespace Juliet::D3D12
+{
+ namespace
+ {
+ constexpr size_t kStagingHeapDescriptorExpectedCount = 1024;
+
+ void InitStagingDescriptorPool(NonNullPtr heap, NonNullPtr pool)
+ {
+ for (uint32 idx = 0; idx < kStagingHeapDescriptorExpectedCount; ++idx)
+ {
+ pool->FreeDescriptors[idx].Pool = pool;
+ pool->FreeDescriptors[idx].Heap = heap.Get();
+ pool->FreeDescriptors[idx].CpuHandleIndex = idx;
+ pool->FreeDescriptors[idx].CpuHandle.ptr = heap->DescriptorHeapCPUStart.ptr + (idx * heap->DescriptorSize);
+ }
+ }
+
+ bool ExtendStagingDescriptorPool(NonNullPtr driver, D3D12StagingDescriptorPool& pool)
+ {
+ D3D12DescriptorHeap* heap =
+ CreateDescriptorHeap(driver, pool.Heaps[0]->HeapType, kStagingHeapDescriptorExpectedCount, true);
+ if (!heap)
+ {
+ return false;
+ }
+
+ pool.HeapCount += 1;
+ pool.Heaps = static_cast(Realloc(pool.Heaps, pool.HeapCount * sizeof(D3D12DescriptorHeap*)));
+ pool.Heaps[pool.HeapCount - 1] = heap;
+
+ pool.FreeDescriptorCapacity += kStagingHeapDescriptorExpectedCount;
+ pool.FreeDescriptorCount += kStagingHeapDescriptorExpectedCount;
+ pool.FreeDescriptors = static_cast(
+ Realloc(pool.FreeDescriptors, pool.FreeDescriptorCapacity * sizeof(D3D12StagingDescriptor)));
+
+ InitStagingDescriptorPool(heap, &pool);
+
+ return true;
+ }
+ } // namespace
+
+ D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr driver, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging)
+ {
+ ID3D12DescriptorHeap* handle;
+
+ auto heap = static_cast(Calloc(1, sizeof(D3D12DescriptorHeap)));
+ if (!heap)
+ {
+ return nullptr;
+ }
+
+ heap->CurrentDescriptorIndex = 0;
+
+ D3D12_DESCRIPTOR_HEAP_DESC heapDesc;
+ heapDesc.NumDescriptors = count;
+ heapDesc.Type = type;
+ heapDesc.Flags = isStaging ? D3D12_DESCRIPTOR_HEAP_FLAG_NONE : D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
+ heapDesc.NodeMask = 0;
+
+ HRESULT result =
+ ID3D12Device_CreateDescriptorHeap(driver->D3D12Device, &heapDesc, IID_ID3D12DescriptorHeap, (void**)&handle);
+ if (FAILED(result))
+ {
+ LogError(driver, "Failed to create descriptor heap!", result);
+ DestroyDescriptorHeap(heap);
+ return nullptr;
+ }
+
+ heap->Handle = handle;
+ heap->HeapType = type;
+ heap->MaxDescriptors = count;
+ heap->Staging = isStaging;
+ heap->DescriptorSize = ID3D12Device_GetDescriptorHandleIncrementSize(driver->D3D12Device, type);
+ ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(handle, &heap->DescriptorHeapCPUStart);
+ if (!isStaging)
+ {
+ ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(handle, &heap->DescriptorHeapGPUStart);
+ }
+
+ return heap;
+ }
+
+ void DestroyDescriptorHeap(NonNullPtr heap)
+ {
+ if (heap->Handle)
+ {
+ ID3D12DescriptorHeap_Release(heap->Handle);
+ }
+ Free(heap.Get());
+ }
+
+ D3D12StagingDescriptorPool* CreateStagingDescriptorPool(NonNullPtr driver, D3D12_DESCRIPTOR_HEAP_TYPE type)
+ {
+ D3D12DescriptorHeap* heap = CreateDescriptorHeap(driver, type, kStagingHeapDescriptorExpectedCount, true);
+ if (!heap)
+ {
+ return nullptr;
+ }
+
+ auto pool = static_cast(Calloc(1, sizeof(D3D12StagingDescriptorPool)));
+
+ // First create the heaps
+ pool->HeapCount = 1;
+ pool->Heaps = static_cast(Malloc(sizeof(D3D12DescriptorHeap*)));
+ pool->Heaps[0] = heap;
+
+ pool->FreeDescriptorCapacity = kStagingHeapDescriptorExpectedCount;
+ pool->FreeDescriptorCount = kStagingHeapDescriptorExpectedCount;
+ pool->FreeDescriptors =
+ static_cast(Malloc(kStagingHeapDescriptorExpectedCount * sizeof(D3D12StagingDescriptor)));
+
+ InitStagingDescriptorPool(heap, pool);
+
+ return pool;
+ }
+
+ bool AssignStagingDescriptor(NonNullPtr driver, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12StagingDescriptor& outDescriptor)
+ {
+ // TODO: Make it thread safe
+ D3D12StagingDescriptor* descriptor = nullptr;
+ D3D12StagingDescriptorPool* pool = driver->StagingDescriptorPools[type];
+
+ if (pool->FreeDescriptorCount == 0)
+ {
+ if (!ExtendStagingDescriptorPool(driver, *pool))
+ {
+ return false;
+ }
+ }
+
+ descriptor = &pool->FreeDescriptors[pool->FreeDescriptorCount - 1];
+ MemCopy(&outDescriptor, descriptor, sizeof(D3D12StagingDescriptor));
+ pool->FreeDescriptorCount -= 1;
+
+ return true;
+ }
+
+ void ReleaseStagingDescriptor(NonNullPtr driver, D3D12StagingDescriptor& cpuDescriptor)
+ {
+ D3D12StagingDescriptorPool* pool = cpuDescriptor.Pool;
+
+ if (pool != nullptr)
+ {
+ MemCopy(&pool->FreeDescriptors[pool->FreeDescriptorCount], &cpuDescriptor, sizeof(D3D12StagingDescriptor));
+ pool->FreeDescriptorCount += 1;
+ }
+ }
+
+ void DestroyStagingDescriptorPool(NonNullPtr pool)
+ {
+ for (uint32 i = 0; i < pool->HeapCount; i += 1)
+ {
+ DestroyDescriptorHeap(pool->Heaps[i]);
+ }
+
+ Free(pool->Heaps);
+ Free(pool->FreeDescriptors);
+ Free(pool.Get());
+ }
+} // namespace Juliet::D3D12
diff --git a/Juliet/src/Graphics/D3D12/D3D12Common.h b/Juliet/src/Graphics/D3D12/D3D12Common.h
new file mode 100644
index 0000000..7afddca
--- /dev/null
+++ b/Juliet/src/Graphics/D3D12/D3D12Common.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include
+#include
+#include
+
+// Definitions:
+// RTV = Render Target View
+// DSV = Depth Stencil View
+// SRV = Shader Resource View
+// UAV = Unordered Access View
+// CBV = Constant Buffer View
+
+// Inspired (copy pasted a lot) by SDL GPU
+namespace Juliet::D3D12
+{
+ struct D3D12Driver;
+ // Forward declare
+ struct D3D12StagingDescriptor;
+
+ // https://learn.microsoft.com/en-us/windows/win32/direct3d12/descriptor-heaps
+ struct D3D12DescriptorHeap
+ {
+ ID3D12DescriptorHeap* Handle;
+ D3D12_DESCRIPTOR_HEAP_TYPE HeapType;
+ D3D12_CPU_DESCRIPTOR_HANDLE DescriptorHeapCPUStart;
+ D3D12_GPU_DESCRIPTOR_HANDLE DescriptorHeapGPUStart; // only used by GPU heaps
+ uint32 MaxDescriptors;
+ uint32 DescriptorSize;
+ uint32 CurrentDescriptorIndex; // only used by GPU heaps
+ bool Staging : 1;
+ };
+
+ struct D3D12StagingDescriptorPool
+ {
+ D3D12DescriptorHeap** Heaps;
+ uint32 HeapCount;
+
+ // Descriptor handles are owned by resources, so these can be thought of as descriptions of a free index within a heap.
+ uint32 FreeDescriptorCapacity;
+ uint32 FreeDescriptorCount;
+ D3D12StagingDescriptor* FreeDescriptors;
+ };
+
+ // https://learn.microsoft.com/en-us/windows/win32/direct3d12/descriptors-overview
+ struct D3D12StagingDescriptor
+ {
+ D3D12StagingDescriptorPool* Pool;
+ D3D12DescriptorHeap* Heap;
+ D3D12_CPU_DESCRIPTOR_HANDLE CpuHandle;
+ uint32 CpuHandleIndex;
+ };
+
+ extern D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr driver, D3D12_DESCRIPTOR_HEAP_TYPE type,
+ uint32 count, bool isStaging);
+ extern void DestroyDescriptorHeap(NonNullPtr heap);
+
+ extern D3D12StagingDescriptorPool* CreateStagingDescriptorPool(NonNullPtr driver, D3D12_DESCRIPTOR_HEAP_TYPE type);
+ extern bool AssignStagingDescriptor(NonNullPtr driver, D3D12_DESCRIPTOR_HEAP_TYPE type,
+ D3D12StagingDescriptor& outDescriptor);
+ extern void ReleaseStagingDescriptor(NonNullPtr driver, D3D12StagingDescriptor& cpuDescriptor);
+ extern void DestroyStagingDescriptorPool(NonNullPtr pool);
+} // namespace Juliet::D3D12
diff --git a/Juliet/src/Graphics/D3D12/D3D12Texture.cpp b/Juliet/src/Graphics/D3D12/D3D12Texture.cpp
new file mode 100644
index 0000000..7400dfc
--- /dev/null
+++ b/Juliet/src/Graphics/D3D12/D3D12Texture.cpp
@@ -0,0 +1,8 @@
+#include
+
+#include
+
+namespace Juliet::D3D12
+{
+
+}
\ No newline at end of file
diff --git a/Juliet/src/Graphics/D3D12/D3D12Texture.h b/Juliet/src/Graphics/D3D12/D3D12Texture.h
new file mode 100644
index 0000000..d94cf47
--- /dev/null
+++ b/Juliet/src/Graphics/D3D12/D3D12Texture.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include
+#include
+#include
+
+struct ID3D12Resource;
+
+namespace Juliet::D3D12
+{
+ struct D3D12Texture;
+
+ struct D3D12ResourceHeap;
+
+ struct D3D12TextureContainer
+ {
+ TextureHeader Header;
+
+ D3D12Texture* ActiveTexture;
+ D3D12Texture** Textures;
+ uint32 Capacity;
+ uint32 Count;
+
+ // Note: Swapchain images cannot be cycled
+ bool CanBeCycled;
+
+#if JULIET_DEBUG
+ char* DebugName;
+#endif
+ };
+
+ // D3D12 subresourcces: https://learn.microsoft.com/en-us/windows/win32/direct3d12/subresources (mipmaps, etc..)
+ using D3D12TextureSubresource = struct D3D12TextureSubresource
+ {
+ D3D12Texture* Parent;
+ uint32 Layer;
+ uint32 Level;
+ uint32 Depth;
+ uint32 Index;
+
+ // One per depth slice
+ D3D12StagingDescriptor* RTVHandles; // NULL if not a color target
+
+ D3D12StagingDescriptor UAVHandle; // NULL if not a compute storage write texture
+ D3D12StagingDescriptor DSVHandle; // NULL if not a depth stencil target
+ };
+
+ struct D3D12Texture
+ {
+ D3D12TextureContainer* Container;
+ uint32 IndexInContainer;
+
+ ID3D12Resource* Resource;
+
+ D3D12TextureSubresource* Subresources;
+ uint32 SubresourceCount; // Layer Count * number of Levels
+
+ D3D12StagingDescriptor SRVHandle;
+
+ // TODO: Should be atomic to support multithreading
+ int32 RefCount;
+ };
+} // namespace Juliet::D3D12
diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp
index d484939..9172af6 100644
--- a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp
+++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp
@@ -167,6 +167,15 @@ namespace Juliet::D3D12
void DestroyDriver_Internal(NonNullPtr driver)
{
+ for (uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1)
+ {
+ if (driver->StagingDescriptorPools[i])
+ {
+ DestroyStagingDescriptorPool(driver->StagingDescriptorPools[i]);
+ driver->StagingDescriptorPools[i] = nullptr;
+ }
+ }
+
if (driver->IndirectDrawCommandSignature)
{
ID3D12CommandSignature_Release(driver->IndirectDrawCommandSignature);
@@ -240,7 +249,7 @@ namespace Juliet::D3D12
windowData->Window = window;
- if (!CreateSwapChain(d3d12Driver, windowData))
+ if (!CreateSwapChain(d3d12Driver, windowData, SwapChainComposition::SDR, PresentMode::VSync))
{
Log(LogLevel::Error, LogCategory::Graphics, "AttachToWindow failure: Cannot create Swap Chain.");
Free(windowData);
@@ -515,6 +524,20 @@ namespace Juliet::D3D12
return nullptr;
}
+ // Create Pools
+
+ for (uint32 i = 0; i < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; i += 1)
+ {
+ driver->StagingDescriptorPools[i] =
+ CreateStagingDescriptorPool(driver, static_cast(i));
+
+ if (driver->StagingDescriptorPools[i] == nullptr)
+ {
+ DestroyDriver_Internal(driver);
+ return NULL;
+ }
+ }
+
// Assign Functions to the device
device->DestroyDevice = DestroyGraphicsDevice;
diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h
index 2e0cc09..d9923bb 100644
--- a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h
+++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h
@@ -1,7 +1,8 @@
#pragma once
#include
-#include
+#include
+#include
#include
namespace Juliet
@@ -17,10 +18,17 @@ namespace Juliet::D3D12
{
Window* Window;
- IDXGISwapChain3* SwapChain;
- uint8 SwapChainTextureCount;
+ IDXGISwapChain3* SwapChain;
+ D3D12TextureContainer SwapChainTextureContainers[GPUDriver::kMaxFramesInFlight];
+ DXGI_COLOR_SPACE_TYPE SwapChainColorSpace;
+ SwapChainComposition SwapChainComposition;
+ uint8 SwapChainTextureCount;
+
+ PresentMode PresentMode;
uint32 FrameCounter;
+ uint32 Width;
+ uint32 Height;
};
struct D3D12Driver : GPUDriver
@@ -56,6 +64,8 @@ namespace Juliet::D3D12
D3D12CommandList** AvailableCommandLists;
uint8 AvailableCommandListCapacity;
+ D3D12StagingDescriptorPool* StagingDescriptorPools[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
+
uint8 FramesInFlight;
bool IsTearingSupported : 1;
diff --git a/Juliet/src/Graphics/D3D12/DX12SwapChain.cpp b/Juliet/src/Graphics/D3D12/DX12SwapChain.cpp
index ed9e53b..3898a97 100644
--- a/Juliet/src/Graphics/D3D12/DX12SwapChain.cpp
+++ b/Juliet/src/Graphics/D3D12/DX12SwapChain.cpp
@@ -1,14 +1,140 @@
#include
#include
+#include
+#include
#include
+#include
#include
+#include
#include
-
namespace Juliet::D3D12
{
- bool CreateSwapChain(NonNullPtr driver, NonNullPtr windowData)
+ namespace
+ {
+ DXGI_COLOR_SPACE_TYPE SwapchainCompositionToColorSpace[] = {
+ DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, // SDR
+ DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, // SDR_LINEAR
+ DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, // HDR_EXTENDED_LINEAR
+ DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 // HDR10_ST2084
+ };
+
+ DXGI_FORMAT SwapchainCompositionToTextureFormat[] = {
+ DXGI_FORMAT_B8G8R8A8_UNORM, // SDR
+ DXGI_FORMAT_B8G8R8A8_UNORM, // SDR_LINEAR (NOTE: The RTV uses the sRGB format)
+ DXGI_FORMAT_R16G16B16A16_FLOAT, // HDR_EXTENDED_LINEAR
+ DXGI_FORMAT_R10G10B10A2_UNORM, // HDR10_ST2084
+ };
+
+ TextureFormat SwapchainCompositionToJulietTextureFormat[] = {
+ TextureFormat::B8G8R8A8_UNORM, // SDR
+ TextureFormat::B8G8R8A8_UNORM_SRGB, // SDR_LINEAR
+ TextureFormat::R16G16B16A16_FLOAT, // HDR_EXTENDED_LINEAR
+ TextureFormat::R10G10B10A2_UNORM, // HDR10_ST2084
+ };
+
+ bool CreateSwapChainTexture(NonNullPtr driver, NonNullPtr swapChain,
+ SwapChainComposition composition, NonNullPtr textureContainer, uint8 index)
+ {
+ ID3D12Resource* swapChainTexture = nullptr;
+ HRESULT result =
+ IDXGISwapChain_GetBuffer(swapChain, index, IID_ID3D12Resource, reinterpret_cast(&swapChainTexture));
+ if (FAILED(result))
+ {
+ LogError(driver, "Cannot get buffer from SwapChain", result);
+ return false;
+ }
+
+ auto texture = static_cast(Calloc(1, sizeof(D3D12Texture)));
+ if (!texture)
+ {
+ LogError(driver, "Cannot allocate D3D12Texture (out of memory)", result);
+ ID3D12Resource_Release(swapChainTexture);
+ return false;
+ }
+
+ texture->RefCount += 1;
+ texture->SubresourceCount = 1;
+ texture->Subresources = static_cast(Calloc(1, sizeof(D3D12TextureSubresource)));
+ if (!texture->Subresources)
+ {
+ LogError(driver, "Cannot allocate D3D12TextureSubresource (out of memory)", result);
+ Free(texture);
+ ID3D12Resource_Release(swapChainTexture);
+ return false;
+ }
+ texture->Subresources[0].RTVHandles =
+ static_cast(Calloc(1, sizeof(D3D12StagingDescriptor)));
+ texture->Subresources[0].UAVHandle.Heap = nullptr;
+ texture->Subresources[0].UAVHandle.Heap = nullptr;
+ texture->Subresources[0].Parent = texture;
+ texture->Subresources[0].Index = 0;
+ texture->Subresources[0].Layer = 0;
+ texture->Subresources[0].Depth = 1;
+ texture->Subresources[0].Level = 0;
+
+ D3D12_RESOURCE_DESC textureDesc;
+ ID3D12Resource_GetDesc(swapChainTexture, &textureDesc);
+ textureContainer->Header.CreateInfo.Width = static_cast(textureDesc.Width);
+ textureContainer->Header.CreateInfo.Height = static_cast(textureDesc.Height);
+ textureContainer->Header.CreateInfo.LayerCount = 1;
+ textureContainer->Header.CreateInfo.MipLevelCount = 1;
+ textureContainer->Header.CreateInfo.Type = TextureType::Texture_2D;
+ textureContainer->Header.CreateInfo.Flags = TextureUsageFlag::ColorTarget;
+ textureContainer->Header.CreateInfo.SampleCount = TextureSampleCount::One;
+ textureContainer->Header.CreateInfo.Format = SwapchainCompositionToJulietTextureFormat[ToUnderlying(composition)];
+
+ textureContainer->Textures = static_cast(Calloc(1, sizeof(D3D12Texture*)));
+ if (!textureContainer->Textures)
+ {
+ Free(texture->Subresources);
+ Free(texture);
+ ID3D12Resource_Release(swapChainTexture);
+ return false;
+ }
+
+ textureContainer->Capacity = 1;
+ textureContainer->Count = 1;
+ textureContainer->Textures[0] = texture;
+ textureContainer->ActiveTexture = texture;
+ textureContainer->CanBeCycled = false;
+
+ texture->Container = textureContainer;
+ texture->IndexInContainer = 0;
+
+ // Assign SRV to the swapchain texture
+ AssignStagingDescriptor(driver, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, texture->SRVHandle);
+ D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
+ srvDesc.Format = SwapchainCompositionToTextureFormat[ToUnderlying(composition)];
+ srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+ srvDesc.Texture2D.MipLevels = 1;
+ srvDesc.Texture2D.MostDetailedMip = 0;
+ srvDesc.Texture2D.ResourceMinLODClamp = 0;
+ srvDesc.Texture2D.PlaneSlice = 0;
+ ID3D12Device_CreateShaderResourceView(driver->D3D12Device, swapChainTexture, &srvDesc, texture->SRVHandle.CpuHandle);
+
+ // Assign RTV to the swapchain texture
+ DXGI_FORMAT swapchainFormat = SwapchainCompositionToTextureFormat[ToUnderlying(composition)];
+ AssignStagingDescriptor(driver, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, texture->Subresources[0].RTVHandles[0]);
+ D3D12_RENDER_TARGET_VIEW_DESC rtvDesc;
+ rtvDesc.Format = (composition == SwapChainComposition::SDR_LINEAR) ? DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : swapchainFormat;
+ rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
+ rtvDesc.Texture2D.MipSlice = 0;
+ rtvDesc.Texture2D.PlaneSlice = 0;
+
+ ID3D12Device_CreateRenderTargetView(driver->D3D12Device, swapChainTexture, &rtvDesc,
+ texture->Subresources[0].RTVHandles[0].CpuHandle);
+
+ ID3D12Resource_Release(swapChainTexture);
+
+ return true;
+ }
+ } // namespace
+
+ bool CreateSwapChain(NonNullPtr driver, NonNullPtr windowData,
+ SwapChainComposition composition, PresentMode presentMode)
{
auto windowWin32State = static_cast(windowData->Window->State);
HWND windowHandle = windowWin32State->Handle;
@@ -18,9 +144,8 @@ namespace Juliet::D3D12
return false;
}
- // SDR.
- // TODO: Not enough for HDR. but i have no way to test HDR easily except the steamdeck
- DXGI_FORMAT swapChainFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
+ // TODO: I have no way to test HDR easily except the steamdeck
+ DXGI_FORMAT swapChainFormat = SwapchainCompositionToTextureFormat[ToUnderlying(composition)];
windowData->SwapChainTextureCount = std::clamp(driver->FramesInFlight, 2, 3);
@@ -71,6 +196,11 @@ namespace Juliet::D3D12
return false;
}
+ if (composition != SwapChainComposition::SDR)
+ {
+ IDXGISwapChain3_SetColorSpace1(swapChain3, SwapchainCompositionToColorSpace[ToUnderlying(composition)]);
+ }
+
IDXGIFactory1* parentFactory = nullptr;
result = IDXGISwapChain3_GetParent(swapChain3, IID_IDXGIFactory1, reinterpret_cast(&parentFactory));
if (FAILED(result))
@@ -94,11 +224,40 @@ namespace Juliet::D3D12
LogError(driver, "Failed to retrieve SwapChain descriptor", result);
return false;
}
- windowData->SwapChain = swapChain3;
- windowData->FrameCounter = 0;
+ windowData->SwapChain = swapChain3;
+ windowData->SwapChainColorSpace = SwapchainCompositionToColorSpace[ToUnderlying(composition)];
+ windowData->SwapChainComposition = composition;
+ windowData->FrameCounter = 0;
+ windowData->Width = swapChainDesc.Width;
+ windowData->Height = swapChainDesc.Height;
+ windowData->PresentMode = presentMode;
+
+ for (uint8 idx = 0; idx < windowData->SwapChainTextureCount; ++idx)
+ {
+ if (!CreateSwapChainTexture(driver, swapChain3, composition, &windowData->SwapChainTextureContainers[idx], idx))
+ {
+ IDXGISwapChain3_Release(swapChain3);
+ return false;
+ }
+ }
return true;
}
- void DestroySwapChain(NonNullPtr driver, NonNullPtr windowData) {}
+ void DestroySwapChain(NonNullPtr driver, NonNullPtr windowData)
+ {
+ for (uint32 idx = 0; idx < windowData->SwapChainTextureCount; ++idx)
+ {
+ ReleaseStagingDescriptor(driver, windowData->SwapChainTextureContainers[idx].ActiveTexture->SRVHandle);
+ ReleaseStagingDescriptor(driver, windowData->SwapChainTextureContainers[idx].ActiveTexture->Subresources[0].RTVHandles[0]);
+
+ Free(windowData->SwapChainTextureContainers[idx].ActiveTexture->Subresources[0].RTVHandles);
+ Free(windowData->SwapChainTextureContainers[idx].ActiveTexture->Subresources);
+ Free(windowData->SwapChainTextureContainers[idx].ActiveTexture);
+ Free(windowData->SwapChainTextureContainers[idx].Textures);
+ }
+
+ IDXGISwapChain_Release(windowData->SwapChain);
+ windowData->SwapChain = nullptr;
+ }
} // namespace Juliet::D3D12
diff --git a/Juliet/src/Graphics/D3D12/DX12SwapChain.h b/Juliet/src/Graphics/D3D12/DX12SwapChain.h
index 34b6e0e..478ead4 100644
--- a/Juliet/src/Graphics/D3D12/DX12SwapChain.h
+++ b/Juliet/src/Graphics/D3D12/DX12SwapChain.h
@@ -6,6 +6,7 @@ namespace Juliet::D3D12
struct D3D12Driver;
struct D3D12WindowData;
- extern bool CreateSwapChain(NonNullPtr driver, NonNullPtr windowData);
+ extern bool CreateSwapChain(NonNullPtr driver, NonNullPtr windowData,
+ SwapChainComposition composition, PresentMode presentMode);
extern void DestroySwapChain(NonNullPtr driver, NonNullPtr windowData);
-}
+} // namespace Juliet::D3D12
diff --git a/Juliet/src/Graphics/Graphics.cpp b/Juliet/src/Graphics/Graphics.cpp
index 1650f99..91b083e 100644
--- a/Juliet/src/Graphics/Graphics.cpp
+++ b/Juliet/src/Graphics/Graphics.cpp
@@ -7,8 +7,6 @@ namespace Juliet
{
namespace
{
- GraphicsDevice* CurrentGraphicsDevice = nullptr;
-
// TODO : IfDef new factories that are not compatible
// Low chance of porting on something else than windows though. May be linux but will use vulkan that works on windows
constexpr GraphicsDeviceFactory* Factories[] = { &DX12DeviceFactory };
diff --git a/Juliet/src/Graphics/GraphicsDevice.h b/Juliet/src/Graphics/GraphicsDevice.h
index e68fb25..7339b2b 100644
--- a/Juliet/src/Graphics/GraphicsDevice.h
+++ b/Juliet/src/Graphics/GraphicsDevice.h
@@ -3,16 +3,23 @@
#include
#include
#include
+#include
namespace Juliet
{
struct Window;
+ struct TextureHeader
+ {
+ TextureCreateInfo CreateInfo;
+ };
+
struct GPUDriver
{
uint8 CommandListCount;
static constexpr uint8 kResourceBufferCount = 2;
+ static constexpr uint8 kMaxFramesInFlight = 3;
};
struct GraphicsDevice