Swapchain creation and destruction

This commit is contained in:
2025-02-15 21:49:10 -05:00
parent d90a0bdf83
commit 3e7caa2c7c
17 changed files with 727 additions and 17 deletions

View File

@@ -136,6 +136,7 @@
<ClInclude Include="include\Engine\Engine.h"/>
<ClInclude Include="include\Graphics\Graphics.h"/>
<ClInclude Include="include\Graphics\GraphicsConfig.h"/>
<ClInclude Include="include\Graphics\Texture.h" />
<ClInclude Include="include\Juliet.h"/>
<ClInclude Include="include\pch.h"/>
<ClInclude Include="src\Core\HAL\Display\DisplayDevice.h"/>
@@ -150,6 +151,8 @@
<ClInclude Include="src\Core\HAL\Event\WindowEvent.h"/>
<ClInclude Include="src\Core\Networking\SocketPlatformImpl.h"/>
<ClInclude Include="src\Core\HAL\Win32.h"/>
<ClInclude Include="src\Graphics\D3D12\D3D12Common.h" />
<ClInclude Include="src\Graphics\D3D12\D3D12Texture.h" />
<ClInclude Include="src\Graphics\D3D12\DX12CommandList.h"/>
<ClInclude Include="src\Graphics\D3D12\DX12GraphicsDevice.h"/>
<ClInclude Include="src\Graphics\D3D12\DX12Includes.h"/>
@@ -232,6 +235,8 @@
<ClCompile Include="src\Core\Networking\TcpSocket.cpp"/>
<ClCompile Include="src\Core\Networking\Win32\Win32SocketPlatformImpl.cpp"/>
<ClCompile Include="src\Engine\Engine.cpp"/>
<ClCompile Include="src\Graphics\D3D12\D3D12Common.cpp" />
<ClCompile Include="src\Graphics\D3D12\D3D12Texture.cpp" />
<ClCompile Include="src\Graphics\D3D12\DX12CommandList.cpp"/>
<ClCompile Include="src\Graphics\D3D12\DX12GraphicsDevice.cpp"/>
<ClCompile Include="src\Graphics\D3D12\DX12SwapChain.cpp"/>

View File

@@ -33,4 +33,6 @@ namespace Juliet
*Byte++ = 0;
}
}
#define MemCopy memcpy
} // namespace Juliet

View File

@@ -105,4 +105,7 @@ namespace Juliet
private:
Type* InternalPtr;
};
template <typename T>
NonNullPtr(T) -> NonNullPtr<T>;
} // namespace Juliet

View File

@@ -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);

View File

@@ -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;

View File

@@ -0,0 +1,180 @@
#pragma once
#include <D3D12Common.h>
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

View File

@@ -1,3 +1,4 @@
#include <D3D12Texture.h>
#include <pch.h>
#include <core/Memory/Allocator.h>

View File

@@ -0,0 +1,168 @@
#include <pch.h>
#include <Core/Memory/Allocator.h>
#include <Graphics/D3D12/D3D12Common.h>
#include <Graphics/D3D12/DX12GraphicsDevice.h>
#include <Graphics/D3D12/DX12Includes.h>
#include <Graphics/D3D12/DX12Utils.h>
namespace Juliet::D3D12
{
namespace
{
constexpr size_t kStagingHeapDescriptorExpectedCount = 1024;
void InitStagingDescriptorPool(NonNullPtr<D3D12DescriptorHeap> heap, NonNullPtr<D3D12StagingDescriptorPool> 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<D3D12Driver> driver, D3D12StagingDescriptorPool& pool)
{
D3D12DescriptorHeap* heap =
CreateDescriptorHeap(driver, pool.Heaps[0]->HeapType, kStagingHeapDescriptorExpectedCount, true);
if (!heap)
{
return false;
}
pool.HeapCount += 1;
pool.Heaps = static_cast<D3D12DescriptorHeap**>(Realloc(pool.Heaps, pool.HeapCount * sizeof(D3D12DescriptorHeap*)));
pool.Heaps[pool.HeapCount - 1] = heap;
pool.FreeDescriptorCapacity += kStagingHeapDescriptorExpectedCount;
pool.FreeDescriptorCount += kStagingHeapDescriptorExpectedCount;
pool.FreeDescriptors = static_cast<D3D12StagingDescriptor*>(
Realloc(pool.FreeDescriptors, pool.FreeDescriptorCapacity * sizeof(D3D12StagingDescriptor)));
InitStagingDescriptorPool(heap, &pool);
return true;
}
} // namespace
D3D12DescriptorHeap* CreateDescriptorHeap(NonNullPtr<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 count, bool isStaging)
{
ID3D12DescriptorHeap* handle;
auto heap = static_cast<D3D12DescriptorHeap*>(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<D3D12DescriptorHeap> heap)
{
if (heap->Handle)
{
ID3D12DescriptorHeap_Release(heap->Handle);
}
Free(heap.Get());
}
D3D12StagingDescriptorPool* CreateStagingDescriptorPool(NonNullPtr<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type)
{
D3D12DescriptorHeap* heap = CreateDescriptorHeap(driver, type, kStagingHeapDescriptorExpectedCount, true);
if (!heap)
{
return nullptr;
}
auto pool = static_cast<D3D12StagingDescriptorPool*>(Calloc(1, sizeof(D3D12StagingDescriptorPool)));
// First create the heaps
pool->HeapCount = 1;
pool->Heaps = static_cast<D3D12DescriptorHeap**>(Malloc(sizeof(D3D12DescriptorHeap*)));
pool->Heaps[0] = heap;
pool->FreeDescriptorCapacity = kStagingHeapDescriptorExpectedCount;
pool->FreeDescriptorCount = kStagingHeapDescriptorExpectedCount;
pool->FreeDescriptors =
static_cast<D3D12StagingDescriptor*>(Malloc(kStagingHeapDescriptorExpectedCount * sizeof(D3D12StagingDescriptor)));
InitStagingDescriptorPool(heap, pool);
return pool;
}
bool AssignStagingDescriptor(NonNullPtr<D3D12Driver> 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<D3D12Driver> driver, D3D12StagingDescriptor& cpuDescriptor)
{
D3D12StagingDescriptorPool* pool = cpuDescriptor.Pool;
if (pool != nullptr)
{
MemCopy(&pool->FreeDescriptors[pool->FreeDescriptorCount], &cpuDescriptor, sizeof(D3D12StagingDescriptor));
pool->FreeDescriptorCount += 1;
}
}
void DestroyStagingDescriptorPool(NonNullPtr<D3D12StagingDescriptorPool> 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

View File

@@ -0,0 +1,63 @@
#pragma once
#include <Core/Common/NonNullPtr.h>
#include <Core/Thread/Mutex.h>
#include <Graphics/D3D12/DX12Includes.h>
// 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<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type,
uint32 count, bool isStaging);
extern void DestroyDescriptorHeap(NonNullPtr<D3D12DescriptorHeap> heap);
extern D3D12StagingDescriptorPool* CreateStagingDescriptorPool(NonNullPtr<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type);
extern bool AssignStagingDescriptor(NonNullPtr<D3D12Driver> driver, D3D12_DESCRIPTOR_HEAP_TYPE type,
D3D12StagingDescriptor& outDescriptor);
extern void ReleaseStagingDescriptor(NonNullPtr<D3D12Driver> driver, D3D12StagingDescriptor& cpuDescriptor);
extern void DestroyStagingDescriptorPool(NonNullPtr<D3D12StagingDescriptorPool> pool);
} // namespace Juliet::D3D12

View File

@@ -0,0 +1,8 @@
#include <pch.h>
#include <Graphics/D3D12/D3D12Texture.h>
namespace Juliet::D3D12
{
}

View File

@@ -0,0 +1,63 @@
#pragma once
#include <Graphics/D3D12/D3D12Common.h>
#include <Graphics/D3D12/DX12Includes.h>
#include <Graphics/GraphicsDevice.h>
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

View File

@@ -167,6 +167,15 @@ namespace Juliet::D3D12
void DestroyDriver_Internal(NonNullPtr<D3D12Driver> 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<D3D12_DESCRIPTOR_HEAP_TYPE>(i));
if (driver->StagingDescriptorPools[i] == nullptr)
{
DestroyDriver_Internal(driver);
return NULL;
}
}
// Assign Functions to the device
device->DestroyDevice = DestroyGraphicsDevice;

View File

@@ -1,7 +1,8 @@
#pragma once
#include <Core/Common/EnumUtils.h>
#include <Graphics/D3D12/DX12Utils.h>
#include <Graphics/D3D12/D3D12Common.h>
#include <Graphics/D3D12/D3D12Texture.h>
#include <Graphics/GraphicsDevice.h>
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;

View File

@@ -1,14 +1,140 @@
#include <pch.h>
#include <Core/HAL/Display/Win32/Win32Window.h>
#include <Core/Memory/Allocator.h>
#include <Graphics/D3D12/D3D12Texture.h>
#include <Graphics/D3D12/DX12GraphicsDevice.h>
#include <Graphics/D3D12/DX12Includes.h>
#include <Graphics/D3D12/DX12SwapChain.h>
#include <Graphics/D3D12/DX12Utils.h>
#include <algorithm>
namespace Juliet::D3D12
{
bool CreateSwapChain(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12WindowData> 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<D3D12Driver> driver, NonNullPtr<IDXGISwapChain3> swapChain,
SwapChainComposition composition, NonNullPtr<D3D12TextureContainer> textureContainer, uint8 index)
{
ID3D12Resource* swapChainTexture = nullptr;
HRESULT result =
IDXGISwapChain_GetBuffer(swapChain, index, IID_ID3D12Resource, reinterpret_cast<void**>(&swapChainTexture));
if (FAILED(result))
{
LogError(driver, "Cannot get buffer from SwapChain", result);
return false;
}
auto texture = static_cast<D3D12Texture*>(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<D3D12TextureSubresource*>(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<D3D12StagingDescriptor*>(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<uint32>(textureDesc.Width);
textureContainer->Header.CreateInfo.Height = static_cast<uint32>(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<D3D12Texture**>(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<D3D12Driver> driver, NonNullPtr<D3D12WindowData> windowData,
SwapChainComposition composition, PresentMode presentMode)
{
auto windowWin32State = static_cast<Win32::Window32State*>(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<uint8>(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<void**>(&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<D3D12Driver> driver, NonNullPtr<D3D12WindowData> windowData) {}
void DestroySwapChain(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12WindowData> 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

View File

@@ -6,6 +6,7 @@ namespace Juliet::D3D12
struct D3D12Driver;
struct D3D12WindowData;
extern bool CreateSwapChain(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12WindowData> windowData);
extern bool CreateSwapChain(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12WindowData> windowData,
SwapChainComposition composition, PresentMode presentMode);
extern void DestroySwapChain(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12WindowData> windowData);
}
} // namespace Juliet::D3D12

View File

@@ -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 };

View File

@@ -3,16 +3,23 @@
#include <Core/Common/NonNullPtr.h>
#include <Graphics/Graphics.h>
#include <Graphics/GraphicsConfig.h>
#include <Graphics/Texture.h>
namespace Juliet
{
struct Window;
struct TextureHeader
{
TextureCreateInfo CreateInfo;
};
struct GPUDriver
{
uint8 CommandListCount;
static constexpr uint8 kResourceBufferCount = 2;
static constexpr uint8 kMaxFramesInFlight = 3;
};
struct GraphicsDevice