diff --git a/Assets/compiled/Debug.vert.dxil b/Assets/compiled/Debug.vert.dxil index 062cb47..5fdd7a0 100644 Binary files a/Assets/compiled/Debug.vert.dxil and b/Assets/compiled/Debug.vert.dxil differ diff --git a/Assets/compiled/ImGui.frag.dxil b/Assets/compiled/ImGui.frag.dxil index 5b0eb5f..368b0b5 100644 Binary files a/Assets/compiled/ImGui.frag.dxil and b/Assets/compiled/ImGui.frag.dxil differ diff --git a/Assets/compiled/ImGui.vert.dxil b/Assets/compiled/ImGui.vert.dxil index b87161a..e26ed40 100644 Binary files a/Assets/compiled/ImGui.vert.dxil and b/Assets/compiled/ImGui.vert.dxil differ diff --git a/Assets/compiled/Skybox.vert.dxil b/Assets/compiled/Skybox.vert.dxil index 8702269..12ae4fb 100644 Binary files a/Assets/compiled/Skybox.vert.dxil and b/Assets/compiled/Skybox.vert.dxil differ diff --git a/Assets/compiled/SolidColor.frag.dxil b/Assets/compiled/SolidColor.frag.dxil index 3ac12c8..8a78cc9 100644 Binary files a/Assets/compiled/SolidColor.frag.dxil and b/Assets/compiled/SolidColor.frag.dxil differ diff --git a/Assets/compiled/Triangle.vert.dxil b/Assets/compiled/Triangle.vert.dxil index 7b20223..1e2d232 100644 Binary files a/Assets/compiled/Triangle.vert.dxil and b/Assets/compiled/Triangle.vert.dxil differ diff --git a/Assets/source/RootConstants.hlsl b/Assets/source/RootConstants.hlsl index 9945f39..301fcf9 100644 --- a/Assets/source/RootConstants.hlsl +++ b/Assets/source/RootConstants.hlsl @@ -1,5 +1,13 @@ #ifndef ROOT_CONSTANTS_HLSL #define ROOT_CONSTANTS_HLSL + +struct PointLight +{ + float3 Position; + float Radius; + float3 Color; + float Intensity; +}; cbuffer RootConstants : register(b0, space0) { @@ -16,6 +24,9 @@ cbuffer RootConstants : register(b0, space0) float LightPad; float3 LightColor; float AmbientIntensity; + + uint LightBufferIndex; + uint ActiveLightCount; }; diff --git a/Assets/source/SolidColor.frag.hlsl b/Assets/source/SolidColor.frag.hlsl index cb18e2d..aad361b 100644 --- a/Assets/source/SolidColor.frag.hlsl +++ b/Assets/source/SolidColor.frag.hlsl @@ -1,10 +1,46 @@ #include "RootConstants.hlsl" -float4 main(float4 Color : TEXCOORD0, float3 WorldNormal : TEXCOORD1) : SV_Target0 +struct Input { - float3 N = normalize(WorldNormal); - float NdotL = saturate(dot(N, -LightDirection)); - float3 diffuse = Color.rgb * LightColor * NdotL; - float3 ambient = Color.rgb * AmbientIntensity; - return float4(diffuse + ambient, Color.a); + float4 Color : TEXCOORD0; + float3 WorldNormal : TEXCOORD1; + float3 WorldPosition : TEXCOORD2; +}; + +float4 main(Input input) : SV_Target0 +{ + float3 normal = normalize(input.WorldNormal); + + // Initial ambient component + float3 result = input.Color.rgb * LightColor * AmbientIntensity; + + // Directional light contribution + float ndotl = max(dot(normal, -LightDirection), 0.0); + result += input.Color.rgb * LightColor * ndotl; + + // Point lights + if (ActiveLightCount > 0) + { + StructuredBuffer pointLights = ResourceDescriptorHeap[LightBufferIndex]; + + for (uint i = 0; i < ActiveLightCount; ++i) + { + PointLight light = pointLights[i]; + + float3 lightDir = light.Position - input.WorldPosition; + float dist = length(lightDir); + + if (dist < light.Radius) + { + lightDir = normalize(lightDir); + float attenuation = 1.0 - (dist / light.Radius); + attenuation = max(attenuation, 0.0); + + float pndotl = max(dot(normal, lightDir), 0.0); + result += light.Color * input.Color.rgb * pndotl * attenuation * light.Intensity; + } + } + } + + return float4(result, input.Color.a); } diff --git a/Assets/source/Triangle.vert.hlsl b/Assets/source/Triangle.vert.hlsl index 664651e..eb1c49b 100644 --- a/Assets/source/Triangle.vert.hlsl +++ b/Assets/source/Triangle.vert.hlsl @@ -2,6 +2,7 @@ struct Output { float4 Color : TEXCOORD0; float3 WorldNormal : TEXCOORD1; + float3 WorldPosition : TEXCOORD2; float4 Position : SV_Position; }; @@ -20,8 +21,10 @@ Output main(uint vertexIndex : SV_VertexID) float3 normal = asfloat(buffer.Load3(offset + 12)); float4 col = asfloat(buffer.Load4(offset + 24)); - output.Position = mul(ViewProjection, mul(Model, float4(pos, 1.0f))); + float4 worldPos = mul(Model, float4(pos, 1.0f)); + output.Position = mul(ViewProjection, worldPos); output.Color = col; + output.WorldPosition = worldPos.xyz; float3 worldNormal = mul((float3x3)Model, normal); output.WorldNormal = worldNormal; diff --git a/Juliet/include/Core/Container/Vector.h b/Juliet/include/Core/Container/Vector.h index f26909a..f55d76f 100644 --- a/Juliet/include/Core/Container/Vector.h +++ b/Juliet/include/Core/Container/Vector.h @@ -193,11 +193,16 @@ namespace Juliet { Assert(Arena); - ArenaClear(Arena); + if (InternalArena) + { + ArenaClear(Arena); + Data = nullptr; + Capacity = 0; + Reserve(ReserveSize); + } + DataFirst = DataLast = nullptr; - Data = nullptr; Count = 0; - Capacity = 0; } bool IsEmpty() const { return Count == 0; } diff --git a/Juliet/include/Core/Main.h b/Juliet/include/Core/Main.h index 4ae14fd..7bf98ce 100644 --- a/Juliet/include/Core/Main.h +++ b/Juliet/include/Core/Main.h @@ -37,6 +37,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) (void)hPrev; (void)szCmdLine; (void)sw; + return Juliet::Bootstrap(JulietMain, __argc, __wargv); } } diff --git a/Juliet/include/Core/Math/Matrix.h b/Juliet/include/Core/Math/Matrix.h index 38fc07f..4c7d876 100644 --- a/Juliet/include/Core/Math/Matrix.h +++ b/Juliet/include/Core/Math/Matrix.h @@ -81,6 +81,13 @@ namespace Juliet return result; } + inline void MatrixTranslate(Matrix& m, const Vector3& v) + { + m.m[0][3] += v.x; + m.m[1][3] += v.y; + m.m[2][3] += v.z; + } + [[nodiscard]] inline Matrix MatrixRotation(float x, float y, float z) { return MatrixRotationX(x) * MatrixRotationY(y) * MatrixRotationZ(z); diff --git a/Juliet/include/Graphics/Graphics.h b/Juliet/include/Graphics/Graphics.h index 0f0fed6..8be9a04 100644 --- a/Juliet/include/Graphics/Graphics.h +++ b/Juliet/include/Graphics/Graphics.h @@ -164,6 +164,8 @@ namespace Juliet extern JULIET_API GraphicsBuffer* CreateGraphicsBuffer(NonNullPtr device, const BufferCreateInfo& createInfo); extern JULIET_API GraphicsTransferBuffer* CreateGraphicsTransferBuffer(NonNullPtr device, const TransferBufferCreateInfo& createInfo); + extern JULIET_API void* MapGraphicsBuffer(NonNullPtr device, NonNullPtr buffer); + extern JULIET_API void UnmapGraphicsBuffer(NonNullPtr device, NonNullPtr buffer); extern JULIET_API void* MapGraphicsTransferBuffer(NonNullPtr device, NonNullPtr buffer); extern JULIET_API void UnmapGraphicsTransferBuffer(NonNullPtr device, NonNullPtr buffer); extern JULIET_API void CopyBuffer(NonNullPtr commandList, NonNullPtr dst, diff --git a/Juliet/include/Graphics/GraphicsBuffer.h b/Juliet/include/Graphics/GraphicsBuffer.h index b3d15fe..2c719b1 100644 --- a/Juliet/include/Graphics/GraphicsBuffer.h +++ b/Juliet/include/Graphics/GraphicsBuffer.h @@ -8,7 +8,6 @@ namespace Juliet IndexBuffer = 1 << 0, ConstantBuffer = 1 << 1, StructuredBuffer = 1 << 2, - VertexBuffer = 1 << 3, }; enum class TransferBufferUsage : uint8 @@ -20,7 +19,9 @@ namespace Juliet struct BufferCreateInfo { size_t Size; + size_t Stride; BufferUsage Usage; + bool IsDynamic; }; struct TransferBufferCreateInfo diff --git a/Juliet/include/Graphics/Lighting.h b/Juliet/include/Graphics/Lighting.h new file mode 100644 index 0000000..ef44c9e --- /dev/null +++ b/Juliet/include/Graphics/Lighting.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace Juliet +{ + struct PointLight + { + Vector3 Position; + float Radius; + Vector3 Color; + float Intensity; + }; +} // namespace Juliet diff --git a/Juliet/include/Graphics/MeshRenderer.h b/Juliet/include/Graphics/MeshRenderer.h index 81b5683..347482f 100644 --- a/Juliet/include/Graphics/MeshRenderer.h +++ b/Juliet/include/Graphics/MeshRenderer.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -17,13 +18,15 @@ namespace Juliet struct GraphicsDevice; struct Mesh; - using MeshID = index_t; + using MeshID = index_t; + using LightID = index_t; constexpr size_t kGeometryPage = Megabytes(64); constexpr size_t kIndexPage = Megabytes(32); constexpr size_t kDefaultMeshNumber = 500; constexpr size_t kDefaultVertexCount = 2'000'000; // Fit less than one geometry page constexpr size_t kDefaultIndexCount = 16'000'000; // Fit less than one index page + constexpr size_t kDefaultLightCount = 1024; struct MeshRenderer { @@ -31,12 +34,16 @@ namespace Juliet VectorArena Meshes; VectorArena Vertices; VectorArena Indices; + VectorArena PointLights; GraphicsBuffer* VertexBuffer; GraphicsBuffer* IndexBuffer; GraphicsTransferBuffer* StreamCopyBuffer; GraphicsTransferBuffer* LoadCopyBuffer; + GraphicsBuffer* LightsBuffer; + PointLight* MappedLights; + GraphicsDevice* Device; GraphicsPipeline* Pipeline; }; @@ -48,6 +55,12 @@ namespace Juliet JULIET_API void LoadMeshesOnGPU(NonNullPtr cmdList); JULIET_API void RenderMeshes(NonNullPtr pass, NonNullPtr cmdList, PushData& pushData); + // Lights + [[nodiscard]] JULIET_API LightID AddPointLight(const PointLight& light); + JULIET_API void SetPointLightPosition(LightID id, const Vector3& position); + JULIET_API void SetPointLightColor(LightID id, const Vector3& color); + JULIET_API void ClearPointLights(); + // Utils [[nodiscard]] JULIET_API MeshID AddCube(); [[nodiscard]] JULIET_API MeshID AddQuad(); diff --git a/Juliet/include/Graphics/PushConstants.h b/Juliet/include/Graphics/PushConstants.h index 37a53bd..5a3fa04 100644 --- a/Juliet/include/Graphics/PushConstants.h +++ b/Juliet/include/Graphics/PushConstants.h @@ -21,5 +21,8 @@ namespace Juliet float LightPad; Vector3 LightColor; float AmbientIntensity; + + uint32 LightBufferIndex; + uint32 ActiveLightCount; }; } // namespace Juliet diff --git a/Juliet/src/Core/HAL/OS/Win32/Win32OS.cpp b/Juliet/src/Core/HAL/OS/Win32/Win32OS.cpp index d20547c..1e94f25 100644 --- a/Juliet/src/Core/HAL/OS/Win32/Win32OS.cpp +++ b/Juliet/src/Core/HAL/OS/Win32/Win32OS.cpp @@ -17,7 +17,7 @@ namespace Juliet { Byte* OS_Reserve(size_t size) { - auto result = static_cast(VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE)); + auto* result = static_cast(VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE)); return result; } @@ -68,8 +68,6 @@ namespace Juliet { int OS_Main(EntryPointFunc entryPointFunc, int argc, wchar_t** argv) { - (void)argc; - (void)argv; SetUnhandledExceptionFilter(&ExceptionFilter); // Allow only one instance to be launched. @@ -87,12 +85,8 @@ namespace Juliet GUID guid = WSAID_MULTIPLE_RIO; DWORD rio_byte = 0; SOCKET Sock = socket(AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP); - int result = WSAIoctl(Sock, SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), - (void**)&w32_rio_functions, sizeof(w32_rio_functions), &rio_byte, nullptr, nullptr); - if (result != 0) - { - LogError(LogCategory::Core, "Couldnt get w32 RIO Functions. Error code %d", result); - } + WSAIoctl(Sock, SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), + reinterpret_cast(&w32_rio_functions), sizeof(w32_rio_functions), &rio_byte, nullptr, nullptr); closesocket(Sock); } diff --git a/Juliet/src/Graphics/D3D12/D3D12Buffer.cpp b/Juliet/src/Graphics/D3D12/D3D12Buffer.cpp index 71adce5..bd0c077 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Buffer.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12Buffer.cpp @@ -38,7 +38,6 @@ namespace Juliet::D3D12 case BufferUsage::ConstantBuffer: return "ConstantBuffer"; case BufferUsage::StructuredBuffer: return "StructuredBuffer"; case BufferUsage::IndexBuffer: return "IndexBuffer"; - case BufferUsage::VertexBuffer: return "VertexBuffer"; } return "Unknown"; } @@ -62,95 +61,62 @@ namespace Juliet::D3D12 Free(buffer); } - D3D12Buffer* CreateBuffer(NonNullPtr d3d12Driver, size_t size, BufferUsage usage, D3D12BufferType type) + D3D12Buffer* CreateBuffer(NonNullPtr d3d12Driver, size_t size, size_t stride, BufferUsage usage, + D3D12BufferType type, bool isDynamic) { - auto buffer = static_cast(Calloc(1, sizeof(D3D12Buffer))); + auto* buffer = static_cast(Calloc(1, sizeof(D3D12Buffer))); if (!buffer) { return nullptr; } + if (type == D3D12BufferType::Base && usage == BufferUsage::None) + { + Assert(false, "Creating Base buffer with BufferUsage::None is invalid"); + DestroyBuffer(buffer); + return nullptr; + } + // Align size for Constant Buffers if (usage == BufferUsage::ConstantBuffer) { size = (size + 255U) & ~255U; } - D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON; D3D12_HEAP_PROPERTIES heapProperties = {}; - D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - switch (type) + D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON; + D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE; + + // Constant buffers or Dynamic buffers generally need to be uploaded every frame + const bool isUpload = isDynamic || (type == D3D12BufferType::TransferUpload) || (usage == BufferUsage::ConstantBuffer); + + if (type == D3D12BufferType::TransferDownload) { - case D3D12BufferType::Base: + heapProperties.Type = D3D12_HEAP_TYPE_READBACK; + initialState = D3D12_RESOURCE_STATE_COPY_DEST; + } + else if (isUpload) + { + if (d3d12Driver->GPUUploadHeapSupported) { - switch (usage) - { - case BufferUsage::None: - { - Assert(false, "Creating buffer with invalid usage"); - DestroyBuffer(buffer); - return nullptr; - } - - case BufferUsage::IndexBuffer: - case BufferUsage::StructuredBuffer: - case BufferUsage::VertexBuffer: - { - heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; - heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapFlags = D3D12_HEAP_FLAG_NONE; - break; - } - case BufferUsage::ConstantBuffer: - { - if (d3d12Driver->GPUUploadHeapSupported) - { - heapProperties.Type = D3D12_HEAP_TYPE_GPU_UPLOAD; - } - else - { - heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; - heapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS; - initialState = D3D12_RESOURCE_STATE_GENERIC_READ; - } - heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - - break; - } - } - break; + heapProperties.Type = D3D12_HEAP_TYPE_GPU_UPLOAD; + initialState = D3D12_RESOURCE_STATE_COMMON; } - case D3D12BufferType::TransferDownload: + else { - heapProperties.Type = D3D12_HEAP_TYPE_READBACK; - heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - heapFlags = D3D12_HEAP_FLAG_NONE; - initialState = D3D12_RESOURCE_STATE_COPY_DEST; - break; - } - case D3D12BufferType::TransferUpload: - { - if (d3d12Driver->GPUUploadHeapSupported) - { - heapProperties.Type = D3D12_HEAP_TYPE_GPU_UPLOAD; - } - else - { - heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; - heapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS; - initialState = D3D12_RESOURCE_STATE_GENERIC_READ; - } - heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - break; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + initialState = D3D12_RESOURCE_STATE_GENERIC_READ; } } - - D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {}; + else + { + // Must be a static buffer (Base type) + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + initialState = D3D12_RESOURCE_STATE_COMMON; + } D3D12_RESOURCE_DESC desc = {}; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; @@ -167,20 +133,22 @@ namespace Juliet::D3D12 Log(LogLevel::Message, LogCategory::Graphics, "CreateBuffer: Device=%p, Size=%zu, Type=%s Use=%s", (void*)d3d12Driver->D3D12Device, size, D3D12BufferTypeToString(type), BufferUsageToString(usage)); + ID3D12Resource* handle = nullptr; HRESULT result = d3d12Driver->D3D12Device->CreateCommittedResource(&heapProperties, heapFlags, &desc, initialState, nullptr, IID_ID3D12Resource, reinterpret_cast(&handle)); + if (FAILED(result)) { - Log(LogLevel::Error, LogCategory::Graphics, "Could not create buffer! HRESULT=0x%08X", (uint32)result); + Log(LogLevel::Error, LogCategory::Graphics, "Could not create buffer! HRESULT=0x%08X", static_cast(result)); Log(LogLevel::Error, LogCategory::Graphics, "Failed Desc: Width=%llu Layout=%d HeapType=%d", (unsigned long long)desc.Width, (int)desc.Layout, (int)heapProperties.Type); HRESULT removeReason = d3d12Driver->D3D12Device->GetDeviceRemovedReason(); if (FAILED(removeReason)) { - Log(LogLevel::Error, LogCategory::Graphics, "Device Removed Reason: 0x%08X", (uint32)removeReason); + Log(LogLevel::Error, LogCategory::Graphics, "Device Removed Reason: 0x%08X", static_cast(removeReason)); } DestroyBuffer(buffer); @@ -205,20 +173,33 @@ namespace Juliet::D3D12 if (usage == BufferUsage::ConstantBuffer) { - cbvDesc.BufferLocation = handle->GetGPUVirtualAddress(); - cbvDesc.SizeInBytes = static_cast(size); + D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {}; + cbvDesc.BufferLocation = handle->GetGPUVirtualAddress(); + cbvDesc.SizeInBytes = static_cast(size); d3d12Driver->D3D12Device->CreateConstantBufferView(&cbvDesc, cpuHandle); } else if (usage == BufferUsage::StructuredBuffer) { D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; - srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; srvDesc.Buffer.FirstElement = 0; - srvDesc.Buffer.NumElements = static_cast(size / 4); - srvDesc.Buffer.StructureByteStride = 0; - srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; + + if (stride > 0) + { + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.Buffer.NumElements = static_cast(size / stride); + srvDesc.Buffer.StructureByteStride = static_cast(stride); + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + } + else + { + srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; + srvDesc.Buffer.NumElements = static_cast(size / 4); + srvDesc.Buffer.StructureByteStride = 0; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; + } + d3d12Driver->D3D12Device->CreateShaderResourceView(handle, &srvDesc, cpuHandle); Log(LogLevel::Message, LogCategory::Graphics, " -> SRV DescriptorIndex=%u", descriptor.Index); } @@ -233,10 +214,10 @@ namespace Juliet::D3D12 } } // namespace - GraphicsBuffer* CreateGraphicsBuffer(NonNullPtr driver, size_t size, BufferUsage usage) + GraphicsBuffer* CreateGraphicsBuffer(NonNullPtr driver, size_t size, size_t stride, BufferUsage usage, bool isDynamic) { auto d3d12Driver = static_cast(driver.Get()); - return reinterpret_cast(CreateBuffer(d3d12Driver, size, usage, D3D12BufferType::Base)); + return reinterpret_cast(CreateBuffer(d3d12Driver, size, stride, usage, D3D12BufferType::Base, isDynamic)); } void DestroyGraphicsBuffer(NonNullPtr buffer) @@ -248,8 +229,9 @@ namespace Juliet::D3D12 { auto d3d12Driver = static_cast(driver.Get()); return reinterpret_cast( - CreateBuffer(d3d12Driver, size, BufferUsage::None, - usage == TransferBufferUsage::Upload ? D3D12BufferType::TransferUpload : D3D12BufferType::TransferDownload)); + CreateBuffer(d3d12Driver, size, 0, BufferUsage::None, + usage == TransferBufferUsage::Upload ? D3D12BufferType::TransferUpload : D3D12BufferType::TransferDownload, + false)); } void DestroyGraphicsTransferBuffer(NonNullPtr buffer) @@ -276,6 +258,24 @@ namespace Juliet::D3D12 d3d12Buffer->Handle->Unmap(0, nullptr); } + void* MapBuffer(NonNullPtr /*driver*/, NonNullPtr buffer) + { + auto d3d12Buffer = reinterpret_cast(buffer.Get()); + void* ptr = nullptr; + D3D12_RANGE readRange = { 0, 0 }; + if (FAILED(d3d12Buffer->Handle->Map(0, &readRange, &ptr))) + { + return nullptr; + } + return ptr; + } + + void UnmapBuffer(NonNullPtr /*driver*/, NonNullPtr buffer) + { + auto d3d12Buffer = reinterpret_cast(buffer.Get()); + d3d12Buffer->Handle->Unmap(0, nullptr); + } + uint32 GetDescriptorIndex(NonNullPtr /*driver*/, NonNullPtr buffer) { auto d3d12Buffer = reinterpret_cast(buffer.Get()); diff --git a/Juliet/src/Graphics/D3D12/D3D12Buffer.h b/Juliet/src/Graphics/D3D12/D3D12Buffer.h index d132dfc..7480b63 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Buffer.h +++ b/Juliet/src/Graphics/D3D12/D3D12Buffer.h @@ -20,7 +20,7 @@ namespace Juliet::D3D12 size_t Size; }; - extern GraphicsBuffer* CreateGraphicsBuffer(NonNullPtr driver, size_t size, BufferUsage usage); + extern GraphicsBuffer* CreateGraphicsBuffer(NonNullPtr driver, size_t size, size_t stride, BufferUsage usage, bool isDynamic); extern void DestroyGraphicsBuffer(NonNullPtr buffer); extern GraphicsTransferBuffer* CreateGraphicsTransferBuffer(NonNullPtr driver, size_t size, TransferBufferUsage usage); diff --git a/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp index 17698a8..e24366a 100644 --- a/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12GraphicsDevice.cpp @@ -1032,8 +1032,10 @@ namespace Juliet::D3D12 device->DestroyShader = DestroyShader; device->CreateGraphicsPipeline = CreateGraphicsPipeline; device->DestroyGraphicsPipeline = DestroyGraphicsPipeline; - device->CreateGraphicsBuffer = CreateGraphicsBuffer; - device->DestroyGraphicsBuffer = DestroyGraphicsBuffer; + device->CreateGraphicsBuffer = CreateGraphicsBuffer; + device->DestroyGraphicsBuffer = DestroyGraphicsBuffer; + device->MapGraphicsBuffer = MapBuffer; + device->UnmapGraphicsBuffer = UnmapBuffer; device->CreateGraphicsTransferBuffer = CreateGraphicsTransferBuffer; device->DestroyGraphicsTransferBuffer = DestroyGraphicsTransferBuffer; device->MapGraphicsTransferBuffer = MapBuffer; diff --git a/Juliet/src/Graphics/Graphics.cpp b/Juliet/src/Graphics/Graphics.cpp index 5fec4fa..c484b4c 100644 --- a/Juliet/src/Graphics/Graphics.cpp +++ b/Juliet/src/Graphics/Graphics.cpp @@ -371,7 +371,7 @@ namespace Juliet GraphicsBuffer* CreateGraphicsBuffer(NonNullPtr device, const BufferCreateInfo& createInfo) { - return device->CreateGraphicsBuffer(device->Driver, createInfo.Size, createInfo.Usage); + return device->CreateGraphicsBuffer(device->Driver, createInfo.Size, createInfo.Stride, createInfo.Usage, createInfo.IsDynamic); } GraphicsTransferBuffer* CreateGraphicsTransferBuffer(NonNullPtr device, const TransferBufferCreateInfo& createInfo) @@ -379,6 +379,16 @@ namespace Juliet return device->CreateGraphicsTransferBuffer(device->Driver, createInfo.Size, createInfo.Usage); } + void* MapGraphicsBuffer(NonNullPtr device, NonNullPtr buffer) + { + return device->MapGraphicsBuffer(device->Driver, buffer); + } + + void UnmapGraphicsBuffer(NonNullPtr device, NonNullPtr buffer) + { + device->UnmapGraphicsBuffer(device->Driver, buffer); + } + void* MapGraphicsTransferBuffer(NonNullPtr device, NonNullPtr buffer) { return device->MapGraphicsTransferBuffer(device->Driver, buffer); diff --git a/Juliet/src/Graphics/GraphicsDevice.h b/Juliet/src/Graphics/GraphicsDevice.h index b740855..fce65c2 100644 --- a/Juliet/src/Graphics/GraphicsDevice.h +++ b/Juliet/src/Graphics/GraphicsDevice.h @@ -100,9 +100,12 @@ namespace Juliet void (*DestroyTexture)(NonNullPtr driver, NonNullPtr texture); // Buffers - GraphicsBuffer* (*CreateGraphicsBuffer)(NonNullPtr driver, size_t size, BufferUsage usage); + GraphicsBuffer* (*CreateGraphicsBuffer)(NonNullPtr driver, size_t size, size_t stride, BufferUsage usage, bool isDynamic); void (*DestroyGraphicsBuffer)(NonNullPtr buffer); + void* (*MapGraphicsBuffer)(NonNullPtr driver, NonNullPtr buffer); + void (*UnmapGraphicsBuffer)(NonNullPtr driver, NonNullPtr buffer); + GraphicsTransferBuffer* (*CreateGraphicsTransferBuffer)(NonNullPtr driver, size_t size, TransferBufferUsage usage); void (*DestroyGraphicsTransferBuffer)(NonNullPtr buffer); diff --git a/Juliet/src/Graphics/ImGuiRenderer.cpp b/Juliet/src/Graphics/ImGuiRenderer.cpp index 0d19b7d..17a6559 100644 --- a/Juliet/src/Graphics/ImGuiRenderer.cpp +++ b/Juliet/src/Graphics/ImGuiRenderer.cpp @@ -150,8 +150,8 @@ namespace Juliet TextureCreateInfo texCI = {}; texCI.Type = TextureType::Texture_2D; - texCI.Width = (uint32)width; - texCI.Height = (uint32)height; + texCI.Width = static_cast(width); + texCI.Height = static_cast(height); texCI.Format = TextureFormat::R8G8B8A8_UNORM; texCI.Flags = TextureUsageFlag::Sampler; @@ -160,10 +160,10 @@ namespace Juliet texCI.SampleCount = TextureSampleCount::One; g_ImGuiState.FontTexture = CreateTexture(device, texCI); - io.Fonts->SetTexID((ImTextureID)g_ImGuiState.FontTexture); + io.Fonts->SetTexID(reinterpret_cast(g_ImGuiState.FontTexture)); // Upload data - uint32 rowPitch = (uint32)width * 4u; + uint32 rowPitch = static_cast(width) * 4u; uint32 alignedRowPitch = (rowPitch + 255u) & ~255u; uint32 textureSize = alignedRowPitch * static_cast(height); @@ -177,7 +177,7 @@ namespace Juliet return false; } - auto dst = (uint8*)MapGraphicsTransferBuffer(device, tb); + auto* dst = static_cast(MapGraphicsTransferBuffer(device, tb)); for (uint32 y = 0; y < static_cast(height); ++y) { @@ -315,13 +315,13 @@ namespace Juliet FrameResources& currentFrame = g_ImGuiState.Frames[g_ImGuiState.FrameIndex]; // Upload Buffers - uint32 totalVtx = (uint32)drawData->TotalVtxCount; - uint32 totalIdx = (uint32)drawData->TotalIdxCount; + uint32 totalVtx = static_cast(drawData->TotalVtxCount); + uint32 totalIdx = static_cast(drawData->TotalIdxCount); EnsureBufferSize(currentFrame, totalVtx * sizeof(ImDrawVert), totalIdx * sizeof(ImDrawIdx)); - auto vtxDst = (ImDrawVert*)MapGraphicsTransferBuffer(g_ImGuiState.Device, currentFrame.VertexUpload); - auto idxDst = (ImDrawIdx*)MapGraphicsTransferBuffer(g_ImGuiState.Device, currentFrame.IndexUpload); + auto* vtxDst = static_cast(MapGraphicsTransferBuffer(g_ImGuiState.Device, currentFrame.VertexUpload)); + auto* idxDst = static_cast(MapGraphicsTransferBuffer(g_ImGuiState.Device, currentFrame.IndexUpload)); for (int n = 0; n < drawData->CmdListsCount; n++) { @@ -403,28 +403,18 @@ namespace Juliet SetScissorRect(renderPass, scissorRect); // Bind Texture - uint32 textureIndex = GetDescriptorIndex(g_ImGuiState.Device, (Texture*)pcmd->GetTexID()); + uint32 textureIndex = GetDescriptorIndex(g_ImGuiState.Device, reinterpret_cast(pcmd->GetTexID())); // Push Constants - // Layout: ViewProjection(64) + BufferIndex(4) + TextureIndex(4) + VertexOffset(4) + Padding(4) + Scale(8) + Translate(8) - struct - { - float dummyVP[16]; // Occupy VP slot - uint32 bufferIndex; - uint32 textureIndex; - uint32 vertexOffset; // Base vertex for indexed bindless drawing - uint32 padding; // Alignment padding - float scale[2]; - float translate[2]; - } pushData = {}; // Zero-initialize all fields + PushData pushData = {}; // Zero-initialize all fields - pushData.bufferIndex = GetDescriptorIndex(g_ImGuiState.Device, currentFrame.VertexBuffer); - pushData.textureIndex = textureIndex; - pushData.vertexOffset = pcmd->VtxOffset + globalVtxOffset; // Pass vertex offset for bindless - pushData.scale[0] = scale[0]; - pushData.scale[1] = scale[1]; - pushData.translate[0] = translate[0]; - pushData.translate[1] = translate[1]; + pushData.BufferIndex = GetDescriptorIndex(g_ImGuiState.Device, currentFrame.VertexBuffer); + pushData.TextureIndex = textureIndex; + pushData.VertexOffset = pcmd->VtxOffset + globalVtxOffset; // Pass vertex offset for bindless + pushData.Scale[0] = scale[0]; + pushData.Scale[1] = scale[1]; + pushData.Translate[0] = translate[0]; + pushData.Translate[1] = translate[1]; SetPushConstants(cmdList, ShaderStage::Vertex, 0, sizeof(pushData) / 4, &pushData); diff --git a/Juliet/src/Graphics/MeshRenderer.cpp b/Juliet/src/Graphics/MeshRenderer.cpp index 965ec56..2aa73ee 100644 --- a/Juliet/src/Graphics/MeshRenderer.cpp +++ b/Juliet/src/Graphics/MeshRenderer.cpp @@ -20,6 +20,7 @@ namespace Juliet g_MeshRenderer.Meshes.Create(arena JULIET_DEBUG_PARAM("Meshes")); g_MeshRenderer.Vertices.Create(arena JULIET_DEBUG_PARAM("Vertices")); g_MeshRenderer.Indices.Create(arena JULIET_DEBUG_PARAM("Indices")); + g_MeshRenderer.PointLights.Create(arena JULIET_DEBUG_PARAM("PointLights")); } bool InitializeMeshRendererGraphics(NonNullPtr device, NonNullPtr window) @@ -53,6 +54,8 @@ namespace Juliet .DepthStencilFormat = TextureFormat::D32_FLOAT, .HasDepthStencilTarget = true }; pipelineCI.RasterizerState.FillMode = FillMode::Solid; + pipelineCI.RasterizerState.CullMode = CullMode::None; + pipelineCI.RasterizerState.FrontFace = FrontFace::Clockwise; pipelineCI.DepthStencilState.EnableDepthTest = true; pipelineCI.DepthStencilState.EnableDepthWrite = true; pipelineCI.DepthStencilState.CompareOperation = CompareOperation::Less; @@ -67,13 +70,33 @@ namespace Juliet // Create the vertex and index buffers BufferCreateInfo vertexBufferCI = {}; vertexBufferCI.Size = kGeometryPage; + vertexBufferCI.Stride = 0; vertexBufferCI.Usage = BufferUsage::StructuredBuffer; g_MeshRenderer.VertexBuffer = CreateGraphicsBuffer(graphicsDevice, vertexBufferCI); + Assert(g_MeshRenderer.VertexBuffer != nullptr); BufferCreateInfo indexBufferCI = {}; indexBufferCI.Size = kIndexPage; indexBufferCI.Usage = BufferUsage::IndexBuffer; g_MeshRenderer.IndexBuffer = CreateGraphicsBuffer(graphicsDevice, indexBufferCI); + Assert(g_MeshRenderer.IndexBuffer != nullptr); + + // Lights Buffer + BufferCreateInfo lightsBufferCI = {}; + lightsBufferCI.Size = 1024 * sizeof(PointLight); // Max 1024 lights for now + lightsBufferCI.Stride = sizeof(PointLight); + lightsBufferCI.Usage = BufferUsage::StructuredBuffer; + lightsBufferCI.IsDynamic = true; + g_MeshRenderer.LightsBuffer = CreateGraphicsBuffer(graphicsDevice, lightsBufferCI); + Assert(g_MeshRenderer.LightsBuffer != nullptr); + g_MeshRenderer.MappedLights = static_cast(MapGraphicsBuffer(graphicsDevice, g_MeshRenderer.LightsBuffer)); + Assert(g_MeshRenderer.MappedLights != nullptr); + + // Sync existing lights that might have been added before graphics initialization + for (index_t i = 0; i < g_MeshRenderer.PointLights.Count; ++i) + { + g_MeshRenderer.MappedLights[i] = g_MeshRenderer.PointLights.Data[i]; + } if (vertexShader) { @@ -88,7 +111,6 @@ namespace Juliet CommandList* loadCmd = AcquireCommandList(device); LoadMeshesOnGPU(loadCmd); SubmitCommandLists(loadCmd); - return result; } @@ -103,6 +125,12 @@ namespace Juliet DestroyGraphicsBuffer(g_MeshRenderer.Device, g_MeshRenderer.IndexBuffer); DestroyGraphicsBuffer(g_MeshRenderer.Device, g_MeshRenderer.VertexBuffer); + + if (g_MeshRenderer.LightsBuffer) + { + UnmapGraphicsBuffer(g_MeshRenderer.Device, g_MeshRenderer.LightsBuffer); + DestroyGraphicsBuffer(g_MeshRenderer.Device, g_MeshRenderer.LightsBuffer); + } } void ShutdownMeshRenderer() @@ -110,6 +138,7 @@ namespace Juliet g_MeshRenderer.Indices.Destroy(); g_MeshRenderer.Vertices.Destroy(); g_MeshRenderer.Meshes.Destroy(); + g_MeshRenderer.PointLights.Destroy(); } void LoadMeshesOnGPU(NonNullPtr cmdList) @@ -159,6 +188,8 @@ namespace Juliet MemCopy(ptrOneByte + indexOfByteOffset, g_MeshRenderer.Indices.Data, totalIndexBytes); + UnmapGraphicsTransferBuffer(g_MeshRenderer.Device, g_MeshRenderer.LoadCopyBuffer); + CopyBuffer(cmdList, g_MeshRenderer.VertexBuffer, g_MeshRenderer.LoadCopyBuffer, totalVertexBytes, 0, 0); CopyBuffer(cmdList, g_MeshRenderer.IndexBuffer, g_MeshRenderer.LoadCopyBuffer, totalIndexBytes, 0, indexOfByteOffset); @@ -181,8 +212,11 @@ namespace Juliet BindGraphicsPipeline(pass, g_MeshRenderer.Pipeline); uint32 vertexDescriptorIndex = GetDescriptorIndex(g_MeshRenderer.Device, g_MeshRenderer.VertexBuffer); + uint32 lightDescriptorIndex = GetDescriptorIndex(g_MeshRenderer.Device, g_MeshRenderer.LightsBuffer); - pushData.BufferIndex = vertexDescriptorIndex; + pushData.BufferIndex = vertexDescriptorIndex; + pushData.LightBufferIndex = lightDescriptorIndex; + pushData.ActiveLightCount = static_cast(g_MeshRenderer.PointLights.Count); SetIndexBuffer(cmdList, g_MeshRenderer.IndexBuffer, IndexFormat::UInt16, g_MeshRenderer.Indices.Count, 0); @@ -202,6 +236,43 @@ namespace Juliet } } + LightID AddPointLight(const PointLight& light) + { + Assert(g_MeshRenderer.PointLights.Count < kDefaultLightCount); + LightID id = g_MeshRenderer.PointLights.Count; + g_MeshRenderer.PointLights.PushBack(light); + if (g_MeshRenderer.MappedLights) + { + g_MeshRenderer.MappedLights[id] = light; + } + return id; + } + + void SetPointLightPosition(LightID id, const Vector3& position) + { + Assert(id < static_cast(g_MeshRenderer.PointLights.Count)); + g_MeshRenderer.PointLights.Data[id].Position = position; + if (g_MeshRenderer.MappedLights) + { + g_MeshRenderer.MappedLights[id].Position = position; + } + } + + void SetPointLightColor(LightID id, const Vector3& color) + { + Assert(id < static_cast(g_MeshRenderer.PointLights.Count)); + g_MeshRenderer.PointLights.Data[id].Color = color; + if (g_MeshRenderer.MappedLights) + { + g_MeshRenderer.MappedLights[id].Color = color; + } + } + + void ClearPointLights() + { + g_MeshRenderer.PointLights.Clear(); + } + MeshID AddCube() { Mesh result = {}; @@ -300,6 +371,7 @@ namespace Juliet Assert(id < static_cast(g_MeshRenderer.Meshes.Count)); g_MeshRenderer.Meshes.Data[id].Transform = transform; } + #if ALLOW_SHADER_HOT_RELOAD void ReloadMeshRendererShaders() { diff --git a/JulietApp/main.cpp b/JulietApp/main.cpp index 5b63356..8eec4a9 100644 --- a/JulietApp/main.cpp +++ b/JulietApp/main.cpp @@ -1,5 +1,12 @@ #include "main.h" +#ifdef global +#undef global +#endif +#include +#include +#define global static + #include #include #include @@ -44,13 +51,6 @@ static bool ShowMemoryDebugger = false; using namespace Juliet; -extern "C" { -__declspec(dllexport) extern const unsigned int D3D12SDKVersion = 615; -} -extern "C" { -__declspec(dllexport) extern const char* D3D12SDKPath = ".\\"; -} - namespace { using GameInit_t = void (*)(GameInitParams*); @@ -64,6 +64,9 @@ namespace } Game; const char* GameFunctionTable[] = { "GameInit", "GameShutdown", "GameUpdate" }; + + LightID RedLightID = 0; + LightID BlueLightID = 0; } // namespace void JulietApplication::Init(NonNullPtr) @@ -75,6 +78,7 @@ void JulietApplication::Init(NonNullPtr) #if JULIET_DEBUG config.EnableDebug = true; #endif + GraphicsDevice = CreateGraphicsDevice(config); MainWindow = CreatePlatformWindow("Juliet Editor", 1280, 720); @@ -119,6 +123,21 @@ void JulietApplication::Init(NonNullPtr) SetMeshTransform(cube, MatrixTranslation(x, y, 0.0f) * rotation); } } + + // Start with some default test lights + PointLight redLight = {}; + redLight.Position = { 5.0f, 5.0f, 2.0f }; + redLight.Radius = 10.0f; + redLight.Color = { 1.0f, 0.2f, 0.2f }; + redLight.Intensity = 5.0f; + RedLightID = AddPointLight(redLight); + + PointLight blueLight = {}; + blueLight.Position = { -5.0f, 0.0f, 2.0f }; + blueLight.Radius = 15.0f; + blueLight.Color = { 0.2f, 0.2f, 1.0f }; + blueLight.Intensity = 8.0f; + BlueLightID = AddPointLight(blueLight); } GameCode.Functions = reinterpret_cast(&Game); @@ -232,14 +251,23 @@ void JulietApplication::Update() } } + ArenaClear(GameScratchArena); + + Vector3 redLightPos{ cosf(CameraTime * 2.0f) * 5.0f, sinf(CameraTime * 2.0f) * 5.0f, 2.0f }; + SetPointLightPosition(RedLightID, redLightPos); + Vector3 blueLightPos{ -5.0f, cosf(CameraTime) * 3.0f, 2.0f }; + SetPointLightPosition(BlueLightID, blueLightPos); + #ifdef JULIET_ENABLE_IMGUI - // ImGui::ShowDemoWindow(); + ImGui::ShowDemoWindow(); #endif DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f }, false); DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, true); DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, true); DebugDisplay_DrawSphere({ 0.0f, 0.0f, 0.0f }, 0.5f, { 1.0f, 1.0f, 0.0f, 1.0f }, true); + DebugDisplay_DrawSphere(blueLightPos, 0.5f, { 0.0f, 0.0f, 1.0f, 1.0f }, true); + DebugDisplay_DrawSphere(redLightPos, 0.5f, { 1.0f, 0.0f, 0.0f, 1.0f }, true); Game.Update(0.0f);