From bfd042abbfcf67a29814d5461ee18306a59759fa Mon Sep 17 00:00:00 2001 From: Patedam Date: Sun, 11 Jan 2026 22:07:38 -0500 Subject: [PATCH] Added: - Depth buffer - Debug display basics - Basic vector + matrix maths Made partially with gemini + antigravity --- Assets/compiled/Debug.frag.dxil | Bin 0 -> 2812 bytes Assets/compiled/Debug.vert.dxil | Bin 0 -> 4676 bytes Assets/compiled/Triangle.vert.dxil | Bin 3552 -> 3552 bytes Assets/source/Debug.frag.hlsl | 4 + Assets/source/Debug.vert.hlsl | 27 ++ Assets/source/RootConstants.hlsl | 11 + Assets/source/Triangle.vert.hlsl | 19 +- Juliet/Juliet.vcxproj | 5 + Juliet/Juliet.vcxproj.filters | 15 + Juliet/include/Core/Math/Matrix.h | 86 +++++ Juliet/include/Core/Math/Vector.h | 39 ++ Juliet/include/Graphics/Camera.h | 33 ++ Juliet/include/Graphics/DebugDisplay.h | 17 + Juliet/include/Graphics/Graphics.h | 10 +- Juliet/include/Graphics/RenderPass.h | 14 +- Juliet/src/Graphics/D3D12/D3D12CommandList.h | 1 + .../Graphics/D3D12/D3D12GraphicsDevice.cpp | 11 +- Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp | 49 ++- Juliet/src/Graphics/D3D12/D3D12RenderPass.h | 2 +- Juliet/src/Graphics/D3D12/D3D12Texture.cpp | 175 ++++++++- Juliet/src/Graphics/D3D12/D3D12Texture.h | 3 + Juliet/src/Graphics/DebugDisplayRenderer.cpp | 349 ++++++++++++++++++ Juliet/src/Graphics/Graphics.cpp | 19 +- Juliet/src/Graphics/GraphicsDevice.h | 6 +- JulietApp/main.cpp | 75 +++- JulietApp/main.h | 1 + JulietShaderCompiler/main.cpp | 2 +- misc/recompile_shaders.bat | 53 +-- 28 files changed, 959 insertions(+), 67 deletions(-) create mode 100644 Assets/compiled/Debug.frag.dxil create mode 100644 Assets/compiled/Debug.vert.dxil create mode 100644 Assets/source/Debug.frag.hlsl create mode 100644 Assets/source/Debug.vert.hlsl create mode 100644 Assets/source/RootConstants.hlsl create mode 100644 Juliet/include/Core/Math/Matrix.h create mode 100644 Juliet/include/Core/Math/Vector.h create mode 100644 Juliet/include/Graphics/Camera.h create mode 100644 Juliet/include/Graphics/DebugDisplay.h create mode 100644 Juliet/src/Graphics/DebugDisplayRenderer.cpp diff --git a/Assets/compiled/Debug.frag.dxil b/Assets/compiled/Debug.frag.dxil new file mode 100644 index 0000000000000000000000000000000000000000..8920976a73ee942d460e86a1b44fe147238d85e0 GIT binary patch literal 2812 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 literal 0 HcmV?d00001 diff --git a/Assets/compiled/Triangle.vert.dxil b/Assets/compiled/Triangle.vert.dxil index df120250c1ef6f90686241f320db4458e490c3ff..e9737f9c8686d53c65b9df2539fc8557210d33f6 100644 GIT binary patch delta 140 zcmaDL{Xkm8CBn(s!}CCX^^^aGTn}%t)i5rp|Ljk-esXxk{tGtf5`Xru3HWw0c*wlMYD6-cSFc%557Z)&BbFfz? zv=^LcFJHl4HKWnyNdteL1OL+td> + + @@ -66,7 +68,9 @@ + + @@ -166,6 +170,7 @@ + diff --git a/Juliet/Juliet.vcxproj.filters b/Juliet/Juliet.vcxproj.filters index 313a49f..4168253 100644 --- a/Juliet/Juliet.vcxproj.filters +++ b/Juliet/Juliet.vcxproj.filters @@ -79,9 +79,15 @@ include\Core\Math + + include\Core\Math + include\Core\Math + + include\Core\Math + include\Core\Memory @@ -118,9 +124,15 @@ include\Engine + + include\Graphics + include\Graphics + + include\Graphics + include\Graphics @@ -417,6 +429,9 @@ src\Graphics\D3D12 + + src\Graphics + src\Graphics diff --git a/Juliet/include/Core/Math/Matrix.h b/Juliet/include/Core/Math/Matrix.h new file mode 100644 index 0000000..36e7d97 --- /dev/null +++ b/Juliet/include/Core/Math/Matrix.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +namespace Juliet +{ + struct Matrix + { + float m[4][4]; + + static Matrix Identity() + { + Matrix result = {}; + result.m[0][0] = 1.0f; + result.m[1][1] = 1.0f; + result.m[2][2] = 1.0f; + result.m[3][3] = 1.0f; + return result; + } + + Matrix operator*(const Matrix& rhs) const + { + Matrix result = {}; + for (int i = 0; i < 4; ++i) + { + for (int j = 0; j < 4; ++j) + { + for (int k = 0; k < 4; ++k) + { + result.m[i][j] += m[i][k] * rhs.m[k][j]; + } + } + } + return result; + } + }; + + inline Matrix LookAt(const Vector3& eye, const Vector3& target, const Vector3& up) + { + // Left-Handed convention + Vector3 zaxis = Normalize(target - eye); // Forward is +z + Vector3 xaxis = Normalize(Cross(up, zaxis)); + Vector3 yaxis = Cross(zaxis, xaxis); + + Matrix result = {}; + // Row 0 + result.m[0][0] = xaxis.x; + result.m[0][1] = xaxis.y; + result.m[0][2] = xaxis.z; + result.m[0][3] = -Dot(xaxis, eye); + + // Row 1 + result.m[1][0] = yaxis.x; + result.m[1][1] = yaxis.y; + result.m[1][2] = yaxis.z; + result.m[1][3] = -Dot(yaxis, eye); + + // Row 2 + result.m[2][0] = zaxis.x; + result.m[2][1] = zaxis.y; + result.m[2][2] = zaxis.z; + result.m[2][3] = -Dot(zaxis, eye); + + // Row 3 + result.m[3][3] = 1.0f; + + return result; + } + + inline Matrix PerspectiveFov(float fovY, float aspectRatio, float nearZ, float farZ) + { + // Left-Handed Perspective + float yScale = 1.0f / tanf(fovY * 0.5f); + float xScale = yScale / aspectRatio; + + Matrix result = {}; + result.m[0][0] = xScale; + result.m[1][1] = yScale; + result.m[2][2] = farZ / (farZ - nearZ); + result.m[2][3] = (-nearZ * farZ) / (farZ - nearZ); + result.m[3][2] = 1.0f; + result.m[3][3] = 0.0f; + return result; + } +} // namespace Juliet diff --git a/Juliet/include/Core/Math/Vector.h b/Juliet/include/Core/Math/Vector.h new file mode 100644 index 0000000..5158d74 --- /dev/null +++ b/Juliet/include/Core/Math/Vector.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +namespace Juliet +{ + struct Vector3 + { + float x, y, z; + + Vector3 operator+(const Vector3& rhs) const { return { x + rhs.x, y + rhs.y, z + rhs.z }; } + Vector3 operator-(const Vector3& rhs) const { return { x - rhs.x, y - rhs.y, z - rhs.z }; } + Vector3 operator*(float s) const { return { x * s, y * s, z * s }; } + }; + + struct Vector4 + { + float x, y, z, w; + }; + + inline Vector3 Normalize(const Vector3& v) + { + float len = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z); + if (len > 0.0001f) + { + return { v.x / len, v.y / len, v.z / len }; + } + return v; + } + + inline Vector3 Cross(const Vector3& a, const Vector3& b) + { + return { a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x }; + } + + inline float Dot(const Vector3& a, const Vector3& b) { return a.x * b.x + a.y * b.y + a.z * b.z; } +} // namespace Juliet diff --git a/Juliet/include/Graphics/Camera.h b/Juliet/include/Graphics/Camera.h new file mode 100644 index 0000000..2a8e4d1 --- /dev/null +++ b/Juliet/include/Graphics/Camera.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +namespace Juliet +{ + struct Camera + { + Vector3 Position; + Vector3 Target; + Vector3 Up; + float FOV; // In radians + float AspectRatio; + float NearPlane; + float FarPlane; + }; + + inline Matrix Camera_GetViewMatrix(const Camera& cam) + { + return LookAt(cam.Position, cam.Target, cam.Up); + } + + inline Matrix Camera_GetProjectionMatrix(const Camera& cam) + { + return PerspectiveFov(cam.FOV, cam.AspectRatio, cam.NearPlane, cam.FarPlane); + } + + inline Matrix Camera_GetViewProjectionMatrix(const Camera& cam) + { + return Camera_GetProjectionMatrix(cam) * Camera_GetViewMatrix(cam); + } +} // namespace Juliet diff --git a/Juliet/include/Graphics/DebugDisplay.h b/Juliet/include/Graphics/DebugDisplay.h new file mode 100644 index 0000000..216ae21 --- /dev/null +++ b/Juliet/include/Graphics/DebugDisplay.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Juliet +{ + extern JULIET_API void DebugDisplay_Initialize(GraphicsDevice* device); + extern JULIET_API void DebugDisplay_Shutdown(GraphicsDevice* device); + extern JULIET_API void DebugDisplay_DrawLine(const Vector3& start, const Vector3& end, const FColor& color, bool overlay); + extern JULIET_API void DebugDisplay_DrawSphere(const Vector3& center, float radius, const FColor& color, bool overlay); + extern JULIET_API void DebugDisplay_Prepare(CommandList* cmdList); + extern JULIET_API void DebugDisplay_Flush(CommandList* cmdList, RenderPass* renderPass, const Camera& camera); +} // namespace Juliet diff --git a/Juliet/include/Graphics/Graphics.h b/Juliet/include/Graphics/Graphics.h index 24e5078..1421d13 100644 --- a/Juliet/include/Graphics/Graphics.h +++ b/Juliet/include/Graphics/Graphics.h @@ -104,14 +104,20 @@ namespace Juliet extern JULIET_API bool WaitForSwapchain(NonNullPtr device, NonNullPtr window); extern JULIET_API TextureFormat GetSwapChainTextureFormat(NonNullPtr device, NonNullPtr window); + // Textures + extern JULIET_API Texture* CreateTexture(NonNullPtr device, const TextureCreateInfo& createInfo); + extern JULIET_API void DestroyTexture(NonNullPtr device, NonNullPtr texture); + // Command List extern JULIET_API CommandList* AcquireCommandList(NonNullPtr device, QueueType queueType = QueueType::Graphics); extern JULIET_API void SubmitCommandLists(NonNullPtr commandList); // RenderPass - extern JULIET_API RenderPass* BeginRenderPass(NonNullPtr commandList, ColorTargetInfo& colorTargetInfo); + extern JULIET_API RenderPass* BeginRenderPass(NonNullPtr commandList, ColorTargetInfo& colorTargetInfo, + DepthStencilTargetInfo* depthStencilTargetInfo = nullptr); extern JULIET_API RenderPass* BeginRenderPass(NonNullPtr commandList, - NonNullPtr colorTargetInfos, uint32 colorTargetInfoCount); + NonNullPtr colorTargetInfos, uint32 colorTargetInfoCount, + DepthStencilTargetInfo* depthStencilTargetInfo = nullptr); extern JULIET_API void EndRenderPass(NonNullPtr renderPass); extern JULIET_API void SetGraphicsViewPort(NonNullPtr renderPass, const GraphicsViewPort& viewPort); diff --git a/Juliet/include/Graphics/RenderPass.h b/Juliet/include/Graphics/RenderPass.h index 5f7b06f..68da627 100644 --- a/Juliet/include/Graphics/RenderPass.h +++ b/Juliet/include/Graphics/RenderPass.h @@ -41,7 +41,19 @@ namespace Juliet StoreOperation StoreOperation; }; - enum class BlendFactor : uint8 + struct DepthStencilTargetInfo + { + Texture* TargetTexture; + uint32 MipLevel; + uint32 LayerIndex; + + float ClearDepth; + uint8 ClearStencil; + LoadOperation LoadOperation; + StoreOperation StoreOperation; + }; + + enum class BlendFactor : uint8 { Invalid, Zero, diff --git a/Juliet/src/Graphics/D3D12/D3D12CommandList.h b/Juliet/src/Graphics/D3D12/D3D12CommandList.h index a6de01c..fb31cae 100644 --- a/Juliet/src/Graphics/D3D12/D3D12CommandList.h +++ b/Juliet/src/Graphics/D3D12/D3D12CommandList.h @@ -59,6 +59,7 @@ namespace Juliet::D3D12 D3D12TextureSubresource* ColorTargetSubresources[GPUDriver::kMaxColorTargetInfo]; D3D12TextureSubresource* ColorResolveSubresources[GPUDriver::kMaxColorTargetInfo]; + D3D12TextureSubresource* DepthStencilSubresource; bool NeedVertexBufferBind : 1; bool NeedVertexSamplerBind : 1; diff --git a/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp index 3feb46f..4e287c2 100644 --- a/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -254,13 +255,7 @@ namespace Juliet::D3D12 .pParameters = parameters, .NumStaticSamplers = ArraySize(samplers), .pStaticSamplers = samplers, - .Flags = D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS | + .Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED, }, @@ -1010,6 +1005,8 @@ namespace Juliet::D3D12 device->CopyBuffer = CopyBuffer; device->TransitionBufferToReadable = TransitionBufferToReadable; device->GetDescriptorIndex = GetDescriptorIndex; + device->CreateTexture = CreateTexture; + device->DestroyTexture = DestroyTexture; #if ALLOW_SHADER_HOT_RELOAD device->UpdateGraphicsPipelineShaders = UpdateGraphicsPipelineShaders; diff --git a/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp b/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp index 3217d1a..b3bdd8b 100644 --- a/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12RenderPass.cpp @@ -21,7 +21,8 @@ namespace Juliet::D3D12 ToUnderlying(PrimitiveType::Count)); } // namespace - void BeginRenderPass(NonNullPtr commandList, NonNullPtr colorTargetInfos, uint32 colorTargetInfoCount) + void BeginRenderPass(NonNullPtr commandList, NonNullPtr colorTargetInfos, + uint32 colorTargetInfoCount, const DepthStencilTargetInfo* depthStencilTargetInfo) { auto* d3d12CommandList = reinterpret_cast(commandList.Get()); @@ -39,7 +40,38 @@ namespace Juliet::D3D12 frameBufferHeight = Min(height, frameBufferHeight); } - // TODO : Depth Stencil and DSV + // Depth Stencil and DSV + D3D12_CPU_DESCRIPTOR_HANDLE DSV; + bool hasDSV = false; + if (depthStencilTargetInfo && depthStencilTargetInfo->TargetTexture) + { + auto* container = reinterpret_cast(depthStencilTargetInfo->TargetTexture); + uint32 width = container->Header.CreateInfo.Width; + uint32 height = container->Header.CreateInfo.Height; + + frameBufferWidth = Min(width, frameBufferWidth); + frameBufferHeight = Min(height, frameBufferHeight); + + D3D12TextureSubresource* subresource = Internal::PrepareTextureSubresourceForWrite( + d3d12CommandList, container, 0, 0, false, D3D12_RESOURCE_STATE_DEPTH_WRITE); + + DSV = subresource->DSVHandle.CpuHandle; + hasDSV = true; + d3d12CommandList->DepthStencilSubresource = subresource; + + Internal::TrackTexture(d3d12CommandList, subresource->Parent); + + if (depthStencilTargetInfo->LoadOperation == LoadOperation::Clear) + { + D3D12_CLEAR_FLAGS clearFlags = D3D12_CLEAR_FLAG_DEPTH; + // TODO: Check if texture has stencil + // if (HasStencil(container->Header.CreateInfo.Format)) clearFlags |= D3D12_CLEAR_FLAG_STENCIL; + + ID3D12GraphicsCommandList_ClearDepthStencilView(d3d12CommandList->GraphicsCommandList.CommandList, DSV, + clearFlags, depthStencilTargetInfo->ClearDepth, + depthStencilTargetInfo->ClearStencil, 0, nullptr); + } + } D3D12_CPU_DESCRIPTOR_HANDLE RTVs[GPUDriver::kMaxColorTargetInfo]; for (uint32 idx = 0; idx < colorTargetInfoCount; ++idx) @@ -87,10 +119,8 @@ namespace Juliet::D3D12 } } - // TODO DSV - ID3D12GraphicsCommandList_OMSetRenderTargets(d3d12CommandList->GraphicsCommandList.CommandList, - colorTargetInfoCount, RTVs, false, nullptr); + colorTargetInfoCount, RTVs, false, hasDSV ? &DSV : nullptr); // Set defaults graphics states GraphicsViewPort defaultViewport; @@ -159,7 +189,14 @@ namespace Juliet::D3D12 } } - // TODO : Write Depth stencil + // Reset Depth Stencil state + if (d3d12CommandList->DepthStencilSubresource) + { + Internal::TextureSubresourceTransitionToDefaultUsage(d3d12CommandList, + d3d12CommandList->DepthStencilSubresource, + D3D12_RESOURCE_STATE_DEPTH_WRITE); + d3d12CommandList->DepthStencilSubresource = nullptr; + } d3d12CommandList->CurrentGraphicsPipeline = nullptr; ID3D12GraphicsCommandList_OMSetRenderTargets(d3d12CommandList->GraphicsCommandList.CommandList, 0, nullptr, false, nullptr); diff --git a/Juliet/src/Graphics/D3D12/D3D12RenderPass.h b/Juliet/src/Graphics/D3D12/D3D12RenderPass.h index d3ae3b2..62f8523 100644 --- a/Juliet/src/Graphics/D3D12/D3D12RenderPass.h +++ b/Juliet/src/Graphics/D3D12/D3D12RenderPass.h @@ -7,7 +7,7 @@ namespace Juliet::D3D12 { extern void BeginRenderPass(NonNullPtr commandList, NonNullPtr colorTargetInfos, - uint32 colorTargetInfoCount); + uint32 colorTargetInfoCount, const DepthStencilTargetInfo* depthStencilTargetInfo); extern void EndRenderPass(NonNullPtr commandList); extern void BindGraphicsPipeline(NonNullPtr commandList, NonNullPtr graphicsPipeline); diff --git a/Juliet/src/Graphics/D3D12/D3D12Texture.cpp b/Juliet/src/Graphics/D3D12/D3D12Texture.cpp index e6bc28c..9c6903d 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Texture.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12Texture.cpp @@ -4,6 +4,10 @@ #include #include #include +#include +#include +#include +#include namespace Juliet::D3D12 { @@ -68,11 +72,11 @@ namespace Juliet::D3D12 DXGI_FORMAT_BC2_UNORM_SRGB, // BC2_UNORM_SRGB DXGI_FORMAT_BC3_UNORM_SRGB, // BC3_UNORM_SRGB DXGI_FORMAT_BC7_UNORM_SRGB, // BC7_UNORM_SRGB - DXGI_FORMAT_R16_UNORM, // D16_UNORM - DXGI_FORMAT_R24_UNORM_X8_TYPELESS, // D24_UNORM - DXGI_FORMAT_R32_FLOAT, // D32_FLOAT - DXGI_FORMAT_R24_UNORM_X8_TYPELESS, // D24_UNORM_S8_UINT - DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, // D32_FLOAT_S8_UINT + DXGI_FORMAT_R16_TYPELESS, // D16_UNORM + DXGI_FORMAT_R24G8_TYPELESS, // D24_UNORM + DXGI_FORMAT_R32_TYPELESS, // D32_FLOAT + DXGI_FORMAT_R24G8_TYPELESS, // D24_UNORM_S8_UINT + DXGI_FORMAT_R32G8X24_TYPELESS, // D32_FLOAT_S8_UINT DXGI_FORMAT_UNKNOWN, // ASTC_4x4_UNORM DXGI_FORMAT_UNKNOWN, // ASTC_5x4_UNORM DXGI_FORMAT_UNKNOWN, // ASTC_5x5_UNORM @@ -360,4 +364,165 @@ namespace Juliet::D3D12 8, // MSAA 8x }; } // namespace Internal + + Texture* CreateTexture(NonNullPtr driver, const TextureCreateInfo& createInfo) + { + auto* d3d12Driver = static_cast(driver.Get()); + + D3D12_RESOURCE_DESC desc = {}; + switch (createInfo.Type) + { + case TextureType::Texture_2D: + case TextureType::Texture_2DArray: + case TextureType::Texture_Cube: + case TextureType::Texture_CubeArray: + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + break; + case TextureType::Texture_3D: + case TextureType::Texture_3DArray: + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D; + break; + } + + desc.Alignment = 0; + desc.Width = createInfo.Width; + desc.Height = createInfo.Height; + desc.DepthOrArraySize = static_cast(createInfo.LayerCount); + desc.MipLevels = static_cast(createInfo.MipLevelCount); + desc.Format = Internal::ConvertToD3D12TextureFormat(createInfo.Format); + desc.SampleDesc.Count = Internal::JulietToD3D12_SampleCount[ToUnderlying(createInfo.SampleCount)]; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + desc.Flags = D3D12_RESOURCE_FLAG_NONE; + + if ((createInfo.Flags & TextureUsageFlag::ColorTarget) != TextureUsageFlag::None) + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + if ((createInfo.Flags & TextureUsageFlag::DepthStencilTarget) != TextureUsageFlag::None) + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + if ((createInfo.Flags & TextureUsageFlag::ComputeStorageWrite) != TextureUsageFlag::None) + desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + + D3D12_HEAP_PROPERTIES heapProps = {}; + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + + ID3D12Resource* resource = nullptr; + D3D12_CLEAR_VALUE clearValue = {}; + D3D12_CLEAR_VALUE* pClearValue = nullptr; + + if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) + { + clearValue.Format = Internal::ConvertToD3D12DepthFormat(createInfo.Format); + clearValue.DepthStencil.Depth = 1.0f; + clearValue.DepthStencil.Stencil = 0; + pClearValue = &clearValue; + } + else if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) + { + clearValue.Format = desc.Format; + clearValue.Color[0] = 0.0f; + clearValue.Color[1] = 0.0f; + clearValue.Color[2] = 0.0f; + clearValue.Color[3] = 0.0f; + pClearValue = &clearValue; + } + + HRESULT hr = ID3D12Device_CreateCommittedResource(d3d12Driver->D3D12Device, &heapProps, D3D12_HEAP_FLAG_NONE, &desc, + D3D12_RESOURCE_STATE_COMMON, pClearValue, IID_ID3D12Resource, + reinterpret_cast(&resource)); + + if (FAILED(hr)) + { + LogError(d3d12Driver->D3D12Device, "Failed to create D3D12 committed resource for texture", hr); + return nullptr; + } + + auto* textureContainer = static_cast(Calloc(1, sizeof(D3D12TextureContainer))); + auto* texture = static_cast(Calloc(1, sizeof(D3D12Texture))); + + textureContainer->Header.CreateInfo = createInfo; + textureContainer->ActiveTexture = texture; + textureContainer->Textures = static_cast(Malloc(sizeof(D3D12Texture*))); + textureContainer->Textures[0] = texture; + textureContainer->Capacity = 1; + textureContainer->Count = 1; + textureContainer->CanBeCycled = true; + + texture->Container = textureContainer; + texture->Resource = resource; + texture->ReferenceCount = 1; + + uint32 numLayers = std::max(1, createInfo.LayerCount); + uint32 numMips = std::max(1, createInfo.MipLevelCount); + texture->SubresourceCount = numLayers * numMips; + texture->Subresources = static_cast(Calloc(texture->SubresourceCount, sizeof(D3D12TextureSubresource))); + + for (uint32 layer = 0; layer < numLayers; ++layer) + { + for (uint32 mip = 0; mip < numMips; ++mip) + { + uint32 index = mip + (layer * numMips); + auto& sub = texture->Subresources[index]; + sub.Parent = texture; + sub.Layer = layer; + sub.Level = mip; + sub.Index = index; + sub.Depth = 1; // 3D texture depth handling would go here + + if ((createInfo.Flags & TextureUsageFlag::ColorTarget) != TextureUsageFlag::None) + { + sub.RTVHandles = static_cast(Calloc(1, sizeof(D3D12StagingDescriptor))); + Internal::AssignStagingDescriptor(d3d12Driver, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, sub.RTVHandles[0]); + + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {}; + rtvDesc.Format = desc.Format; + rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + rtvDesc.Texture2D.MipSlice = mip; + ID3D12Device_CreateRenderTargetView(d3d12Driver->D3D12Device, resource, &rtvDesc, sub.RTVHandles[0].CpuHandle); + } + + if ((createInfo.Flags & TextureUsageFlag::DepthStencilTarget) != TextureUsageFlag::None) + { + Internal::AssignStagingDescriptor(d3d12Driver, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, sub.DSVHandle); + + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; + dsvDesc.Format = Internal::ConvertToD3D12DepthFormat(createInfo.Format); + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + dsvDesc.Texture2D.MipSlice = mip; + ID3D12Device_CreateDepthStencilView(d3d12Driver->D3D12Device, resource, &dsvDesc, sub.DSVHandle.CpuHandle); + } + } + } + + return reinterpret_cast(textureContainer); + } + + void DestroyTexture(NonNullPtr driver, NonNullPtr texture) + { + auto* d3d12Driver = static_cast(driver.Get()); + auto* textureContainer = reinterpret_cast(texture.Get()); + + for (uint32 i = 0; i < textureContainer->Count; ++i) + { + D3D12Texture* d3d12Texture = textureContainer->Textures[i]; + for (uint32 j = 0; j < d3d12Texture->SubresourceCount; ++j) + { + D3D12TextureSubresource& sub = d3d12Texture->Subresources[j]; + if (sub.RTVHandles) + { + Internal::ReleaseStagingDescriptor(d3d12Driver, sub.RTVHandles[0]); + Free(sub.RTVHandles); + } + if (sub.DSVHandle.Heap) + { + Internal::ReleaseStagingDescriptor(d3d12Driver, sub.DSVHandle); + } + } + ID3D12Resource_Release(d3d12Texture->Resource); + Free(d3d12Texture->Subresources); + Free(d3d12Texture); + } + + Free(textureContainer->Textures); + Free(textureContainer); + } } // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/D3D12Texture.h b/Juliet/src/Graphics/D3D12/D3D12Texture.h index 2da1673..5c12ded 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Texture.h +++ b/Juliet/src/Graphics/D3D12/D3D12Texture.h @@ -91,4 +91,7 @@ namespace Juliet::D3D12 extern DXGI_FORMAT ConvertToD3D12DepthFormat(TextureFormat format); extern uint32 JulietToD3D12_SampleCount[]; } // namespace Internal + + extern Texture* CreateTexture(NonNullPtr driver, const TextureCreateInfo& createInfo); + extern void DestroyTexture(NonNullPtr driver, NonNullPtr texture); } // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/DebugDisplayRenderer.cpp b/Juliet/src/Graphics/DebugDisplayRenderer.cpp new file mode 100644 index 0000000..ee14bc7 --- /dev/null +++ b/Juliet/src/Graphics/DebugDisplayRenderer.cpp @@ -0,0 +1,349 @@ +#include + +#include +#include +#include +#include + +namespace Juliet +{ + namespace + { + constexpr uint32 kMaxDebugVertices = 65536; + + struct DebugVertex + { + Vector3 Position; + FColor Color; + }; + + struct DebugDisplayState + { + GraphicsDevice* Device; + + // Pipelines + GraphicsPipeline* DepthTestedPipeline; + GraphicsPipeline* OverlayPipeline; + + // Vertex data + DebugVertex* DepthTestedVertices; + uint32 DepthTestedVertexCount; + + DebugVertex* OverlayVertices; + uint32 OverlayVertexCount; + + // GPU buffers + GraphicsBuffer* DepthTestedBuffer; + GraphicsBuffer* OverlayBuffer; + GraphicsTransferBuffer* DepthTestedTransfer; + GraphicsTransferBuffer* OverlayTransfer; + + bool Initialized; + }; + + DebugDisplayState g_DebugState = {}; + + void AddLine(DebugVertex* vertices, uint32& count, const Vector3& start, const Vector3& end, const FColor& color) + { + if (count + 2 > kMaxDebugVertices) + { + return; + } + + vertices[count++] = { start, color }; + vertices[count++] = { end, color }; + } + + void AddSphereWireframe(DebugVertex* vertices, uint32& count, const Vector3& center, float radius, const FColor& color) + { + constexpr int segments = 16; + constexpr float pi = 3.14159265358979f; + + // Draw 3 circles (XY, XZ, YZ planes) + for (int i = 0; i < segments; ++i) + { + float a1 = (float)i / segments * 2.0f * pi; + float a2 = (float)(i + 1) / segments * 2.0f * pi; + + // XY circle + Vector3 p1 = { center.x + cosf(a1) * radius, center.y + sinf(a1) * radius, center.z }; + Vector3 p2 = { center.x + cosf(a2) * radius, center.y + sinf(a2) * radius, center.z }; + AddLine(vertices, count, p1, p2, color); + + // XZ circle + p1 = { center.x + cosf(a1) * radius, center.y, center.z + sinf(a1) * radius }; + p2 = { center.x + cosf(a2) * radius, center.y, center.z + sinf(a2) * radius }; + AddLine(vertices, count, p1, p2, color); + + // YZ circle + p1 = { center.x, center.y + cosf(a1) * radius, center.z + sinf(a1) * radius }; + p2 = { center.x, center.y + cosf(a2) * radius, center.z + sinf(a2) * radius }; + AddLine(vertices, count, p1, p2, color); + } + } + + GraphicsPipeline* CreateDebugPipeline(GraphicsDevice* device, TextureFormat colorFormat, bool enableDepthTest) + { + String entryPoint = WrapString("main"); + ShaderCreateInfo shaderCI = {}; + shaderCI.EntryPoint = entryPoint; + + String vertPath = WrapString("../../Assets/compiled/Debug.vert.dxil"); + shaderCI.Stage = ShaderStage::Vertex; + Shader* vertexShader = CreateShader(device, vertPath, shaderCI); + + String fragPath = WrapString("../../Assets/compiled/Debug.frag.dxil"); + shaderCI.Stage = ShaderStage::Fragment; + Shader* fragmentShader = CreateShader(device, fragPath, shaderCI); + + if (!vertexShader || !fragmentShader) + { + LogError(LogCategory::Graphics, "Failed to create debug shaders"); + if (vertexShader) + { + DestroyShader(device, vertexShader); + } + if (fragmentShader) + { + DestroyShader(device, fragmentShader); + } + return nullptr; + } + + ColorTargetDescription colorDesc = {}; + colorDesc.Format = colorFormat; + + GraphicsPipelineCreateInfo createInfo = {}; + createInfo.VertexShader = vertexShader; + createInfo.FragmentShader = fragmentShader; + createInfo.PrimitiveType = PrimitiveType::LineList; + createInfo.RasterizerState.FillMode = FillMode::Solid; + createInfo.RasterizerState.CullMode = CullMode::None; + createInfo.RasterizerState.FrontFace = FrontFace::CounterClockwise; + createInfo.MultisampleState.SampleCount = TextureSampleCount::One; + + // No vertex input state - using bindless buffer access via SV_VertexID + + createInfo.TargetInfo.ColorTargetDescriptions = &colorDesc; + createInfo.TargetInfo.NumColorTargets = 1; + + // Now that we support depth-stencil targets in the backend, we can enable them. + createInfo.TargetInfo.HasDepthStencilTarget = true; + createInfo.TargetInfo.DepthStencilFormat = TextureFormat::D32_FLOAT; + + if (enableDepthTest) + { + createInfo.DepthStencilState.EnableDepthTest = true; + createInfo.DepthStencilState.EnableDepthWrite = true; + createInfo.DepthStencilState.CompareOperation = CompareOperation::Less; + } + else + { + // Overlay pipeline: depth test/write off, so it draws on top + createInfo.DepthStencilState.EnableDepthTest = false; + createInfo.DepthStencilState.EnableDepthWrite = false; + createInfo.DepthStencilState.CompareOperation = CompareOperation::Always; + } + + GraphicsPipeline* pipeline = CreateGraphicsPipeline(device, createInfo); + + DestroyShader(device, vertexShader); + DestroyShader(device, fragmentShader); + + return pipeline; + } + } // namespace + + void DebugDisplay_Initialize(GraphicsDevice* device) + { + if (g_DebugState.Initialized) + { + return; + } + + g_DebugState.Device = device; + + // Allocate CPU vertex arrays + g_DebugState.DepthTestedVertices = static_cast(Malloc(kMaxDebugVertices * sizeof(DebugVertex))); + g_DebugState.OverlayVertices = static_cast(Malloc(kMaxDebugVertices * sizeof(DebugVertex))); + g_DebugState.DepthTestedVertexCount = 0; + g_DebugState.OverlayVertexCount = 0; + + // Create GPU buffers + BufferCreateInfo bufferCI = {}; + bufferCI.Size = kMaxDebugVertices * sizeof(DebugVertex); + bufferCI.Usage = BufferUsage::StructuredBuffer; + + g_DebugState.DepthTestedBuffer = CreateGraphicsBuffer(device, bufferCI); + g_DebugState.OverlayBuffer = CreateGraphicsBuffer(device, bufferCI); + + TransferBufferCreateInfo transferCI = {}; + transferCI.Size = kMaxDebugVertices * sizeof(DebugVertex); + transferCI.Usage = TransferBufferUsage::Upload; + + g_DebugState.DepthTestedTransfer = CreateGraphicsTransferBuffer(device, transferCI); + g_DebugState.OverlayTransfer = CreateGraphicsTransferBuffer(device, transferCI); + + g_DebugState.Initialized = true; + } + + void DebugDisplay_Shutdown(GraphicsDevice* device) + { + if (!g_DebugState.Initialized) + { + return; + } + + if (g_DebugState.DepthTestedPipeline) + { + DestroyGraphicsPipeline(device, g_DebugState.DepthTestedPipeline); + } + if (g_DebugState.OverlayPipeline) + { + DestroyGraphicsPipeline(device, g_DebugState.OverlayPipeline); + } + + if (g_DebugState.DepthTestedBuffer) + { + DestroyGraphicsBuffer(device, g_DebugState.DepthTestedBuffer); + } + if (g_DebugState.OverlayBuffer) + { + DestroyGraphicsBuffer(device, g_DebugState.OverlayBuffer); + } + if (g_DebugState.DepthTestedTransfer) + { + DestroyGraphicsTransferBuffer(device, g_DebugState.DepthTestedTransfer); + } + if (g_DebugState.OverlayTransfer) + { + DestroyGraphicsTransferBuffer(device, g_DebugState.OverlayTransfer); + } + + SafeFree(g_DebugState.DepthTestedVertices); + SafeFree(g_DebugState.OverlayVertices); + + g_DebugState = {}; + } + + void DebugDisplay_DrawLine(const Vector3& start, const Vector3& end, const FColor& color, bool overlay) + { + if (overlay) + { + AddLine(g_DebugState.OverlayVertices, g_DebugState.OverlayVertexCount, start, end, color); + } + else + { + AddLine(g_DebugState.DepthTestedVertices, g_DebugState.DepthTestedVertexCount, start, end, color); + } + } + + void DebugDisplay_DrawSphere(const Vector3& center, float radius, const FColor& color, bool overlay) + { + if (overlay) + { + AddSphereWireframe(g_DebugState.OverlayVertices, g_DebugState.OverlayVertexCount, center, radius, color); + } + else + { + AddSphereWireframe(g_DebugState.DepthTestedVertices, g_DebugState.DepthTestedVertexCount, center, radius, color); + } + } + + void DebugDisplay_Prepare(CommandList* cmdList) + { + if (!g_DebugState.Initialized) + { + return; + } + + // Render depth-tested primitives + if (g_DebugState.DepthTestedVertexCount > 0 && g_DebugState.DepthTestedBuffer) + { + // Upload vertex data + void* ptr = MapGraphicsTransferBuffer(g_DebugState.Device, g_DebugState.DepthTestedTransfer); + if (ptr) + { + MemCopy(ptr, g_DebugState.DepthTestedVertices, g_DebugState.DepthTestedVertexCount * sizeof(DebugVertex)); + UnmapGraphicsTransferBuffer(g_DebugState.Device, g_DebugState.DepthTestedTransfer); + + CopyBuffer(cmdList, g_DebugState.DepthTestedBuffer, g_DebugState.DepthTestedTransfer, + g_DebugState.DepthTestedVertexCount * sizeof(DebugVertex)); + TransitionBufferToReadable(cmdList, g_DebugState.DepthTestedBuffer); + } + } + + // Render overlay primitives + if (g_DebugState.OverlayVertexCount > 0 && g_DebugState.OverlayBuffer) + { + // Upload vertex data + void* ptr = MapGraphicsTransferBuffer(g_DebugState.Device, g_DebugState.OverlayTransfer); + if (ptr) + { + MemCopy(ptr, g_DebugState.OverlayVertices, g_DebugState.OverlayVertexCount * sizeof(DebugVertex)); + UnmapGraphicsTransferBuffer(g_DebugState.Device, g_DebugState.OverlayTransfer); + + CopyBuffer(cmdList, g_DebugState.OverlayBuffer, g_DebugState.OverlayTransfer, + g_DebugState.OverlayVertexCount * sizeof(DebugVertex)); + TransitionBufferToReadable(cmdList, g_DebugState.OverlayBuffer); + } + } + } + + void DebugDisplay_Flush(CommandList* cmdList, RenderPass* renderPass, const Camera& camera) + { + + if (!g_DebugState.Initialized) + { + return; + } + + // Lazy-create pipelines (need swapchain format) + if (!g_DebugState.DepthTestedPipeline) + { + // Use B8G8R8A8_UNORM which matches the SDR swapchain + g_DebugState.DepthTestedPipeline = CreateDebugPipeline(g_DebugState.Device, TextureFormat::B8G8R8A8_UNORM, true); + g_DebugState.OverlayPipeline = CreateDebugPipeline(g_DebugState.Device, TextureFormat::B8G8R8A8_UNORM, false); + } + + // Render depth-tested primitives + if (g_DebugState.DepthTestedVertexCount > 0 && g_DebugState.DepthTestedPipeline && g_DebugState.DepthTestedBuffer) + { + BindGraphicsPipeline(renderPass, g_DebugState.DepthTestedPipeline); + + // Pack VP matrix + buffer index into push constants + struct { + Matrix vp; + uint32 bufferIndex; + uint32 padding[3]; + } pushData; + pushData.vp = Camera_GetViewProjectionMatrix(camera); + pushData.bufferIndex = GetDescriptorIndex(g_DebugState.Device, g_DebugState.DepthTestedBuffer); + SetPushConstants(cmdList, ShaderStage::Vertex, 0, sizeof(pushData) / sizeof(uint32), &pushData); + + DrawPrimitives(renderPass, g_DebugState.DepthTestedVertexCount, 1, 0, 0); + } + + // Render overlay primitives + if (g_DebugState.OverlayVertexCount > 0 && g_DebugState.OverlayPipeline && g_DebugState.OverlayBuffer) + { + BindGraphicsPipeline(renderPass, g_DebugState.OverlayPipeline); + + // Pack VP matrix + buffer index into push constants + struct { + Matrix vp; + uint32 bufferIndex; + uint32 padding[3]; + } pushData; + pushData.vp = Camera_GetViewProjectionMatrix(camera); + pushData.bufferIndex = GetDescriptorIndex(g_DebugState.Device, g_DebugState.OverlayBuffer); + SetPushConstants(cmdList, ShaderStage::Vertex, 0, sizeof(pushData) / sizeof(uint32), &pushData); + + DrawPrimitives(renderPass, g_DebugState.OverlayVertexCount, 1, 0, 0); + } + + // Clear for next frame + g_DebugState.DepthTestedVertexCount = 0; + g_DebugState.OverlayVertexCount = 0; + } +} // namespace Juliet diff --git a/Juliet/src/Graphics/Graphics.cpp b/Juliet/src/Graphics/Graphics.cpp index c85d516..61abded 100644 --- a/Juliet/src/Graphics/Graphics.cpp +++ b/Juliet/src/Graphics/Graphics.cpp @@ -159,6 +159,16 @@ namespace Juliet return device->GetSwapChainTextureFormat(device->Driver, window); } + Texture* CreateTexture(NonNullPtr device, const TextureCreateInfo& createInfo) + { + return device->CreateTexture(device->Driver, createInfo); + } + + void DestroyTexture(NonNullPtr device, NonNullPtr texture) + { + device->DestroyTexture(device->Driver, texture); + } + CommandList* AcquireCommandList(NonNullPtr device, QueueType queueType /* = QueueType::Graphics */) { GPUDriver* driver = device->Driver; @@ -185,13 +195,14 @@ namespace Juliet commandListHeader->Device->SubmitCommandLists(commandList); } - RenderPass* BeginRenderPass(NonNullPtr commandList, ColorTargetInfo& colorTargetInfo) + RenderPass* BeginRenderPass(NonNullPtr commandList, ColorTargetInfo& colorTargetInfo, + DepthStencilTargetInfo* depthStencilTargetInfo) { - return BeginRenderPass(commandList, &colorTargetInfo, 1); + return BeginRenderPass(commandList, &colorTargetInfo, 1, depthStencilTargetInfo); } RenderPass* BeginRenderPass(NonNullPtr commandList, NonNullPtr colorTargetInfos, - uint32 colorTargetInfoCount) + uint32 colorTargetInfoCount, DepthStencilTargetInfo* depthStencilTargetInfo) { if (colorTargetInfoCount > GPUDriver::kMaxColorTargetInfo) { @@ -200,7 +211,7 @@ namespace Juliet } auto* header = reinterpret_cast(commandList.Get()); - header->Device->BeginRenderPass(commandList, colorTargetInfos, colorTargetInfoCount); + header->Device->BeginRenderPass(commandList, colorTargetInfos, colorTargetInfoCount, depthStencilTargetInfo); header->RenderPass.IsInProgress = true; return reinterpret_cast(&header->RenderPass); diff --git a/Juliet/src/Graphics/GraphicsDevice.h b/Juliet/src/Graphics/GraphicsDevice.h index acdd256..1fe8b43 100644 --- a/Juliet/src/Graphics/GraphicsDevice.h +++ b/Juliet/src/Graphics/GraphicsDevice.h @@ -59,7 +59,7 @@ namespace Juliet // RenderPass void (*BeginRenderPass)(NonNullPtr commandList, NonNullPtr colorTargetInfos, - uint32 colorTargetInfoCount); + uint32 colorTargetInfoCount, const DepthStencilTargetInfo* depthStencilTargetInfo); void (*EndRenderPass)(NonNullPtr commandList); void (*SetViewPort)(NonNullPtr commandList, const GraphicsViewPort& viewPort); @@ -89,6 +89,10 @@ namespace Juliet bool (*UpdateGraphicsPipelineShaders)(NonNullPtr driver, NonNullPtr graphicsPipeline, Shader* optional_vertexShader, Shader* optional_fragmentShader); + // Textures + Texture* (*CreateTexture)(NonNullPtr driver, const TextureCreateInfo& createInfo); + void (*DestroyTexture)(NonNullPtr driver, NonNullPtr texture); + // Buffers GraphicsBuffer* (*CreateGraphicsBuffer)(NonNullPtr driver, size_t size, BufferUsage usage); void (*DestroyGraphicsBuffer)(NonNullPtr buffer); diff --git a/JulietApp/main.cpp b/JulietApp/main.cpp index d027ba8..31657e0 100644 --- a/JulietApp/main.cpp +++ b/JulietApp/main.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -93,9 +95,12 @@ void JulietApplication::Init() pipelineCI.PrimitiveType = PrimitiveType::TriangleList; pipelineCI.TargetInfo = { .ColorTargetDescriptions = &colorTargetDescription, .NumColorTargets = 1, - .DepthStencilFormat = {}, - .HasDepthStencilTarget = false }; + .DepthStencilFormat = TextureFormat::D32_FLOAT, + .HasDepthStencilTarget = true }; pipelineCI.RasterizerState.FillMode = FillMode::Solid; + pipelineCI.DepthStencilState.EnableDepthTest = true; + pipelineCI.DepthStencilState.EnableDepthWrite = true; + pipelineCI.DepthStencilState.CompareOperation = CompareOperation::Less; GraphicsPipeline = CreateGraphicsPipeline(GraphicsDevice, pipelineCI); if (GraphicsPipeline == nullptr) @@ -104,6 +109,23 @@ void JulietApplication::Init() Running = false; } + // Create Depth Buffer + TextureCreateInfo depthCI = {}; + depthCI.Type = TextureType::Texture_2D; + depthCI.Width = 1280; + depthCI.Height = 720; + depthCI.Format = TextureFormat::D32_FLOAT; + depthCI.Flags = TextureUsageFlag::DepthStencilTarget; + depthCI.LayerCount = 1; + depthCI.MipLevelCount = 1; + depthCI.SampleCount = TextureSampleCount::One; + DepthBuffer = CreateTexture(GraphicsDevice, depthCI); + if (DepthBuffer == nullptr) + { + LogError(LogCategory::Game, "Failed to create depth buffer!"); + Running = false; + } + // Create Buffers BufferCreateInfo bufferCI = {}; bufferCI.Size = 256; @@ -138,6 +160,9 @@ void JulietApplication::Init() { Game.Init(); } + + // Initialize DebugDisplay + DebugDisplay_Initialize(GraphicsDevice); } } @@ -151,6 +176,12 @@ void JulietApplication::Shutdown() ShutdownHotReloadCode(GameCode); } + // Shutdown DebugDisplay before graphics device + if (GraphicsDevice) + { + DebugDisplay_Shutdown(GraphicsDevice); + } + if (GraphicsPipeline) { DestroyGraphicsPipeline(GraphicsDevice, GraphicsPipeline); @@ -163,6 +194,10 @@ void JulietApplication::Shutdown() { DestroyGraphicsTransferBuffer(GraphicsDevice, TransferBuffer); } + if (DepthBuffer) + { + DestroyTexture(GraphicsDevice, DepthBuffer); + } if (MainWindow && GraphicsDevice) { @@ -275,7 +310,7 @@ void JulietApplication::Update() { ColorTargetInfo colorTargetInfo = {}; colorTargetInfo.TargetTexture = swapChainTexture; - colorTargetInfo.ClearColor = { .R = .5f, .G = .8f, .B = .0f, .A = 1.f }; + colorTargetInfo.ClearColor = { .R = .0f, .G = .0f, .B = .0f, .A = 1.f }; colorTargetInfo.LoadOperation = LoadOperation::Clear; colorTargetInfo.StoreOperation = StoreOperation::Store; @@ -303,7 +338,22 @@ void JulietApplication::Update() TransitionBufferToReadable(cmdList, ConstantBuffer); } - RenderPass* renderPass = BeginRenderPass(cmdList, colorTargetInfo); + // Test lines and sphere - GIANT SCALE + DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 10.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f }, false); // X-Axis (Red) + DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 10.0f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, true); // Y-Axis (Green) + DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 10.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, true); // Z-Axis (Blue) - Up + DebugDisplay_DrawSphere({ 0.0f, 0.0f, 0.0f }, 5.0f, { 1.0f, 1.0f, 0.0f, 1.0f }, true); // Yellow sphere + + // Prepare debug data (outside render pass) + DebugDisplay_Prepare(cmdList); + + DepthStencilTargetInfo depthTargetInfo = {}; + depthTargetInfo.TargetTexture = DepthBuffer; + depthTargetInfo.ClearDepth = 1.0f; + depthTargetInfo.LoadOperation = LoadOperation::Clear; + depthTargetInfo.StoreOperation = StoreOperation::Store; + + RenderPass* renderPass = BeginRenderPass(cmdList, colorTargetInfo, &depthTargetInfo); BindGraphicsPipeline(renderPass, GraphicsPipeline); // Pass descriptor index via Push Constants AFTER finding the pipeline (RootSignature) @@ -311,6 +361,23 @@ void JulietApplication::Update() SetPushConstants(cmdList, ShaderStage::Vertex, 0, 1, &descriptorIndex); DrawPrimitives(renderPass, 6, 1, 0, 0); + + // Debug Display - render shapes (inside render pass) + static float orbitTime = 0.0f; + orbitTime += 0.016f; // Rough approximation for 60fps + + float radius = 30.0f; + Camera debugCamera = {}; + debugCamera.Position = { cosf(orbitTime) * radius, sinf(orbitTime) * radius, 10.0f }; // Rotate in XY plane + debugCamera.Target = { 0.0f, 0.0f, 0.0f }; + debugCamera.Up = { 0.0f, 0.0f, 1.0f }; // Z-Up + debugCamera.FOV = 1.047f; // 60 degrees + debugCamera.AspectRatio = 1200.0f / 800.0f; + debugCamera.NearPlane = 0.1f; + debugCamera.FarPlane = 1000.0f; + + DebugDisplay_Flush(cmdList, renderPass, debugCamera); + EndRenderPass(renderPass); } diff --git a/JulietApp/main.h b/JulietApp/main.h index 6566154..74ca696 100644 --- a/JulietApp/main.h +++ b/JulietApp/main.h @@ -29,6 +29,7 @@ class JulietApplication : public Juliet::IApplication Juliet::GraphicsPipeline* GraphicsPipeline = {}; Juliet::GraphicsBuffer* ConstantBuffer = {}; Juliet::GraphicsTransferBuffer* TransferBuffer = {}; + Juliet::Texture* DepthBuffer = {}; bool Running = false; }; diff --git a/JulietShaderCompiler/main.cpp b/JulietShaderCompiler/main.cpp index 060410e..09cd900 100644 --- a/JulietShaderCompiler/main.cpp +++ b/JulietShaderCompiler/main.cpp @@ -52,7 +52,7 @@ int main(int argc, char* argv[]) IOClose(outStream); } // Pause here to not close the console window immediately on stop while debugging - __asm int 3; + // __asm int 3; }); for (int idx = 1; idx < argc; ++idx) diff --git a/misc/recompile_shaders.bat b/misc/recompile_shaders.bat index bd1b57e..81b7062 100644 --- a/misc/recompile_shaders.bat +++ b/misc/recompile_shaders.bat @@ -35,28 +35,39 @@ echo Output Dir: !OUTPUT_DIR! REM Créer le dossier de sortie s'il n'existe pas if not exist "%OUTPUT_DIR%" mkdir "%OUTPUT_DIR%" -REM Parcourir tous les fichiers .frag.hlsl et .vert.hlsl dans le dossier source -for %%F in ("%SOURCE_DIR%\*.frag.hlsl" "%SOURCE_DIR%\*.vert.hlsl") do ( - REM Extraire le nom du fichier sans l'extension - set "FILENAME=%%~nF" - REM Extraire l'extension du fichier - set "EXTENSION=%%~xF" - REM Remplacer .frag.hlsl par frag et .vert.hlsl par vert - if "%%~xF"==".frag.hlsl" ( - set "SHORT_EXTENSION=frag" - ) else if "%%~xF"==".vert.hlsl" ( - set "SHORT_EXTENSION=vert" - ) - REM Construire la ligne de commande - set "COMMAND=%COMPILER_PATH% %SOURCE_DIR%\!FILENAME!!EXTENSION! -o %OUTPUT_DIR%\!FILENAME!!SHORT_EXTENSION!.dxil" - echo !COMMAND! - REM Afficher la ligne de commande pour le débogage - echo Compiling: !FILENAME!!EXTENSION! - REM Appeler JulietShaderCompiler.exe avec les arguments spécifiés - !COMMAND! +REM Parcourir tous les fichiers .hlsl dans le dossier source +for %%F in ("%SOURCE_DIR%\*.hlsl") do ( + set "FULL_FILENAME=%%~nF%%~xF" - if !ERRORLEVEL! NEQ 0 ( - echo ERREUR lors de la compilation de %%F + REM Skip RootConstants.hlsl or other include files + if /I NOT "!FULL_FILENAME!"=="RootConstants.hlsl" ( + REM Detect stage from filename (.vert.hlsl, .frag.hlsl) + set "SHORT_EXTENSION=" + set "BASE_NAME=%%~nF" + + echo !FULL_FILENAME! | findstr /I "\.frag\.hlsl" >nul + if !ERRORLEVEL! EQU 0 ( + set "SHORT_EXTENSION=frag" + set "BASE_NAME=!BASE_NAME:.frag=!" + ) else ( + echo !FULL_FILENAME! | findstr /I "\.vert\.hlsl" >nul + if !ERRORLEVEL! EQU 0 ( + set "SHORT_EXTENSION=vert" + set "BASE_NAME=!BASE_NAME:.vert=!" + ) + ) + + if not "!SHORT_EXTENSION!"=="" ( + set "OUTPUT_FILE=%OUTPUT_DIR%\%%~nF.dxil" + set "COMMAND=%COMPILER_PATH% %%F -o !OUTPUT_FILE!" + + echo Compiling: %%F to !OUTPUT_FILE! + !COMMAND! + + if errorlevel 1 ( + echo ERREUR lors de la compilation de %%F + ) + ) ) )