diff --git a/Juliet/Juliet.vcxproj b/Juliet/Juliet.vcxproj index 7a94569..6e4dc40 100644 --- a/Juliet/Juliet.vcxproj +++ b/Juliet/Juliet.vcxproj @@ -134,9 +134,11 @@ + - + + @@ -151,8 +153,10 @@ - - + + + + @@ -235,12 +239,14 @@ - - + + + + - + MultiThreadedDebugDll Disabled @@ -302,7 +308,7 @@ - + diff --git a/Juliet/include/Core/Common/CoreTypes.h b/Juliet/include/Core/Common/CoreTypes.h index e4a9e59..85d34de 100644 --- a/Juliet/include/Core/Common/CoreTypes.h +++ b/Juliet/include/Core/Common/CoreTypes.h @@ -2,6 +2,7 @@ #include #include +#include using uint8 = uint8_t; using uint16 = uint16_t; @@ -24,3 +25,14 @@ struct ByteBuffer }; using FunctionPtr = auto (*)(void) -> void; + +// Limits +template +consteval Type MaxValueOf() +{ + return std::numeric_limits::max(); +} +constexpr uint32 uint8Max = MaxValueOf(); +constexpr uint32 uint16Max = MaxValueOf(); +constexpr uint32 uint32Max = MaxValueOf(); +constexpr uint32 uint64Max = MaxValueOf(); diff --git a/Juliet/include/Core/Common/CoreUtils.h b/Juliet/include/Core/Common/CoreUtils.h index 8d24753..14f0de0 100644 --- a/Juliet/include/Core/Common/CoreUtils.h +++ b/Juliet/include/Core/Common/CoreUtils.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -35,4 +36,30 @@ namespace Juliet } #define MemCopy memcpy + + template + constexpr Type Min(Type lhs, Type rhs) + { + return rhs < lhs ? rhs : lhs; + } + + template + constexpr Type Max(Type lhs, Type rhs) + { + return lhs < rhs ? rhs : lhs; + } + + template + constexpr Type Clamp(Type val, Type min, Type max) + { + if (val < min) + { + return min; + } + if (val > max) + { + return max; + } + return val; + } } // namespace Juliet diff --git a/Juliet/include/Graphics/Colors.h b/Juliet/include/Graphics/Colors.h new file mode 100644 index 0000000..eae255b --- /dev/null +++ b/Juliet/include/Graphics/Colors.h @@ -0,0 +1,16 @@ +#pragma once + +namespace Juliet +{ + template + struct ColorType + { + Type R; + Type G; + Type B; + Type A; + }; + + using FColor = ColorType; + using Color = ColorType; +} // namespace Juliet diff --git a/Juliet/include/Graphics/Graphics.h b/Juliet/include/Graphics/Graphics.h index e13bc56..7333222 100644 --- a/Juliet/include/Graphics/Graphics.h +++ b/Juliet/include/Graphics/Graphics.h @@ -1,14 +1,16 @@ #pragma once #include +#include #include -#include +#include #include // Graphics Interface namespace Juliet { - struct Window; + // Opaque types + struct CommandList; struct GraphicsDevice; // Parameters of an indirect draw command @@ -65,9 +67,6 @@ namespace Juliet Immediate }; - // Opaque types - struct CommandList; - extern JULIET_API GraphicsDevice* CreateGraphicsDevice(GraphicsConfig config); extern JULIET_API void DestroyGraphicsDevice(NonNullPtr device); @@ -82,4 +81,9 @@ namespace Juliet // Command List extern JULIET_API CommandList* AcquireCommandList(NonNullPtr device, QueueType queueType = QueueType::Graphics); extern JULIET_API void SubmitCommandLists(NonNullPtr device); + + // RenderPass + extern JULIET_API RenderPass* BeginRenderPass(NonNullPtr commandList, ColorTargetInfo& colorTargetInfo); + extern JULIET_API RenderPass* BeginRenderPass(NonNullPtr commandList, + NonNullPtr colorTargetInfos, uint32 colorTargetInfoCount); } // namespace Juliet diff --git a/Juliet/include/Graphics/RenderPass.h b/Juliet/include/Graphics/RenderPass.h new file mode 100644 index 0000000..fd433bb --- /dev/null +++ b/Juliet/include/Graphics/RenderPass.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +namespace Juliet +{ + enum struct LoadOperation : uint8 + { + Load, // Load the texture from memory (preserve) + Clear, // Clear the texture + Ignore // Ignore the content of the texture (undefined) + }; + + enum struct StoreOperation : uint8 + { + Store, // Store the result of the render pass into memory + Ignore, // Whatever is generated is ignored (undefined) + Resolve, // Resolve MipMaps into non mip map texture. Discard MipMap content + ResolveAndStore // Same but store the MipMap content to memory + }; + + struct ColorTargetInfo + { + Texture* Texture; + uint32 MipLevel; + union + { + uint32 DepthPlane; + uint32 LayerCount; + }; + FColor ClearColor; + LoadOperation LoadOperation; + StoreOperation StoreOperation; + bool Cycle; // Whether the texture should be cycled if already bound (and load operation != LOAD) + }; + + // Opaque Type + struct RenderPass; +} // namespace Juliet diff --git a/Juliet/include/Graphics/Texture.h b/Juliet/include/Graphics/Texture.h index 628e8ae..0eb333e 100644 --- a/Juliet/include/Graphics/Texture.h +++ b/Juliet/include/Graphics/Texture.h @@ -161,9 +161,9 @@ namespace Juliet // Create Information structs struct TextureCreateInfo { - TextureType Type; - TextureFormat Format; - TextureUsageFlag Flags; + TextureType Type; + TextureFormat Format; + TextureUsageFlag Flags; TextureSampleCount SampleCount; uint32 Width; @@ -171,10 +171,11 @@ namespace Juliet union { uint32 LayerCount; - uint32 Depth; + uint32 DepthPlane; }; // LayerCount is used in 2d array textures and Depth for 3d textures uint32 MipLevelCount; }; + // Opaque Type struct Texture; } // namespace Juliet diff --git a/Juliet/src/Core/HAL/Win32.h b/Juliet/src/Core/HAL/Win32.h index 28b3cc4..509429a 100644 --- a/Juliet/src/Core/HAL/Win32.h +++ b/Juliet/src/Core/HAL/Win32.h @@ -58,3 +58,6 @@ #define ANSI_ONLY #include + +#undef min +#undef max diff --git a/Juliet/src/Graphics/D3D12/D3D12Common.h b/Juliet/src/Graphics/D3D12/D3D12Common.h index 7afddca..c04feff 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Common.h +++ b/Juliet/src/Graphics/D3D12/D3D12Common.h @@ -14,8 +14,8 @@ // Inspired (copy pasted a lot) by SDL GPU namespace Juliet::D3D12 { - struct D3D12Driver; // Forward declare + struct D3D12Driver; struct D3D12StagingDescriptor; // https://learn.microsoft.com/en-us/windows/win32/direct3d12/descriptor-heaps diff --git a/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp b/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp new file mode 100644 index 0000000..a091dc9 --- /dev/null +++ b/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp @@ -0,0 +1,57 @@ +#include + +#include +#include + +#include + +namespace Juliet::D3D12 +{ + void BeginRenderPass(NonNullPtr commandList, NonNullPtr colorTargetInfos, uint32 colorTargetInfoCount) + { + auto d3d12CommandList = reinterpret_cast(commandList.Get()); + + uint32 frameBufferWidth = uint32Max; + uint32 frameBufferHeight = uint32Max; + + for (uint32 idx = 0; idx < colorTargetInfoCount; ++idx) + { + auto* container = reinterpret_cast(colorTargetInfos[idx].Texture); + uint32 width = container->Header.CreateInfo.Width >> colorTargetInfos[idx].MipLevel; + uint32 height = container->Header.CreateInfo.Height >> colorTargetInfos[idx].MipLevel; + + // Scale the framebuffer to fit the smallest target. + frameBufferWidth = Min(width, frameBufferWidth); + frameBufferHeight = Min(height, frameBufferHeight); + } + + // TODO : Depth Stencil and DSV + + D3D12_CPU_DESCRIPTOR_HANDLE RTVs[GPUDriver::kMaxColorTargetInfo]; + for (uint32 idx = 0; idx < colorTargetInfoCount; ++idx) + { + auto* container = reinterpret_cast(colorTargetInfos[idx].Texture); + D3D12TextureSubresource* subresource = PrepareTextureSubresourceForWrite( + d3d12CommandList, container, + container->Header.CreateInfo.Type == TextureType::Texture_3D ? 0 : colorTargetInfos[idx].LayerCount, + colorTargetInfos[idx].MipLevel, colorTargetInfos[idx].Cycle, D3D12_RESOURCE_STATE_RENDER_TARGET); + + uint32 RTVIndex = container->Header.CreateInfo.Type == TextureType::Texture_3D ? colorTargetInfos[idx].DepthPlane : 0; + D3D12_CPU_DESCRIPTOR_HANDLE rtv = subresource->RTVHandles[RTVIndex].CpuHandle; + + if (colorTargetInfos[idx].LoadOperation == LoadOperation::Clear) + { + float clearColor[4]; + clearColor[0] = colorTargetInfos[idx].ClearColor.R; + clearColor[1] = colorTargetInfos[idx].ClearColor.G; + clearColor[2] = colorTargetInfos[idx].ClearColor.B; + clearColor[3] = colorTargetInfos[idx].ClearColor.A; + + ID3D12GraphicsCommandList6_ClearRenderTargetView(d3d12CommandList->GraphicsCommandList.CommandList, rtv, clearColor, 0, nullptr); + } + + RTVs[idx] = rtv; + // d3d12CommandList->ColorTargetSubresources[idx] = subresource; + } + } +} // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/D3D12RenderPass.h b/Juliet/src/Graphics/D3D12/D3D12RenderPass.h new file mode 100644 index 0000000..97784db --- /dev/null +++ b/Juliet/src/Graphics/D3D12/D3D12RenderPass.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include +#include + +namespace Juliet::D3D12 +{ + extern void BeginRenderPass(NonNullPtr commandList, NonNullPtr colorTargetInfos, + uint32 colorTargetInfoCount); +} diff --git a/Juliet/src/Graphics/D3D12/D3D12Synchronization.cpp b/Juliet/src/Graphics/D3D12/D3D12Synchronization.cpp new file mode 100644 index 0000000..b8778a5 --- /dev/null +++ b/Juliet/src/Graphics/D3D12/D3D12Synchronization.cpp @@ -0,0 +1,41 @@ +#include + +#include +#include + +namespace Juliet::D3D12 +{ + void ResourceBarrier(NonNullPtr commandList, D3D12_RESOURCE_STATES sourceState, + D3D12_RESOURCE_STATES destinationState, ID3D12Resource* resource, uint32 subresourceIndex, bool needsUavBarrier) + { + D3D12_RESOURCE_BARRIER barrierDesc[2]; + uint32 numBarriers = 0; + + // No transition barrier is needed if the state is not changing. + if (sourceState != destinationState) + { + barrierDesc[numBarriers].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrierDesc[numBarriers].Flags = static_cast(0); + barrierDesc[numBarriers].Transition.StateBefore = sourceState; + barrierDesc[numBarriers].Transition.StateAfter = destinationState; + barrierDesc[numBarriers].Transition.pResource = resource; + barrierDesc[numBarriers].Transition.Subresource = subresourceIndex; + + numBarriers += 1; + } + + if (needsUavBarrier) + { + barrierDesc[numBarriers].Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + barrierDesc[numBarriers].Flags = static_cast(0); + barrierDesc[numBarriers].UAV.pResource = resource; + + numBarriers += 1; + } + + if (numBarriers > 0) + { + ID3D12GraphicsCommandList_ResourceBarrier(commandList->GraphicsCommandList.CommandList, numBarriers, barrierDesc); + } + } +} // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/D3D12Synchronization.h b/Juliet/src/Graphics/D3D12/D3D12Synchronization.h new file mode 100644 index 0000000..c10b56d --- /dev/null +++ b/Juliet/src/Graphics/D3D12/D3D12Synchronization.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace Juliet::D3D12 +{ + // Forward Declare + struct D3D12CommandList; + + extern void ResourceBarrier(NonNullPtr commandList, D3D12_RESOURCE_STATES sourceState, D3D12_RESOURCE_STATES destinationState, + ID3D12Resource* resource, uint32 subresourceIndex, bool needsUavBarrier); +} + diff --git a/Juliet/src/Graphics/D3D12/D3D12Texture.cpp b/Juliet/src/Graphics/D3D12/D3D12Texture.cpp index 7400dfc..c3ef383 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Texture.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12Texture.cpp @@ -1,8 +1,102 @@ #include +#include #include +#include namespace Juliet::D3D12 { + namespace + { + uint32 ComputeSubresourceIndex(uint32 mipLevel, uint32 layer, uint32 numLevels) + { + return mipLevel + (layer * numLevels); + } -} \ No newline at end of file + D3D12_RESOURCE_STATES GetDefaultTextureResourceState(TextureUsageFlag usageFlags) + { + if ((usageFlags & TextureUsageFlag::Sampler) != TextureUsageFlag::None) + { + return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE; + } + if ((usageFlags & TextureUsageFlag::GraphicsStorageRead) != TextureUsageFlag::None) + { + return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE; + } + if ((usageFlags & TextureUsageFlag::ColorTarget) != TextureUsageFlag::None) + { + return D3D12_RESOURCE_STATE_RENDER_TARGET; + } + if ((usageFlags & TextureUsageFlag::DepthStencilTarget) != TextureUsageFlag::None) + { + return D3D12_RESOURCE_STATE_DEPTH_WRITE; + } + if ((usageFlags & TextureUsageFlag::ComputeStorageRead) != TextureUsageFlag::None) + { + return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + } + if ((usageFlags & TextureUsageFlag::ComputeStorageWrite) != TextureUsageFlag::None) + { + return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + } + if ((usageFlags & TextureUsageFlag::ComputeStorageSimultaneousReadWrite) != TextureUsageFlag::None) + { + return D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + } + Log(LogLevel::Error, LogCategory::Graphics, "Texture has no default usage mode!"); + return D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE; + } + + void SetTextureSubresourceTransitionFromDefault(NonNullPtr commandList, + NonNullPtr subresource, + D3D12_RESOURCE_STATES newTextureUsage) + { + D3D12_RESOURCE_STATES defaultUsage = + GetDefaultTextureResourceState(subresource->Parent->Container->Header.CreateInfo.Flags); + TextureSubresourceBarrier(commandList, defaultUsage, newTextureUsage, subresource); + } + + void SetTextureTransitionFromDefault(NonNullPtr commandList, NonNullPtr texture, + D3D12_RESOURCE_STATES newTextureUsage) + { + for (uint32 i = 0; i < texture->SubresourceCount; ++i) + { + SetTextureSubresourceTransitionFromDefault(commandList, &texture->Subresources[i], newTextureUsage); + } + } + } // namespace + + D3D12TextureSubresource* PrepareTextureSubresourceForWrite(NonNullPtr commandList, + NonNullPtr container, uint32 layer, + uint32 level, bool shouldCycle, D3D12_RESOURCE_STATES newTextureUsage) + { + D3D12TextureSubresource* subresource = FetchTextureSubresource(container, layer, level); + if (shouldCycle and container->CanBeCycled and subresource->Parent->RefCount > 0) + { + // TODO: Cycle the active texture to an available one. Not needed for swap chain (current objective) + // CycleActiveTexture(commandList->Driver, container); + + subresource = FetchTextureSubresource(container, layer, level); + } + + SetTextureSubresourceTransitionFromDefault(commandList, subresource, newTextureUsage); + + return subresource; + } + + D3D12TextureSubresource* FetchTextureSubresource(NonNullPtr container, uint32 layer, uint32 level) + { + uint32 index = ComputeSubresourceIndex(level, layer, container->Header.CreateInfo.MipLevelCount); + return &container->ActiveTexture->Subresources[index]; + } + + void TextureSubresourceBarrier(NonNullPtr commandList, D3D12_RESOURCE_STATES sourceState, + D3D12_RESOURCE_STATES destinationState, NonNullPtr textureSubresource) + { + TextureUsageFlag currentFlag = textureSubresource->Parent->Container->Header.CreateInfo.Flags; + bool needsUAVBarrier = ((currentFlag & TextureUsageFlag::ComputeStorageWrite) != TextureUsageFlag::None) || + ((currentFlag & TextureUsageFlag::ComputeStorageSimultaneousReadWrite) != TextureUsageFlag::None); + ResourceBarrier(commandList, sourceState, destinationState, textureSubresource->Parent->Resource, + textureSubresource->Index, needsUAVBarrier); + } +} // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/D3D12Texture.h b/Juliet/src/Graphics/D3D12/D3D12Texture.h index d94cf47..66435fa 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Texture.h +++ b/Juliet/src/Graphics/D3D12/D3D12Texture.h @@ -8,9 +8,10 @@ struct ID3D12Resource; namespace Juliet::D3D12 { + // Forward Declare struct D3D12Texture; - struct D3D12ResourceHeap; + struct D3D12CommandList; struct D3D12TextureContainer { @@ -30,7 +31,7 @@ namespace Juliet::D3D12 }; // D3D12 subresourcces: https://learn.microsoft.com/en-us/windows/win32/direct3d12/subresources (mipmaps, etc..) - using D3D12TextureSubresource = struct D3D12TextureSubresource + struct D3D12TextureSubresource { D3D12Texture* Parent; uint32 Layer; @@ -60,4 +61,13 @@ namespace Juliet::D3D12 // TODO: Should be atomic to support multithreading int32 RefCount; }; + + extern D3D12TextureSubresource* PrepareTextureSubresourceForWrite(NonNullPtr, + NonNullPtr container, + uint32 layer, uint32 level, bool shouldCycle, + D3D12_RESOURCE_STATES newTextureUsage); + extern D3D12TextureSubresource* FetchTextureSubresource(NonNullPtr container, uint32 layer, uint32 level); + extern void TextureSubresourceBarrier(NonNullPtr commandList, D3D12_RESOURCE_STATES sourceState, + D3D12_RESOURCE_STATES destinationState, + NonNullPtr textureSubresource); } // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/DX12CommandList.cpp b/Juliet/src/Graphics/D3D12/DX12CommandList.cpp index 1c725c8..2e6ab14 100644 --- a/Juliet/src/Graphics/D3D12/DX12CommandList.cpp +++ b/Juliet/src/Graphics/D3D12/DX12CommandList.cpp @@ -3,67 +3,105 @@ #include #include #include - +#define IID_PPV_ARGS(ppType) __uuidof(decltype(**(ppType))), reinterpret_cast(ppType) +#define PPV_ARGS(x) IID_PPV_ARGS(&x) namespace Juliet::D3D12 { namespace { bool HasD3D12CommandList(NonNullPtr commandList, QueueType queueType) { - return commandList->CommandLists[ToUnderlying(queueType)] != nullptr; + switch (queueType) + { + case QueueType::Graphics: return commandList->GraphicsCommandList.CommandList != nullptr; + case QueueType::Compute: return commandList->ComputeCommandList.CommandList != nullptr; + case QueueType::Copy: return commandList->CopyCommandList.CommandList != nullptr; + default: return false; + } } - bool CreateD3D12CommandList(NonNullPtr driver, NonNullPtr commandList, QueueType queueType) + bool CreateAllocator(NonNullPtr driver, NonNullPtr baseData, + D3D12_COMMAND_QUEUE_DESC queueDesc) { - HRESULT res = 0; - auto& queueDesc = driver->QueueDesc[ToUnderlying(queueType)]; - for (auto& buffer : commandList->CommandAllocator) + for (auto& buffer : baseData->Allocator) { - res = ID3D12Device5_CreateCommandAllocator(driver->D3D12Device, queueDesc.Type, IID_ID3D12CommandAllocator, - reinterpret_cast(&buffer[ToUnderlying(queueType)])); - - if (FAILED(res)) + HRESULT result = ID3D12Device5_CreateCommandAllocator(driver->D3D12Device, queueDesc.Type, IID_ID3D12CommandAllocator, + reinterpret_cast(&buffer)); + if (FAILED(result)) { Assert(false && "Error not implemented: cannot create ID3D12CommandAllocator"); return false; } } + return true; + } - auto& commandListSlot = commandList->CommandLists[ToUnderlying(queueType)]; - if (queueType == QueueType::Graphics || queueType == QueueType::Compute) - { - ID3D12GraphicsCommandList6* d3d12GraphicsCommandList = nullptr; - res = ID3D12Device5_CreateCommandList1(driver->D3D12Device, queueDesc.NodeMask, queueDesc.Type, - D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, - reinterpret_cast(&d3d12GraphicsCommandList)); - if (FAILED(res)) - { - Assert(false && "Error not implemented: cannot create ID3D12GraphicsCommandList6 (graphics or " - "compute command list"); - return false; - } - commandListSlot = reinterpret_cast(d3d12GraphicsCommandList); - } - else if (queueType == QueueType::Copy) - { - ID3D12GraphicsCommandList* d3d12CopyCommandList = nullptr; - res = ID3D12Device5_CreateCommandList1(driver->D3D12Device, queueDesc.NodeMask, queueDesc.Type, - D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, - reinterpret_cast(&d3d12CopyCommandList)); - if (FAILED(res)) - { - Assert(false && - "Error not implemented: cannot create ID3D12GraphicsCommandList (copy command list)"); - return false; - } - commandListSlot = reinterpret_cast(d3d12CopyCommandList); - } - + bool CreateD3D12CommandList(NonNullPtr driver, NonNullPtr commandList, QueueType queueType) + { // TODO: String library std::wstring wide_str = L"CommandList ID:" + std::to_wstring(commandList->ID); - ID3D12GraphicsCommandList_SetName(commandListSlot, wide_str.c_str()); - return true; + // TODO: Factorize this. Flemme + auto& queueDesc = driver->QueueDesc[ToUnderlying(queueType)]; + switch (queueType) + { + case QueueType::Graphics: + { + CreateAllocator(driver, &commandList->GraphicsCommandList, queueDesc); + ID3D12GraphicsCommandList6* d3d12GraphicsCommandList = nullptr; + HRESULT result = + ID3D12Device5_CreateCommandList1(driver->D3D12Device, queueDesc.NodeMask, queueDesc.Type, + D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, + reinterpret_cast(&d3d12GraphicsCommandList)); + if (FAILED(result)) + { + Assert(false && "Error not implemented: cannot create ID3D12GraphicsCommandList6 (graphics or " + "compute command list"); + return false; + } + + commandList->GraphicsCommandList.CommandList = d3d12GraphicsCommandList; + ID3D12GraphicsCommandList6_SetName(d3d12GraphicsCommandList, wide_str.c_str()); + return true; + } + case QueueType::Compute: + { + CreateAllocator(driver, &commandList->ComputeCommandList, queueDesc); + ID3D12GraphicsCommandList6* d3d12GraphicsCommandList = nullptr; + HRESULT result = + ID3D12Device5_CreateCommandList1(driver->D3D12Device, queueDesc.NodeMask, queueDesc.Type, + D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList6, + reinterpret_cast(&d3d12GraphicsCommandList)); + if (FAILED(result)) + { + Assert(false && "Error not implemented: cannot create ID3D12GraphicsCommandList6 (graphics or " + "compute command list"); + return false; + } + + commandList->ComputeCommandList.CommandList = d3d12GraphicsCommandList; + ID3D12GraphicsCommandList6_SetName(d3d12GraphicsCommandList, wide_str.c_str()); + return true; + } + case QueueType::Copy: + { + CreateAllocator(driver, &commandList->CopyCommandList, queueDesc); + ID3D12GraphicsCommandList* d3d12CopyCommandList = nullptr; + HRESULT result = ID3D12Device5_CreateCommandList1(driver->D3D12Device, queueDesc.NodeMask, queueDesc.Type, + D3D12_COMMAND_LIST_FLAG_NONE, IID_ID3D12GraphicsCommandList, + reinterpret_cast(&d3d12CopyCommandList)); + if (FAILED(result)) + { + Assert(false && + "Error not implemented: cannot create ID3D12GraphicsCommandList (copy command list)"); + return false; + } + commandList->CopyCommandList.CommandList = d3d12CopyCommandList; + ID3D12GraphicsCommandList_SetName(d3d12CopyCommandList, wide_str.c_str()); + return true; + } + default: return false; + } } bool AllocateCommandList(NonNullPtr driver, QueueType queueType) @@ -102,16 +140,11 @@ namespace Juliet::D3D12 } } // namespace - ID3D12GraphicsCommandList6* GetGraphicsCommandList(NonNullPtr commandList) - { - return reinterpret_cast(commandList->CommandLists[ToUnderlying(QueueType::Graphics)]); - } - CommandList* AcquireCommandList(NonNullPtr driver, QueueType queueType) { auto* d3d12Driver = static_cast(driver.Get()); - uint8 currentCommandListIndex = d3d12Driver->CommandListCount; + uint8 currentCommandListIndex = d3d12Driver->AvailableCommandListCount; if (currentCommandListIndex >= d3d12Driver->AvailableCommandListCapacity) { if (!AllocateCommandList(d3d12Driver, queueType)) @@ -130,6 +163,35 @@ namespace Juliet::D3D12 } } + // Get Proper allocator for the frame and reset both it and the requested queue + uint8 bufferIndex = d3d12Driver->FrameCounter % GPUDriver::kResourceBufferCount; + switch (queueType) + { + case QueueType::Graphics: + { + auto* allocator = commandList->GraphicsCommandList.Allocator[bufferIndex]; + ID3D12CommandAllocator_Reset(allocator); + ID3D12GraphicsCommandList6_Reset(commandList->GraphicsCommandList.CommandList, allocator, nullptr); + break; + } + case QueueType::Compute: + { + auto* allocator = commandList->ComputeCommandList.Allocator[bufferIndex]; + ID3D12CommandAllocator_Reset(allocator); + ID3D12GraphicsCommandList6_Reset(commandList->ComputeCommandList.CommandList, allocator, nullptr); + break; + } + case QueueType::Copy: + { + auto* allocator = commandList->CopyCommandList.Allocator[bufferIndex]; + ID3D12CommandAllocator_Reset(allocator); + ID3D12GraphicsCommandList6_Reset(commandList->CopyCommandList.CommandList, allocator, nullptr); + break; + } + default: Assert(false && "Unsupported QueueType"); return nullptr; + } + + d3d12Driver->AvailableCommandListCount += 1; return reinterpret_cast(commandList); } @@ -138,7 +200,7 @@ namespace Juliet::D3D12 auto* d3d12Driver = static_cast(driver.Get()); bool success = true; - uint8 commandLastIndex = d3d12Driver->CommandListCount; + uint8 commandLastIndex = d3d12Driver->AvailableCommandListCount; // TODO : Get window data from the command list: We associate the swapchain texture to a window with a missing function HRESULT result = IDXGISwapChain_Present(d3d12Driver->WindowData->SwapChain, 0, 1); @@ -147,6 +209,9 @@ namespace Juliet::D3D12 success = false; } + d3d12Driver->AvailableCommandListCount = 0; + ++d3d12Driver->FrameCounter; + return success; } diff --git a/Juliet/src/Graphics/D3D12/DX12CommandList.h b/Juliet/src/Graphics/D3D12/DX12CommandList.h index f794c00..4ae90ef 100644 --- a/Juliet/src/Graphics/D3D12/DX12CommandList.h +++ b/Juliet/src/Graphics/D3D12/DX12CommandList.h @@ -7,13 +7,29 @@ namespace Juliet::D3D12 { + // Forward Declare struct D3D12Driver; struct D3D12WindowData; + struct D3D12CommandListBaseData + { + ID3D12CommandAllocator* Allocator[GPUDriver::kResourceBufferCount]; + }; + + struct D3D12CopyCommandListData : D3D12CommandListBaseData + { + ID3D12GraphicsCommandList* CommandList; + }; + + struct D3D12GraphicsCommandListData : D3D12CommandListBaseData + { + ID3D12GraphicsCommandList6* CommandList; + }; + struct D3D12PresentData { D3D12WindowData* WindowData; - uint32 SwapChainImageIndex; + uint32 SwapChainImageIndex; }; struct D3D12CommandList @@ -24,16 +40,14 @@ namespace Juliet::D3D12 D3D12Driver* Driver; D3D12PresentData* PresentDatas; - uint32 PresentDataCapacity; - uint32 PresentDataCount; + uint32 PresentDataCapacity; + uint32 PresentDataCount; - // We create kResourceBufferCount allocator per queue to allow reusing the command list every N frames - ID3D12CommandAllocator* CommandAllocator[GPUDriver::kResourceBufferCount][ToUnderlying(QueueType::Count)]; - ID3D12CommandList* CommandLists[ToUnderlying(QueueType::Count)]; + D3D12GraphicsCommandListData GraphicsCommandList; + D3D12GraphicsCommandListData ComputeCommandList; + D3D12CopyCommandListData CopyCommandList; }; - ID3D12GraphicsCommandList6* GetGraphicsCommandList(NonNullPtr commandList); - extern CommandList* AcquireCommandList(NonNullPtr driver, QueueType queueType); extern bool SubmitCommandLists(NonNullPtr driver); } // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp index 63bdcf0..eca3fb8 100644 --- a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp +++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -549,6 +550,8 @@ namespace Juliet::D3D12 device->AcquireCommandList = AcquireCommandList; device->SubmitCommandLists = SubmitCommandLists; + device->BeginRenderPass = BeginRenderPass; + device->Driver = driver; driver->GraphicsDevice = device; diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h index d9923bb..8a2b3d2 100644 --- a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h +++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.h @@ -12,6 +12,7 @@ namespace Juliet namespace Juliet::D3D12 { + // Forward Declare struct D3D12CommandList; struct D3D12WindowData @@ -26,7 +27,7 @@ namespace Juliet::D3D12 PresentMode PresentMode; - uint32 FrameCounter; + uint32 WindowFrameCounter; // Specific to that window. See GraphicsDevice for global counter uint32 Width; uint32 Height; }; @@ -63,10 +64,12 @@ namespace Juliet::D3D12 // Resources D3D12CommandList** AvailableCommandLists; uint8 AvailableCommandListCapacity; + uint8 AvailableCommandListCount; D3D12StagingDescriptorPool* StagingDescriptorPools[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES]; uint8 FramesInFlight; + uint64 FrameCounter = 0; // Number of frame since inception bool IsTearingSupported : 1; bool IsUMAAvailable : 1; diff --git a/Juliet/src/Graphics/D3D12/DX12Includes.h b/Juliet/src/Graphics/D3D12/DX12Includes.h index 8902c69..e895801 100644 --- a/Juliet/src/Graphics/D3D12/DX12Includes.h +++ b/Juliet/src/Graphics/D3D12/DX12Includes.h @@ -26,3 +26,6 @@ #ifdef __IDXGIInfoQueue_INTERFACE_DEFINED__ #define IDXGIINFOQUEUE_SUPPORTED #endif + +#undef min +#undef max diff --git a/Juliet/src/Graphics/D3D12/DX12SwapChain.cpp b/Juliet/src/Graphics/D3D12/DX12SwapChain.cpp index 255f604..4172d73 100644 --- a/Juliet/src/Graphics/D3D12/DX12SwapChain.cpp +++ b/Juliet/src/Graphics/D3D12/DX12SwapChain.cpp @@ -162,11 +162,10 @@ namespace Juliet::D3D12 if (d3d12CommandList->PresentDataCount == d3d12CommandList->PresentDataCapacity) { d3d12CommandList->PresentDataCapacity += 1; - d3d12CommandList->PresentDatas = - static_cast(Realloc(d3d12CommandList->PresentDatas, - d3d12CommandList->PresentDataCapacity * sizeof(D3D12PresentData))); + d3d12CommandList->PresentDatas = static_cast( + Realloc(d3d12CommandList->PresentDatas, d3d12CommandList->PresentDataCapacity * sizeof(D3D12PresentData))); } - d3d12CommandList->PresentDatas[d3d12CommandList->PresentDataCount].WindowData = windowData; + d3d12CommandList->PresentDatas[d3d12CommandList->PresentDataCount].WindowData = windowData; d3d12CommandList->PresentDatas[d3d12CommandList->PresentDataCount].SwapChainImageIndex = swapchainIndex; d3d12CommandList->PresentDataCount += 1; @@ -179,7 +178,7 @@ namespace Juliet::D3D12 barrierDesc.Transition.pResource = windowData->SwapChainTextureContainers[swapchainIndex].ActiveTexture->Resource; barrierDesc.Transition.Subresource = 0; - ID3D12GraphicsCommandList_ResourceBarrier(GetGraphicsCommandList(d3d12CommandList), 1, &barrierDesc); + ID3D12GraphicsCommandList_ResourceBarrier(d3d12CommandList->GraphicsCommandList.CommandList, 1, &barrierDesc); *swapchainTexture = reinterpret_cast(&windowData->SwapChainTextureContainers[swapchainIndex]); @@ -281,7 +280,7 @@ namespace Juliet::D3D12 windowData->SwapChain = swapChain3; windowData->SwapChainColorSpace = SwapchainCompositionToColorSpace[ToUnderlying(composition)]; windowData->SwapChainComposition = composition; - windowData->FrameCounter = 0; + windowData->WindowFrameCounter = 0; windowData->Width = swapChainDesc.Width; windowData->Height = swapChainDesc.Height; windowData->PresentMode = presentMode; diff --git a/Juliet/src/Graphics/D3D12/DX12SwapChain.h b/Juliet/src/Graphics/D3D12/DX12SwapChain.h index de08684..b7a7aef 100644 --- a/Juliet/src/Graphics/D3D12/DX12SwapChain.h +++ b/Juliet/src/Graphics/D3D12/DX12SwapChain.h @@ -3,6 +3,7 @@ namespace Juliet::D3D12 { + // Forward Declare struct D3D12Driver; struct D3D12WindowData; diff --git a/Juliet/src/Graphics/D3D12/DX12Utils.cpp b/Juliet/src/Graphics/D3D12/DX12Utils.cpp index b6e4380..b3a9123 100644 --- a/Juliet/src/Graphics/D3D12/DX12Utils.cpp +++ b/Juliet/src/Graphics/D3D12/DX12Utils.cpp @@ -38,7 +38,7 @@ namespace Juliet::D3D12 } // Ensure valid range - dwChars = std::min(dwChars, MAX_ERROR_LEN); + dwChars = Min(dwChars, MAX_ERROR_LEN); // Trim whitespace from tail of message while (dwChars > 0) diff --git a/Juliet/src/Graphics/Graphics.cpp b/Juliet/src/Graphics/Graphics.cpp index ee8bcbc..7ba48c6 100644 --- a/Juliet/src/Graphics/Graphics.cpp +++ b/Juliet/src/Graphics/Graphics.cpp @@ -65,9 +65,7 @@ namespace Juliet bool AttachToWindow(NonNullPtr device, NonNullPtr window) { GPUDriver* driver = device->Driver; - bool result = device->AttachToWindow(driver, window); - - return result; + return device->AttachToWindow(driver, window); } void DetachFromWindow(NonNullPtr device, NonNullPtr window) @@ -93,16 +91,14 @@ namespace Juliet { GPUDriver* driver = device->Driver; CommandList* cmdList = device->AcquireCommandList(driver, queueType); - if (!cmdList) { return nullptr; } - auto header = reinterpret_cast(cmdList); - header->Device = device.Get(); - - driver->CommandListCount += 1; + auto header = reinterpret_cast(cmdList); + header->Device = device.Get(); + header->RenderPass.CommandList = cmdList; return cmdList; } @@ -111,8 +107,27 @@ namespace Juliet { GPUDriver* driver = device->Driver; device->SubmitCommandLists(driver); + } - driver->CommandListCount = 0; + RenderPass* BeginRenderPass(NonNullPtr commandList, ColorTargetInfo& colorTargetInfo) + { + return BeginRenderPass(commandList, &colorTargetInfo, 1); + } + + RenderPass* BeginRenderPass(NonNullPtr commandList, NonNullPtr colorTargetInfos, + uint32 colorTargetInfoCount) + { + if (colorTargetInfoCount > GPUDriver::kMaxColorTargetInfo) + { + Log(LogLevel::Error, LogCategory::Graphics, "BeginRenderPass: ColorTargetInfoCount is > than kMaxColorTargetInfo"); + return nullptr; + } + + auto* header = reinterpret_cast(commandList.Get()); + header->Device->BeginRenderPass(commandList, colorTargetInfos, colorTargetInfoCount); + + header->RenderPass.IsInProgress = true; + return reinterpret_cast(&header->RenderPass); } } // namespace Juliet diff --git a/Juliet/src/Graphics/GraphicsDevice.h b/Juliet/src/Graphics/GraphicsDevice.h index ab04c05..9a5ff3a 100644 --- a/Juliet/src/Graphics/GraphicsDevice.h +++ b/Juliet/src/Graphics/GraphicsDevice.h @@ -7,25 +7,30 @@ namespace Juliet { - struct Window; - struct TextureHeader { TextureCreateInfo CreateInfo; }; + struct GPUPass + { + CommandList* CommandList; + bool IsInProgress : 1; + }; + struct CommandListHeader { GraphicsDevice* Device = nullptr; bool AcquiredSwapChain = false; + + GPUPass RenderPass; }; struct GPUDriver { - uint8 CommandListCount; - static constexpr uint8 kResourceBufferCount = 2; static constexpr uint8 kMaxFramesInFlight = 3; + static constexpr uint8 kMaxColorTargetInfo = 4; }; struct GraphicsDevice @@ -43,6 +48,10 @@ namespace Juliet CommandList* (*AcquireCommandList)(NonNullPtr driver, QueueType queueType); bool (*SubmitCommandLists)(NonNullPtr driver); + // RenderPass + void (*BeginRenderPass)(NonNullPtr commandList, NonNullPtr colorTargetInfos, + uint32 colorTargetInfoCount); + const char* Name = "Unknown"; GPUDriver* Driver = nullptr; }; diff --git a/Juliet/src/TODO.txt b/Juliet/src/TODO.txt index f585166..5f914c3 100644 --- a/Juliet/src/TODO.txt +++ b/Juliet/src/TODO.txt @@ -1 +1,3 @@ +Rename DX12 files to D3D12 + - Create Simple vector class to make the vector stuff a bit more easier than writing Capacity and Count \ No newline at end of file diff --git a/JulietApp/Editor/EditorMain_win32.cpp b/JulietApp/Editor/EditorMain_win32.cpp index e332fd5..be8ac9a 100644 --- a/JulietApp/Editor/EditorMain_win32.cpp +++ b/JulietApp/Editor/EditorMain_win32.cpp @@ -13,6 +13,7 @@ // TODO : Replace with message box from framework + call main and not winmain + subsystem #include #include +#include #include // TODO : Think how to do the draw pipeline. @@ -97,6 +98,17 @@ void Win32EditorApplication::Update() return; } + if (swapChainTexture) + { + ColorTargetInfo colorTargetInfo = {}; + colorTargetInfo.Texture = swapChainTexture; + colorTargetInfo.ClearColor = { .R = .5f, .G = .8f, .B = .0f, .A = 1.f }; + colorTargetInfo.LoadOperation = LoadOperation::Clear; + colorTargetInfo.StoreOperation = StoreOperation::Store; + + BeginRenderPass(cmdList, colorTargetInfo); + } + // Submit Commands SubmitCommandLists(GraphicsDevice); } diff --git a/JulietApp/Editor/EditorMain_win32.h b/JulietApp/Editor/EditorMain_win32.h index 7a8c8a6..444e586 100644 --- a/JulietApp/Editor/EditorMain_win32.h +++ b/JulietApp/Editor/EditorMain_win32.h @@ -21,7 +21,7 @@ class Win32EditorApplication : public Juliet::IApplication private: Juliet::Window* MainWindow = {}; Juliet::GraphicsDevice* GraphicsDevice = {}; - Juliet::DynamicLibrary* Game = {}; + Juliet::DynamicLibrary* Game = {}; bool Running = false; };