diff --git a/Assets/compiled/Debug.frag.dxil b/Assets/compiled/Debug.frag.dxil
new file mode 100644
index 0000000..8920976
Binary files /dev/null and b/Assets/compiled/Debug.frag.dxil differ
diff --git a/Assets/compiled/Debug.vert.dxil b/Assets/compiled/Debug.vert.dxil
new file mode 100644
index 0000000..c24772d
Binary files /dev/null and b/Assets/compiled/Debug.vert.dxil differ
diff --git a/Assets/compiled/Triangle.vert.dxil b/Assets/compiled/Triangle.vert.dxil
index df12025..e9737f9 100644
Binary files a/Assets/compiled/Triangle.vert.dxil and b/Assets/compiled/Triangle.vert.dxil differ
diff --git a/Assets/source/Debug.frag.hlsl b/Assets/source/Debug.frag.hlsl
new file mode 100644
index 0000000..a8e6d9f
--- /dev/null
+++ b/Assets/source/Debug.frag.hlsl
@@ -0,0 +1,4 @@
+float4 main(float4 Color : TEXCOORD0) : SV_Target0
+{
+ return Color;
+}
diff --git a/Assets/source/Debug.vert.hlsl b/Assets/source/Debug.vert.hlsl
new file mode 100644
index 0000000..a21c382
--- /dev/null
+++ b/Assets/source/Debug.vert.hlsl
@@ -0,0 +1,27 @@
+struct Output
+{
+ float4 Color : TEXCOORD0;
+ float4 Position : SV_Position;
+};
+
+#include "RootConstants.hlsl"
+
+Output main(uint vertexIndex : SV_VertexID)
+{
+ Output output;
+
+ // Retrieve the vertex buffer using SM6.6 bindless syntax
+ ByteAddressBuffer buffer = ResourceDescriptorHeap[BufferIndex];
+
+ // Vertex layout: float3 Position (12 bytes) + float4 Color (16 bytes) = 28 bytes stride
+ uint stride = 28;
+ uint offset = vertexIndex * stride;
+
+ float3 pos = asfloat(buffer.Load3(offset));
+ float4 col = asfloat(buffer.Load4(offset + 12));
+
+ // Standard row-major transformation
+ output.Position = mul(ViewProjection, float4(pos, 1.0f));
+ output.Color = col;
+ return output;
+}
diff --git a/Assets/source/RootConstants.hlsl b/Assets/source/RootConstants.hlsl
new file mode 100644
index 0000000..f96e7fa
--- /dev/null
+++ b/Assets/source/RootConstants.hlsl
@@ -0,0 +1,11 @@
+#ifndef ROOT_CONSTANTS_HLSL
+#define ROOT_CONSTANTS_HLSL
+
+cbuffer RootConstants : register(b0, space0)
+{
+ row_major float4x4 ViewProjection;
+ uint BufferIndex;
+ uint _Padding[3];
+};
+
+#endif // ROOT_CONSTANTS_HLSL
diff --git a/Assets/source/Triangle.vert.hlsl b/Assets/source/Triangle.vert.hlsl
index 5d0a814..9fb0cc6 100644
--- a/Assets/source/Triangle.vert.hlsl
+++ b/Assets/source/Triangle.vert.hlsl
@@ -1,34 +1,25 @@
-struct Input
-{
- uint VertexIndex : SV_VertexID;
-};
-
struct Output
{
float4 Color : TEXCOORD0;
float4 Position : SV_Position;
};
-// Bindless storage buffer access
-// We assume DescriptorIndex 0 is our buffer for this example, or passed via push constant
-// Since we don't have push constants hooked up in the C++ simplified example yet,
-// we will assume the First descriptor in the heap is our buffer (Index 0).
-// In a real app, 'bufferIndex' would be passed as a Root Constant.
+#include "RootConstants.hlsl"
-Output main(Input input)
+Output main(uint vertexIndex : SV_VertexID)
{
Output output;
// Retrieve the buffer using SM6.6 bindless syntax
- // heap index 0 is used for simplicity.
- uint bufferIndex = 0;
+ // We use index 0 as the sample app doesn't pass push constants yet.
+ uint bufferIndex = 0;
ByteAddressBuffer buffer = ResourceDescriptorHeap[bufferIndex];
// Read position from buffer (Index * stride)
// Stride = 2 float (pos) + 4 float (color) = 6 * 4 = 24 bytes ?
// Let's assume just position 2D (8 bytes) + Color (16 bytes)
uint stride = 24;
- uint offset = input.VertexIndex * stride;
+ uint offset = vertexIndex * stride;
float2 pos = asfloat(buffer.Load2(offset));
float4 col = asfloat(buffer.Load4(offset + 8));
diff --git a/Juliet/Juliet.vcxproj b/Juliet/Juliet.vcxproj
index 1722a3f..77159bb 100644
--- a/Juliet/Juliet.vcxproj
+++ b/Juliet/Juliet.vcxproj
@@ -53,7 +53,9 @@
+
+
@@ -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
+ )
+ )
)
)