From 3277624ec299f5523330cea67cf2ebaa6b425b4b Mon Sep 17 00:00:00 2001 From: Patedam Date: Mon, 23 Feb 2026 19:24:57 -0500 Subject: [PATCH] Support lighting using a graphics buffer that is kep open! --- Assets/compiled/Debug.vert.dxil | Bin 4964 -> 5032 bytes Assets/compiled/ImGui.frag.dxil | Bin 4472 -> 4544 bytes Assets/compiled/ImGui.vert.dxil | Bin 4912 -> 4976 bytes Assets/compiled/Skybox.vert.dxil | Bin 5304 -> 5368 bytes Assets/compiled/SolidColor.frag.dxil | Bin 4420 -> 5684 bytes Assets/compiled/Triangle.vert.dxil | Bin 5532 -> 5748 bytes Assets/source/RootConstants.hlsl | 11 ++ Assets/source/SolidColor.frag.hlsl | 48 ++++- Assets/source/Triangle.vert.hlsl | 5 +- Juliet/include/Core/Container/Vector.h | 11 +- Juliet/include/Core/Main.h | 1 + Juliet/include/Core/Math/Matrix.h | 7 + Juliet/include/Graphics/Graphics.h | 2 + Juliet/include/Graphics/GraphicsBuffer.h | 3 +- Juliet/include/Graphics/Lighting.h | 15 ++ Juliet/include/Graphics/MeshRenderer.h | 15 +- Juliet/include/Graphics/PushConstants.h | 3 + Juliet/src/Core/HAL/OS/Win32/Win32OS.cpp | 12 +- Juliet/src/Graphics/D3D12/D3D12Buffer.cpp | 164 +++++++++--------- Juliet/src/Graphics/D3D12/D3D12Buffer.h | 2 +- .../Graphics/D3D12/D3D12GraphicsDevice.cpp | 6 +- Juliet/src/Graphics/Graphics.cpp | 12 +- Juliet/src/Graphics/GraphicsDevice.h | 5 +- Juliet/src/Graphics/ImGuiRenderer.cpp | 46 ++--- Juliet/src/Graphics/MeshRenderer.cpp | 76 +++++++- JulietApp/main.cpp | 44 ++++- 26 files changed, 342 insertions(+), 146 deletions(-) create mode 100644 Juliet/include/Graphics/Lighting.h diff --git a/Assets/compiled/Debug.vert.dxil b/Assets/compiled/Debug.vert.dxil index 062cb479cb2ce770e1e3c3b49c0f29401add96b9..5fdd7a03daafa9e187f6e6608ac9e804c2f52bd9 100644 GIT binary patch delta 773 zcmaE&wnAOfCBn(s|A>m;pEt#4g}21bZO`|(!^ptEuwtU5H0u;D28InAEsGgdpKvfR zq%kluSTZp%xI}pRumRNw0F|8qiaR+UxTna&z+kZXBI6=P;S+*K6D8UTRro(iuxS-1 z%ur@vo$STaKUtKeSy~#X#Yk?3gruO)gdR>c_hT&1QeF!r+!(luCU0g@W#uYZAizHP z28%Xpr33@Rqsa?-t@#cbXxuoPD;*M5Ev7*H01>Ac#B+jU3 zjb~8f$T+|z&B82WCbDD$*ZvPJ%)G|iwj1o)$$3bDP3O{`XD^j0_A6DHA27Sp&Ej7;-jR7Bi|Y;9y`# zV_;;^U}9i!iSYDc1F8`KD(e7>J2@Y?r^v&=Aiwz{<03}k4#A^|5^aSl{GS#WvA0<= z1u!s7?&Im7EXvX>Ed|tKBsW7sQqX5Y52u>@F&1YjuLTlr3|s}1H?yctzRRM`#!xB2 zz;J0YFRMG#L8-}MtSXEbCReknGx}^^%*x7GpY41&MMk2hQE~~R^qxkUD~xh)4z?X^ z_>s`0z#!eg+x(HeeuE-IEAQb4>sv z0`(onGZc94vn`y|%G>>rYyASQl?>d@Iuff4xMP;B*|*@{vr8{7-Mgd4!6KCt(H2#~ z8tu^DF|;ld%^9Em7K@di#NsTBv=4qj;GZIz$mq-V3+;c(Ae-JZO>CW8jX zZj^?fMtDuw!Pb4yPtO+<9W^*}Yd!_D((P%Q?pjsJEoDAk{H1FTW(Q1ZYHY zFpC{Wt5u2-(9q1(a?g}tpm=I8)#{`%N{=lW@Z_QlyYn>TQs#+qO@_wnd5GBRx5!~2F603plz AZ2$lO diff --git a/Assets/compiled/ImGui.frag.dxil b/Assets/compiled/ImGui.frag.dxil index 5b0eb5fd3aeca1d5ac7fd01fa4895edd8fd3334a..368b0b5d4dcb7e24acaaf64e8fa4c4a21a5d3752 100644 GIT binary patch delta 746 zcmeyNbU<0sCBn(s#Wc*aa>m1oWhOr7H*8(Pmza#ftP8r6xS?9`^mLTFGVL@E@g2PnD?Fc zzJmaZ8k3$&?Tv$A9}N=+_gkz_2I+|8oO%2lvHfPL}?7IoH22?mB6lkc*) zGXWK-v8pgGnC#7(#n>}>8|#63r6W_y+T7Gb($m7!ray5DYcqFL*u~3skUdmBLT=## z!(Ar3jh1aOHrcgsyOsGaPNs*s1|9Y*91eamv*FoRo1DOwd4MfnfxWbWz4`(B!VQTA z3V|~eI1aES98Q?xq?yCvlX%!*!XZr`fg=jMW-Kcm7U}Ao?d1p+ymsO8WRZ(3^$R(g zIh_>Qq?wYN0(uUzEL3RbWcrvZpcQ#zgT$H(T_TgJL93z|Pxy>h-h+BM z5(SOo4V;H0<{V@@_@R}TRdbD#zRiAz!}UFHb$jyqTxZ>uxqFADk%3vNI5RyyC&lF1 zrE~k9z38|w^`TKV5UW!lJmM_C1%IYyffv%3g@=xM(tv5VQSaExP=`vQ*}5s;o;5`Q_t?bda`%w zSzpdMK3+i0C6xuKj(K_cC5a_KGm3**0xeptK!#?fmV2fI1BFwAn?gW_1wC_9pU(bu z+A}wEbJZWMdO$_V`K5U!K-+_viZ|Qyon@SSfm6WpuG}{$U28Iuu8?BlcRYf=$ z7}9{^tV|3HE)kwSY(PZip~( zhXf^oVwMGytC@dFO9FKm$<2_E6!e+U!>Q(ejKx{WYk`Ct16RT1eiqfqJ6Y7(7%C+g z7*0-p%Hqz{C^=b=RfVx(axiNaBhTc0tOx3ET`;)ut+!Ef38VC$Mwu&&a&Hc{9c=iK z(4@d1-N4)Yk-dI{B10?h;Roys4IBlUjc*9_Fm-O*U}CNm;u_)X?;qqM-12C)f=I#! zMFTYsR|y{hF2flL0(aRKCbjZ*Cvq)s;99wW>#&x@iVIxQO^M9`q0-Y1iuf^##yN_z zg-X;1Fp8ORtP|k&S+-{1ibGG%oqD0haa1BFq9@XVHOhlEL?Y~_CZb3CBn)1z^!Keor|08Pe15cIK{ejU$+uZlS-A=p2(V9L)n=`fU|@JX*^t$p$x(W86{`y4hspC< zLl{jbe__2<&tbT9?!t?lS7*+gdQx@g#HkN`1=#YN9X~i;WuNA*kdA{!AJ(f1`U-Fu%}@|X-Joco z#?c~~Bd{v*aMA`R?Hz}-4;*^H#in!QP@98TM}TM|udS^oPypQNJ&Md;bTn?FM(6Sh#m?NW9P*$I#AuSf_>Az|nx&?1qBG;|&WHT0;fe z+hsClY~0&=@vN^vn?b_FXJ+o^=8h6a8eC>c-<9dzxn;AynUPd+W_o;aiEv9IPsfF+ z4{L$yCvIHfUA%bq?FTasTP|?6T5)&_AG7tAX6umm4k3xjo9nZSJ(abA3WQifPke}Z zbLz>nOZV<9V{s8SvJu~HV7A+6r)FbdJJ0!4KHZ)*KI>lguIb73YM7#UxOk%W?CRB% zrL||z7P6QM)cfMogHug+?%aEE=mpS-XUjMi6>yb;%)E5!-J}!CfWoJiv8)ULnzi&y zub&?OE8VlbdBS;K4t%jdRmbkVy7K~P`!bgIn-_BYW)wJmfAy)2`D}qY&bwZ4EH|CE bc@5W5Yzc4jZsxwpg1nO$6*e#7?O_D~uD&ww delta 692 zcmeyMwn0tOCBn(Mb!AS2!>^*Y1N$a@yvV&{H6sH9gTX{eY1Ro`3=C^FTCQhQUBbb@ zkjB8spvlC*;1c2K!v<6%094ik6nAnya8Hqkfk9z2FVh@GAwvZVp2Grdj}+xho*gi7 zXW(MkyqlSokx^>$K^95Ig2}g8R422tYO^s^N-!|onas;)J-L9Dhv}fyIAriTZ|H#c@Jt#$l&%aer%NbH8oVtJ-ltYi;1Jck%T6Po`Y=%W+(`xY;auB#@n69 zwY-6A<}JGYo` z*JQld!gKzpUeDQeK6zdVR}>Ey&vfQi)}EYQrL3JiTSy`G#D|zCr=Eq~d-delsb|Yr z{8WLOciHUNq+q_?e3!v4pcy+gndDoXWI={**|b|_lO|AjljcDUkYSp|izlCMuJSHk zJbSj#4<~UWo9P?3Tbb_y+OFwn#jyDy$8SafAK_%)ttwL-;82)a)$e7B=|0Ls<0ng!Wh3w@#%%zqF zlO4GyOuot-%_uq9kVTTQXtFnpDl1pP0s;2P1uV*}l@bgLw^8- zt2*Pt&3dd%jP)FbOXn`U$a!_<%&8|;cTSx8&{u#hui5c~<5l)+?iv|AdS}=5Xujp? z&3m!-TyI{JL5VcaBYv%b4f}ZcCl@9du%%64%lW`o6u@4wfxS+EOPY;YsFkycA&0?? z$4zs}p|%4eW<1QAa}KdQjBe(2)S8gN?OpuXDD`V#nmPUhSUZ>RD z2%e}FtPvHg(GqzF8(3o=0L^CoYBt?%`nP9hjtb`znhYwWTe`%}bei?99Mau$NRMZ? z!2u`z9D$;P;$}Scn?G{g z%*<5_v?e~>dEx27sjNG9?!DN05opF)UqP3LBCkM(E}eQ;b)qj&c&aaF&_bYL-ZQ1= z#>icX203X8gUl|VqMp51cU}bA?#p?Baq}7W6N~~It*&mYp7~nuy!sE-#>%6)o0o9* TVWgnVj@)dFj0&4QcnUZGIvNn8 delta 627 zcmeyNxkFRZCBn%$JhZ;<>d_r9%+IiP9MHIYmyv;iVaG&CY1R%-28JaYEsGgdZ?H2k zq%klud;`k4M0onJ0cioCvI9VIC+7qA6nPjJ-fq6gn99h1B;%F=&*5!_?BxN|nVA_T zH*!yye3dzxQDU+oizH*gWN#ML$)zmHYz&nW3=C%`&t-9E%$WR?MTIeaG8d~l&b1ceB==Lu`p+1&N0*aP4Hs z(fG4Kq~sxE#*B@7TQ8pV6=>^oopoF0?j4rI4;g2c&aK@z@!||`>)*2$CkIY{=4QU# z@nLJ+ht_z8`gTPbox&BXHf+4Pb?1_aQ+HnUnVG5Oo+-gV;nd)!K#*BM^3UAVr?Y>Z26U6T>V;N4psM8j(!3I& z?ZHfi3=ErjI8HDM6g65-^;3`dw2J-npH<-@MVp^+_F*J|&5hh_jEoGMJ9r8>0N91? Ag8%>k diff --git a/Assets/compiled/SolidColor.frag.dxil b/Assets/compiled/SolidColor.frag.dxil index 3ac12c8000c2944b434b589fb086a2823e62ab04..8a78cc935d0931496849e2edb0a2e544d8def2d1 100644 GIT binary patch literal 5684 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 diff --git a/Assets/compiled/Triangle.vert.dxil b/Assets/compiled/Triangle.vert.dxil index 7b20223137087a2324404317000711a8996a106e..1e2d232de2303dbb9c87cd75ad4bc28180b4d7d1 100644 GIT binary patch delta 1286 zcmZ{je@t6d6vyvtUrXEfT3TT4(c*YyUFQaj4zOc@+LrQT(6|C6g3egnU?ltqP!~5B z!l0x>f!XeDp{z3N_~DE=->4blAfZ(WY3gLSKN_e6-9joELKa{IHSs=`IE|j<`QXPq1OV{HQvig)Zzgm-^mgbouNV|PJ_8`MU^bceI6^CtO@NECH1eje+oo!#O^RNr^EGW6UniI(oE7_a6b%#~3q%MRg&E$|9MF$*{5jdwV@|X6x8&c+uv^ z@bH8c8#P8TAAm|YLkD?X=@C5_juS&>6W&&vH8CHD1CZesvaTVKQMrm{J?ERBr1z>y=Xl$)~`@w?|Z9HhBJTs#{s?KSlFl=U3D zC@YJeN6R-=1R85>GBvfePKIV4`W!QNrbE{GrC>5o zM>eH-dcm`6Q}av>f#*djcq|$TD2V}sK*lN#DDnP!L1bG>y+x?mZ>z!z%=eQk)+~@IzT~k^I7o3Nt+{R$c6bDzEC@aYm?fT|#h8p$Mrupio?Tm$XG8oB z#9bb=CC|e5in+2j!A?~IH}M`TVXQR~A2Cg657h9ieAWlBYqfM&s8QyAr%URG@aMnCW8-FlhW2=G@Wl+n(M(LJm&TAm7A)xeo z(?BGeYO)Ah3q>+PQ7NbKw7Kf{-%|CZ3?n|>h-g)+8N*3>Q zMx!To0VZwM0QJ|$UYpIkG2MIvf3Zljg_9-8l!_|DV(>c9CbkhlG#T*W{6WclsO2Ih@J`yKRern>+D delta 1079 zcmZ{jZA@EL7{|})?Hjjmx4^|S{LVw&u(s4zZ?QfU&IkTH6)%%Mp-zCDoCyd37(N7m0Ig+64y0Zr0wp$(#&7^) zc%Zae-i2HYNr=QobKqhwY!1xk%Fpf_fbGRraA*lU0vG9Gn8(Ec#ORmcvzkn{sJ6V` zdh%qo?Vk3wLmC(F@WI7pa7R%2IfaAh z?0tSvE&h=o{KPBy@E(4Z!9N2ctB$;65Gmb_ZW9yd2d5~VM?2$wdWEACPa7r@&$M5d zdg6X}sA{z8y^bG37114hx*rc4Zn($rNT`wFH7t82R_!*&U^ZdJDK$A83hsES*S)v0 zng_$P-fx$}6&*vPLqlq@tJ8FN$=~+T&4%y4%Tg-*(JvxbORl9Q2)=DAxpO_WmRO(+ z>uS+Gwe)9aV9qJsY?r=xh@!rs28yN%jn|HvonG;s`AJ8c{{B*{-BhHR>j;ZpiD>De zbn>l*orcZj#6oi8)3t?UYMfi4C@jm0PDQ^ejfSV9N4snK#P&O0y=+pquyo(A_pv?N z&c_5n=Le{%{>rw#ujRbnH`!u0;B2$x>ej{sX6^3k*5)|(`ZAi_w0-5aA5Cwww@V|r z#A8SoiHMSm!|AcT<9+4un4tWZTUGNcUMuJYu(*BsAKYe;e}LQN|HX~o!#<|t5|4@n zxiDuiQz3Ws`_c+txXHC<+Zi(1$YlWSlBy&udy9xYmOK|CZ}uWVrLNOxx`-(3L{UY t?_kEuat{}n4W>jQ?{uQTWyyZ70uWp*Pv@`{w2b5r|0`W*F|$tq#oyV)X9EBL 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);