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