From d862068c6e5d1244009b66d7826aa0069bf4d94f Mon Sep 17 00:00:00 2001 From: Lillian Salehi Date: Tue, 3 Dec 2024 01:57:55 -0600 Subject: [PATCH] Multiple Object renderinggit add .git add . Completely revamped the model loading system using Model class instances, uses a Material class to build textures, clean slated descriptor sets for bindless rendering, significantly shortening the amount of code needed to load VkImage, VkImageView, and VkSampler. Added multiple texture rendering, abstracted out model loading process to automatically collect all Model instances and render for each object. --- .../agnosiaimgui.cpp.D917B7EB41E8A56F.idx | Bin 3384 -> 3006 bytes .../index/buffers.cpp.FC2CC275D910BCB7.idx | Bin 15906 -> 8628 bytes .../index/buffers.h.CC15E40B6084C10D.idx | Bin 2930 -> 1846 bytes .../devicelibrary.cpp.A6A50BF3BD186A09.idx | Bin 11922 -> 12172 bytes .../index/entrypoint.cpp.9286A9B0BD8A276E.idx | Bin 5252 -> 5396 bytes .../graphicspipeline.cpp.F94E3ACB17FA6762.idx | Bin 11786 -> 12068 bytes .../graphicspipeline.h.093A3C67F46BDBAE.idx | Bin 808 -> 1096 bytes .../index/material.h.3635D536A26B1F7D.idx | Bin 0 -> 1376 bytes .../index/model.cpp.56D1ED026EF1D5F1.idx | Bin 2466 -> 7168 bytes .../clangd/index/model.h.C3ECCCBE7C04E4C8.idx | Bin 348 -> 1808 bytes .../index/render.cpp.657B8B8CBD5B3B6B.idx | Bin 5986 -> 5834 bytes .../index/texture.cpp.14763AFB742F8112.idx | Bin 12892 -> 11972 bytes .../index/texture.h.712506A996DB5236.idx | Bin 1518 -> 1404 bytes .../clangd/index/types.h.2A872A9A515562E6.idx | Bin 2510 -> 1912 bytes Makefile | 4 +- imgui.ini | 4 +- src/agnosiaimgui.cpp | 23 +- src/devicelibrary.cpp | 14 +- src/entrypoint.cpp | 41 +- src/graphics/buffers.cpp | 499 +++++------------- src/graphics/buffers.h | 32 +- src/graphics/graphicspipeline.cpp | 79 ++- src/graphics/graphicspipeline.h | 6 + src/graphics/material.cpp | 19 + src/graphics/material.h | 26 + src/graphics/model.cpp | 179 ++++++- src/graphics/model.h | 35 +- src/graphics/render.cpp | 4 - src/graphics/texture.cpp | 198 ++++--- src/graphics/texture.h | 10 +- src/shaders/common.glsl | 21 + src/shaders/fragment.frag | 9 +- src/shaders/vertex.vert | 33 +- src/types.h | 41 +- 34 files changed, 662 insertions(+), 615 deletions(-) create mode 100644 .cache/clangd/index/material.h.3635D536A26B1F7D.idx create mode 100644 src/graphics/material.cpp create mode 100644 src/graphics/material.h create mode 100644 src/shaders/common.glsl diff --git a/.cache/clangd/index/agnosiaimgui.cpp.D917B7EB41E8A56F.idx b/.cache/clangd/index/agnosiaimgui.cpp.D917B7EB41E8A56F.idx index b61d11b557691b9f06ce76efe1fa7a19fc13157b..27042b5f6916ac1173e457ae2ae5849e7e424066 100644 GIT binary patch delta 206 zcmdlXwNIQa$kWYj8}~*wJ|-qs&dFj-7K}`j7c=RwzlhR#3WRSDEf8D3v;*UJ z4P0Rzi@57Yt{KLY{O213D@q2~K7TZEYKwjV!5`r19Ifyl#QSV*I+j94sl5(J=h>3^`O!6}G z#?bi(yefgh79tkvFvo7bz*fg7uKf7VgRDio(Dp?+4&y5HC= zOi-TLJ)zH`ocQd8&T1dG-KmEMug<)4xomxAV^L6?ZkA!ixaoU47wu10)+gw$98S^4 z?TLLq_1Ihexyi@-4R3gmdgNWz1~1F}!A~|s*v=bfo$GMc;jBKT-LTR;czCubezN@Q z@^#z&41rs053ML3u`lB7vWq<){eEJw_3mY1MP}=-4g2Zq>fEK-@2Jlk9IbxZ24$h=;fxHAhjtoSZdec|QJtTE~L;`?`nxUdAEI zM`Oc%tQuPe)DAy;KHcYs)FUVR&#iI{ygn~UJMFWvm)G39wy$va8QV_Xoo_kVRbF0p zcJg(zn8jA#e)7xGVMjBT-Cgz0d3lU?Wci-Nvia^Q@$sb#A5GWrjT7ZzLt9?h|L6J_ zP0E&qHyZABy!*rK=`(jMIb1bn=;^5kUb$-vepuNq#noa$OG)W~rXDSySLW38om=qV znd<7XF+M3Rc2a5D)`h8G&MW<8@Z|5#a06zIit?No;=O2oLej!xyH+O* z+J5NE%g^W3{HvZHI%i-1=HX|nR>w50+c2Oyqie!>!^MMNhc6oF)9y`H;2$v;M~=C( zK-J(pIPsZn+R#V!17`%fmMt6msIP7G75>VZyidJi=S;sC(Qeo4)Ne;d$ZuX;v#8tm zwfF6#*DQ-`&YN3XbU3c;?+D8d`sLiLc~i%~ZHLw)ybWxK8!D4?ojFdhSK7O^HfTq! z|9tJ_i?yd4WNH?!;Di#UE@5s4gWBN6S$-IxsW~zzJ|V(N&UHh6jZ)*@+O)BADI;#( zdmP}%%ek%y+Zt>`S{s6G9?ThZ0JJIx=Yeyu7m6cCcvk)9m%yh-f%OL0kFB&l3-4K=f zN%*H~7K{T5nRg+JG`M-YMbZH65wDqDM_->R=TsC7Dp^vUE9Zh8MEEvpaNXvs_*%09gUZ zC3+ReR+-X;%)F4H~3w$oLC&6g}IWGj62l0|7 zrkGFRP4;e{%@YfcA;oGZs3^?v=;G$LfhV`;(OlJy;6NtKV+gTr0v&}F9{sg_Gq{is z^XOpaOBqj_K;1H{0;VEE;L&|t3YjPAl1G127BhSDK^}EwRl-!HJUVGqq8OG*S8TX4 z!7J@^IjUfU^@v2;Y5S|cm)t+FQen#rsX}TXb@M`&bC#c^fIM)`g8)edTft!~cu8~x zcvV0rNhN2%@eKHo`g!37sBVC>MAw447F;E|4%*j&heSUF?L!bGl|BKtC(uEnUqSm< z;33iPLHpixd=69RFjq;XxlEbMoFqDrsq;+ne5TBo@MQU|AMV(Fr$Ak82Hj?yxXtb< zt)U%?IgNBH9$82Mi=!i>^oGO4+$IgnV;%EY7ZT%U^hXy?oex*xGRHu340_W3T~qE= zYzjOm0@c8(K|w&Ea#eYCNk3ZzN4{2r(YxU~9YUSr?o zA~1{%>p;_zaiEAFax(cIFT4bcm!Khjn~$vOue-c-XvYg-vM??2^Fp*N+JuUg#TqG! zXE?W*&ZX@aF{>iho?K^JO%1zW{j(EFb{zQQU`u)>-$bAJknc&hX>rCvtFikeihgJ(NwTxxeLq+a-(AGm|+WGp07t-N`PY41hfjCj0RIAPiGzNld><}oB;mtRwSTXOwd;gR43&i{BtfENQ6^MxS#Kj%;22tZujyi# z0?#-&wTr}(6_6E1vhzY7bj*VY+AuF{1)r@DEYWAc?~J5=UZ?}FItZkl@WMmzcxV~| zpFrRfh@_2(-s$xU0wwxAc)T|qpTpd9n4e^*xy&V(c}sL2bI&uy^O;M&gp-`8F!91l z>e{a193~ySD<{ z3Od?S*Z*~0K33H@0)ZUhbHIfFMP|39(etz;5jYQ;^AJN;ne4ck+wy+JXaq*dMrmk; zNfBeloINvXEOwWGrUV>mqUphAw_Li-5rIsbOciNh-)RRQd5g_B?9Q;u=uX{VZAy07 zQ|TafZvfo}@TO_5BtS;HSeHo%{0019Xq0VF#T{#(vco45QxW(Z_`h-CHlV$~{ABxh z=T8urYcp3(E9jTEaDI>Xi{0x$w@%uz#h63C);#=A8Uhn!6I8S@(kvd;hsPQn6=g;Q zK0MM0`1>oj<-698{uC#A0h$-kg(On#{j?~!|JBbBr~p<0o|M=14r;&njZRY$m~As# zN20E+e&jYas7ma95A3~Uy7U4>ixZ!JlEuY2#Cee5m~MP$gwkA}kNRwb{?(`H=sOw? z-&AcS3SO}1>=pFJ;Dx*3co%$0;0GH!{`6(km1!uzHIQGEZk-uw*}D4Zs_6*a0CoeE zWF1`JvpYtHd(1`PIk4xF!>|4+BS&?(w+LJS%>~Jx_BuuFOl`28hroH@&r23h;R*Ho z;So#v{nh?db5@L7mKj>0$0#3ZzZ0A$n8c1b!W=K!Juj4lUpaInYns&6F2k&DP!=wl ztW54nQ`CNwi3z#kZ3F1bEUgk*!l5bd%^}7E;Wj$%axw z2YE&+9|uCt9nL=4w1h0+b1 z7nXzba&V_j^1^DsACsrXT5w)#if@DN+n_g%-*b6U_OY2WFoFyBL4F^DM4Ko5m^`XE ze6&4YJA|8{x(UY1yX*Dz$ji+yF>(lHpevJ>I@-m%-<(eZKSy8{C{{@_ZhKxHaw*^& z5lE4x7*klHkBxc92T#TBzo5fk7`@w;b~x4iWuvll9s-wvU6!oF{pifJCyS%V(XB)$9cVo;jLec323rp{p@vuwF;ag_c=Tv*UBXJ7E5jzk7+!zt z9k2OspRsFjrU4cMOx~u0EC!iSA6a~4q((h0IT6)|TaPpS2<%7DkW`gDrha$$7xOI$ zWLabxFY*D8l8eo6HEh9)cv{!ASV`FV!SlOHj+YN1`4B}m#tV4Sl4mX3iogZnFGz;4_weFV^JbN8N8k*wGm5B6)5pb@O&w0gih*(jR1W7Zbg-eRh1;<=dF)mR^sf* zvSg#{Jzu-;=&)S}_ha{BV2h=9@xbLPlcr{#`!@oYKz>QmcxwM=`cvP_4|8QBjJ#B>-y2-kkP;s(2 zBegaxWu@?JsK^qbiSU+KVl*)(R3A+rBekzwHF<-!c|_6nwYR4&c>J50S!;Qassv`Og%dJsGpz4=7D(?8Gt7VY3Wy{5&vj9a=%BHyN8mPeybayS3|tjUI{(Y^a=isF zOjb^IA~o{D0_6f@kmfN1Dwly1?TyC_h@Pvb*?G)>%0Cfrvn?Jopz@7GAMd%}A+eqO z4_ti@ust9UqxHns%@;35K0x3)$ghJFsg(+O=#L*N@Z^!!6)&ztZD@XoRdpSwvn2P% zSRsrz8UbE_y=Mo~+4I6S@Z4q^X?Fs*6D&yxv7YR{6MQAQ5@eO2l;{G+6);PZNv!vB zCCrjyN6o{t(~idc{77zwzNcCty<5%jGhylZ%S_f%?4At3^Zk?^d``3GNm_zX7fMkkj*JmwFkCj=m;@GWIjHx$wL$Fu1Msws#h#YgKzq)0Np zW_w<`T7UapA{K^(bVa&A=hbjjA8e7LdV}2speO(X$@}$?8f2gN+*xhX+@H?>IrMFLL)9E>b=S_T%Kz7G$*p<=e!iJMaC;dBSbbr9KC^N zPMVS;a?YALVb%fh!&s-~I1iEj@Gp=)IL=G@^pie2NT0^P1aX{?u@{8b5#J^*J`&wW z;qGPd@bvca_3O~lKOm4ZUOG6=(>RD08b>bBKvmIw6mop1%nVESz K%P9P-68C=-gANP; literal 15906 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 diff --git a/.cache/clangd/index/buffers.h.CC15E40B6084C10D.idx b/.cache/clangd/index/buffers.h.CC15E40B6084C10D.idx index 90ecd48d303aaba1cdaa79b6265482fabb27dd7e..19a291615f43adaf14cf77101b146d2b121a31f6 100644 GIT binary patch literal 1846 zcmYL}2~ZPP7{@n{bJ=7`-X&Uh5YD%PorEu_-|6>B|ED($FHL9nBv;|0@ife&^jd;H&j|NFh~{a&0Y z$;mMR3^O$&CEt~qlj_Sbi~zsv9CsEgVVDzyVei2?&#`jvo2xJHq+dUF$<>ij zxh3{Wd-M-Si}o%~`6H{R({)RoYq4)jI^UZ2OWwMr=hpm2r#5&mj~q43FWfuJ%1W#WY}cnBec$n*VVQmMonJ3SdI@7$A82nloLF0tntE}X zv-;kiC#4hgTMs_%X!-2G?TU^aIa_=F{<8Bz@2$73E&DYOS{sj^^X}X*OPaJKJ6+yw zJ29hj@x-OQO%?C9)puWcY&qCIx+yO7>7#u&eL8xRhZl@)>H6lb>C9KB>*60a=A6xQ zH%y;@Ao8K`DBT_8_2lGt$+A4-Kc0}_x>0W#S+u@uzWv+>&AO#;pWkP)3tVY|(2XcT zR;a4!44@>0FhS*nmJo5H8B93VrSrTw9XyN@6&Q+$wuo3by;g6*Pb^x}Ub)gNvrrNM z6^RF@;1wnu-gO~yPIIRwni79_KbDU*bCjoM9JZ!B&R<>s`6x4lj6bz-r zTuR1q2D4!t8V%oH}hQ$UFl4BLrrJBj9WI$bmx#@ByWefXN`JSDzhaPcl9r{tAJ z9L~BKzj^*IUgulnnf zD2K6OVYt8u8ia^|VF&^SvIrK+7zVP4)1W-i5|BlRP_F&G_{@A~(?poOZ=kPcVD3!X zho@G&D0NJNN_4C)6qgw*f)Eij3_;L97V$#)$M8Sz)~&9a2O&n*s7BiG3T}}#bmJ<4 zEE0zDwuP(9wa-_rg^&x4Jtz?0Dci< z5oDCZ$4R|m{{K1<}2kitVV%zhNlQk)X-Bn8?L|v zDQ99$3fwdYpY;lYlPX%W-RaqL#hYO9 o)PG3vhGF=jTkdqX%G2CDH`G5^CS~P3BO4m)|I_Y!mJg}_1Mik4{Qv*} literal 2930 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 delta 3830 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 literal 5252 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 literal 11786 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 diff --git a/.cache/clangd/index/graphicspipeline.h.093A3C67F46BDBAE.idx b/.cache/clangd/index/graphicspipeline.h.093A3C67F46BDBAE.idx index 95d67bdddb26c530326445578555851e7da0806f..6ffaaa084d4008a8f7eb20da1301dab48b30de6f 100644 GIT binary patch literal 1096 zcmYk3TSyd97{_Oh>)vK|nmt}xL-RtCELN7JMWQX{hFNMFgs6eK8zo7)qooj(MH!mr z)*`mMNWB%VrMaRaNG(2yWtiGS?2DF}q0)_f@zyzR-_pR`e*g1*Gw1u|Wg3k~Wek&U z%B-z0SC^1SCWxF;wdJCl+$&jzsdYBD_6VK?{@Cvf(|5V;<(1ih$&re9=OGKi*K?jt zHa6Z~URxS^=z3oI;JD+1-fk>l^Jm^hw=ZPhuabT2?RcdBHX_rtIf~xfr^}E>IcC`F zd2w0FIC9mTwxU(-=<55**jtsSrVCuD+15mRmUF(hE^%S^)cVY3&9%`p-Xv@Ja(JTQ zuwz0x7d(RU8bb4aMxa5nr|6`+yS}jewC#quYBhP7ef8#Un^ad(>L&jkXiZ3s;tdJ~ zA}^dwE)%vcjEd}wVOTEEsb70M)ioD_kqT`whEoWNSVrl4>24Xa_O}h}#7G9QT2Mzq ze8BK}a$kFQBt}|@HG(D@;_>O(&Vx%$aTqBfRtYM9{O6uAB`Y~O7o$Lkm4Y$~su%c5 zX*J(_6C)mCQ53@<*8E!6#8xKRFbaT}7kGd3u(Ybexi7j7j6{gBh;>k%wFLbfr&VB!^fo$ag{Y%208AQ_k5>7;S?%SPb@8_mA_%EnLAOMjD8< zqBb0=o4pr8du)9h7zq#ui9rz%Tg>OBhX35LYM(Qo5eAI0sx4Ds7)%jN4FLv21XDAB z!3@FF2w*TmFf{=fOb|>B00sjD)5!yd$rDT`4j3j*Fr75u6YoZyA#Rh4?2=Wox__Hw zYCgy8$u$=CvplDX(d>t9fEUt)`hX_jgJ9|bu-f%wJjK2-PnrlZf*zWH9TtJe{JeyDz0#ho7qNS`=8asP6X#c8ji_E5(($Ns2(%u}(CwQ{ZuA zXJ-O(cv#q2_`n1MC(!i_42(RibF}^}xV-j>s1Oq;69cy%zn&ToJ0H6$0|!_E!-l@y z6Sq&>At%Jd0+hbT$bXMffrpEaOCF}c$noGM{TUnNg_wAO3YwYto0*h&c=>o0VG7P( zdAQp2O}B;+69*H-Odd`?PPp{f9yc3rbMpWpCRUg<2Ooz5OgHbRzr3miW>bWin1RNB zX5|0OsKmp=#{<{zJ$uLNwzvOR2r;n(6})HUf6u7M!_CJHH({Z`seP;0IA0KAVgo8L z<~LU5VdG;{fmy&%l$us7f)rAWT#yigxRn7GK8&2;aDjy@3Ut_c*(=XJ%)2*4b6m)ikjL6_X;Bz_cjKE-ryVX|8UTY3a^pmeG$2 zDG2+KsevU)8AeiK{^{l_DI#Wbw5)I>~ZI+U0<&qiHaX9w!hr= zw7%Wte4U$}cKevL{C8~Op|R6P18TsoP{axyb$k?*{Fz3;#3E^jz z_wcUDrvBeKCnDso?knU)(|B*qj`-5r9&C$f2?u*iZF2K;|Fa=vS%MoB-9saSVyMLfi#1c!+^Slx$V0R{$jnpn%P%2^zvrVEYGEl&7*|c_i zrsY-zhcQBg1QRh(kTEb}T`-w=6P$E>PmULR{_{0U47uR3HN?CToa521G20#s_3T!(@#M$Z*E| z9E!iQqW^6Uu&`c@3G@IC7C^%Qa{7Vn=!Ew@wLz4aut|W0DUq=(Kxj(riai=O>@u#n zUEz`HgDK%SPJrJgB?itQ!0IfMVh)G-v%6&T;(vD%Cka535({S$V8|C<{z_aL`oYig zP)togiV_>PnE((aj!?%u*pXK^m9`Zp^syEm^F|mcC01-T0uD+PyAlbniPuwJqzzB? d7OOGZh4yFp_u$}yz4bSW#REn1U@6vf=nu{%f!F{5 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/model.cpp.56D1ED026EF1D5F1.idx b/.cache/clangd/index/model.cpp.56D1ED026EF1D5F1.idx index 5bc649ef1c71fc64df2fbac8db8a6f940dd5e53b..431044bc53fee0e720c0b5caa7c51cb7224b9d6b 100644 GIT binary patch literal 7168 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 F{2vMW{I>uA literal 2466 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 diff --git a/.cache/clangd/index/render.cpp.657B8B8CBD5B3B6B.idx b/.cache/clangd/index/render.cpp.657B8B8CBD5B3B6B.idx index b3d994a5c194ae609fa7f79bd4139f09f3b5767b..9b1da59253a8749e308968a82b6a8c14f84bbda5 100644 GIT binary patch delta 3228 zcmYk8e^6A{703C$ec}plm)(V3@PW820#ZQaN01(m$)UlH`G1_#RPBJ7VcG{UVKdfV#YI-;A-TU5u zpL5PT_ndRjx%Ymw?nYkp#z(H3=qtlza}lbEI8p5xb%5Cf9^QYkquGRL2JK^ZT!rh=L?{7&?;Qet%|oX;=MG zNp|bq#G+}{I0OedqOo}z)-;rJZ7+mg$SpVA9mX@a?n?3ql1EU)*^44C>R15IlD$7V z_QBrR6_RXEw5M`64yz-JlgsK-QCDVH9@ka%z}ACozCtf-y=v}5QXf`xJ;6!>E9VKU zoFGq&L62fkR&l>g2~BC7TGLwBacW!D_8m@xkOz^)9S%btR`VzlO6VR~)PwbCfJpSv;#20PSv>V#VLiD4aCxj7L1HTS{TheW0)7nf(5TGzL#2H_&IE|Sldr;GCt=1HP9-J=Lf z91A{W%O5I#e7kQgV&w3k!un0;*_}@L zriyKrZKcf7->e@OEErGaP_bRGTbMYdaMS*&uaon_*7|MnVKG_IKI>6dD{GmJMB6rd z5QQuWiMDO_u$o7aF^Y9u??t8;`E1|9otrtSW*=-mGIfs1y|2(qhi8-bq+bKA8JkJK9AwsWbNDuxvR9Z?a za|*&Kav;X6=}7G$-7&VdS!B&ZVeYbGX06Y^H(X9FN1+{snH9qF!F_KssZ*$;Ew(L% zt@EU-_?a&b-Vcj6>o!OF^ln1!u45}So2Yn~eixBqZJPFyc2W>)tM10KZp1RT540cB99x)UiNlD-_!wjf* zI;%Ok6qicZpfsqcIlVcbTXqRudKHF{mR{cBF6q?Jq^9-t%JQO3!Y?TXOS*>!Jw%ilUc7pd3PNA~Hk zY5C})GINlMo1&Y(9hvZjx9-v1xSsRwAu0}{B#5NQM1IeGw!&Yla2e&YLR^0oRPBh@8j98(L`H?Rs+kA%?OP!{Cq(= zJ3re`E_!anS6!p_P3cYKyT%+%mAQ%>G*&#`5#6C`V7KU&n71RZyxH#h?b&ze4KWDq zAXc(MgpWk+Fz2r>KQ{5yN1B_|`!tHD&zQ_K)fo89`mmD=!=k+6@pBH}Vm*YuWa$jDgfp{T$VPScO&suSk{L@rs z-5R$w(gu>;8P}ryyF@QDp-25F8$$As`cXECqDlTJdvb`}c=5sb zUun1@6o*jFHZ^9?y@%JYmo8C-OXrF#8+haXZ2YI~pF~S?SAJJzxTv-Ndn)!Ir-zTK zB=;hxSIvD$>%$|w*aS9$63!E_O_0ijg2V+xEkMf-zIQ&ka^O|_tvd#CG9lNaR49?Q z7iD{qy_jvQC_Bm>3MXfob0a5Lxl5&MC~r_vv#mLuThdw$p_qjw(%~r@iSUNIqCAF- zF=TQB`oi_1p6#7T_pWYI%|0kTa@3se@k8(0VE7yJ+m}69(!Zta?FEhs@b96LCP!@ zb;^3Bf~73U%?ZsmPJIyiU|@esG~Yy;GG`z1X-X{LD00P8q3zl+<~wgZ{xQ)eZvQXl C$oXXe delta 3361 zcmYk9drX_x6~H;49UkVhv5k$%$IIBfT;uR81%h!b7ej5*;+hD^3ra#z7HEOff ziL^%1FkZ+F;mw-BkQY2$*OtPdC~K2;ZKU;)$I`7)R?^Z;niTbsR%C6`T}QpXpa1)v zbG~!WIrp4IQ{sM>>AS)wLl9(Nr829k!fBnig zjQ_6}ejhavhMJxO$i6CLv=IDk#rE#T?j40M@E! ze|`Aov&qRUXJm{?)QwqX&ZcBhTM8*zHC7#^E{EE|Q@RV>2CDRe#tS;S`VeS_`0Rsp zALLN|C~O`DHRV~@Jd1VIXG72!JvGn#aYr?atL7PqW-2$QB`+au8C2 zkV!cN$`Ic^46-oaJ_0EbzWq5!KL`09>R<^}OOQu-8Pb;_pYjT%uTakVe)8}0KMAG4 zay^osSQk|qFN!{SX{Q1=*C-^6f|yod77}JbOcFfWpS$=K^T*uRCfps(T;#zkNjK}rJ(0~4nF?b*}p+r#C#c;z54L6A_x2rv<3 zPEa;@A;Sx~G^8QO7~-=JWIj+4{jWYhar2Koz4YM6(aeY@)YSGJvZ!cUc!V4@y z)go9($j6%&OOvWgby%?{ZBI3E^dI%-Wq*-*^;m3X%xOfNS>Dip_J<5Z)LOD9H7Z6& z6JPe2a!f_UhQ-~g8-sF^8;iSDH^JvA$e)5OR6hp=b5KIIE84IH^L+M$#t(X`4?tc3 z3?u`bkq6X^pre8yWCx*;atQK5{13vQ4)g6JkR9RMFG0Z)l+f>&p>P>W2}e#m?X<#h zm5}8mtVA_?U5y?jDa|&6j?-<=`%GI}p7h@utmw<` zL#-mszH?A`4vb`}LQz=lyMC$;8%#ms6evm2(r#|NmVxX>tZ*f}GRe9F+bduC#=Q|0 zZx?URA>z9VTi4+vK?4@=m+VLX6lny#RlVqeNYgk7n+747xMlG{zUd4k(^@aUrUghQ z@vyk9l2##w^x1sp?IW(Q_cuwAD|xFY$EYVOvYg3evQsiwnzvE17%fjwvYMY>c8&7q22MTilF*L6Az)?fu~0qc`q7u;IGw zlkAIC@jK;%<>3oIG-L5eU{8XK*#BEmSZ}+LT*4bz@r&pXCQ^7anS2Kx-k9rO|@Umuz&hjRQ#m) z$yogk4Yu^3=y+oYi|vd(c6ijNzVm65zyCcJH!uykAEBEPbw~T7?Iz-m<(^kP&$|@I zVe2?FkQk!xfUT2!o(97-JVx}SNAcMgeXgLkl(N^kn z3-Rs4U+ zH?h8(>6Vgw$&}%UqwgoslGD0paLscm_3?|?zD?VPK9K4KP0A+pqqN@B7vIP0@_u1I zOS~O?n<$e(Q(G^*l;5p%QC_&z6I0=fA zbZgcF6X}lP#Wft-90=zijbt7@>xJ{^ki1sikv@`45<{w9#g$3HU~n^Tw$&x0SGxuoyADWT5si9+G=fEsm@X7;8{BBoIGmRv}>s) zF4Q-=4@~5F7W`H-&J_pTY=IvJM`FhyNhlN@c|eC`O=^XkM#-e{Vs}^?P{x8gvb@>d0CS zzcYGXbRUbmBwgsVvfgGYHPw?uS*~5tuA$@u#s^a3lf_x8aH5p`_}zgM`T41g9!aZD Re5G%u=KWjU3&@?e^Zz;IHdX)t diff --git a/.cache/clangd/index/texture.cpp.14763AFB742F8112.idx b/.cache/clangd/index/texture.cpp.14763AFB742F8112.idx index 7fe761ef8667b0cce3c64de188a261c1229e7a84..11c780f53e9b154a39cc87f0d7361d05a353ba8b 100644 GIT binary patch literal 11972 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 literal 12892 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 literal 1518 zcmX|=3rtg27{_l<>ErfZZrjt_$3?=dsN*q2R)SMm)UaVd#YJbPZt{@T4MbU5qYN{% zMbQA3{`sz9aoHE{%sYjbE{K~W|D4ZD$Y?8! zAJ+aXZo9GeiNh1!)%W#JI=UJri*tIDKJ0D}7wj+U@Q>DewhJ$>UtYX*{w(jiZ#5rG zt~*_OWUy+K^q#NWaIdIzZehixa9``l$}B;sl`;9OBT60u6 z_SvEGldEWZ+`%Oq_B<$fZ`>1Z%FpWbg|5Dn{?eKgpLawC)=#{?awxl|sQ#0RvfGZo zf}JhFS9fe(NCe@HeXAc{zjn$%Nep}9mn19l9Kk&Ep#3}1SlE*&QDOu(DWUad$`$J#OfsC(29T4x|yuC4Qtb8YtC8y|gqr*+Npg*~T5B!-f-) zCAa9dVZ-o|&BQ&#Oz0Aexqpb^AB(wnh|xtBbKekK+HYOVZhSDz)@2o~cC-K*vTCj$ ms)c~RqJ(cL7$^-U@*gt)nz^K9`5A%QkDA+vMHVbb1>ryay}BO& diff --git a/.cache/clangd/index/types.h.2A872A9A515562E6.idx b/.cache/clangd/index/types.h.2A872A9A515562E6.idx index f4ca67de1db49ebbc6877a874b1a43431286e167..64f68ee364052aa4e4c484f24c93b60255f8bff5 100644 GIT binary patch literal 1912 zcmYk64@{J09LJw~?>O$|-uHRl_ujjMd+#`oKZ;Z)W;6#@5CSq778|KU6EeWC>j(@i zBXORSRg6%PfMT>IEfIxu#k8RT(1$*-i2qA@j%l4MOe24xW)FHI*!ZG9YHGbWop<|}&G$0g$16Qu7nZ7a=2&xg#HV#+?QZEk zlRVyhcKEM^{?4s4SKCt^S=OlA@4Ls=O!*t$8lBCr^``|rlJsN7Kz3Cj|HV}G$LFy5 z5itv}Eb@p;yjtC1Tm)tN=z_Y>r5e9EN$29!UXJFF>OJf4|0<*=c1)W;z$bVsnZ0e83!#OHShO<% zJNxwFq_pl9wG|p2fg=AOtx-F%u>syRe+El02>0tu4Hz((A-u}5>jAUx7RyB+X z*g3_E=fz!k1al(2Tfl>zAq21u+@{ z6QGf9Ybe4tP)N5m1Q~l&fIYgcfd?-XqUg2;90XtP92u&us9T{Eh|ORV;Qa!EI*y?S zOaXfI5RE(N3fNo_Lk-Rr2vLa#jsoy#tVSK=1NqTyjW&qw+`Xx8%@gsZoIqR#mjXoz zB+L+|0A~K-xv>?|TVAKh2OENI&}Jxt9;p!uSuUifTy*-q_4H}G!7f8Ba2S1B0~Ydn zbfCi5_3vCG-R4YOBy<-L)(0_kp>l!9qHKdw=_*W)h818Z8H8BB1#fGL_<_56jso58lp`LyjFpz233K~1mZL} z6-Xb3h~}$J5cDbW@_v8OlJpN}Ad)O8Z~-BreDAR+V0*~cEf Qu#UqL!oUYY((=Fm0XpXm%K!iX literal 2510 zcmYjS3rrMO6n#643%ktDV}HQzu*?W7`!T!AXAu^`VigNkN>8cQ*hn!>*&ih zU#ioKjw@}`bctsZKAYP9;i;pl=s(KB+k}%7|1t%y|7G+0b51r--1?yF*s_gg%cU*z zJEE4}?s$2(>hi^`H4V@0eYGdXJh~guH)dnSg!bY5k=iHQs77sfQRBU@cGhTo^A>&E zJlk>gN@dH82M1n^J39G%$3cJN$l956{QDJaAMV`J)4wv-dXs8tNl*{#;*!npUAW@k zv9Ucpzqe?gx6$9{Yx`kn=K-up-H^GVs4MlNPfGXSd9KY}d*iAn(|$$nqr>f}^n>Dgaij*WF_AAbPrcFvQVN7{ zkTM8M29eT{j{*2<=JWY;s?ApO@gac zYAhL`mjno9wO?>^l_-eN$*SZq$pF2sX>srD?zBoBr6XsXX>){RfcDoh6HR+|tz{8I z&Q8=M>Lmko%(la^>+7Fv(ouS{TRTjfAsL`2U7Xc2zTI5MBGV9r(+%l@WPmO{H|2HV z_%nN1DxB=bCOlL!Krj4y?2OFHhM&o9QdFP8CrAe9;+&kWxvRU+u*g8@6hjIx8K83( z_cdwevu!M;A;DQQ%S#66d)`&8x9mwBEEPs*jag%n4A4ny9NT9L-`r55KvJ~?RYJIA zfVMo(EgwI*>J^KOq%29HNoL6aU6Qx3)U70arTVU^Hs7maXl&su5m0LyM{a2j8p1SXst(_f47V&oeZKwf*W9%^l2pWVOAa)0& zar#i(#ZSv?tqKMw(uoX+5&A_zNqqth0DVG&h)n`d2((fy^##Ocu)$&QK$Z*^;)UeF zl8FI!uWyocA36O%`$sMV`)aeaia3mjT8w?Q5N6V0%w-2CeN2broZEo?AOBqeZ zD9E5;#Fmmx{r{wkZmTf^+*!-dHO-5H-o;unTejKP_Ug)ozs`r`2$gtMnTop?i*Iy^p#|95}jm(MuN~JXK@C>n>}t% zPFdiZP)$ObLQS$a;Iv!<>;YMjz2bm^cVF6i>{)Ka({<#&7TyvIs~H@{N6C38btQJQbXxQCazO>x zRP`pk02kW%&YipauRRyt*nqVb8{i3k4u6oCnv!Z}_VL)y~ zK)h9koV;@YRW#qew~D@WL43aulX38xTrd{`2y!Qt5y$jkWvo8yNLa*R_fU5%)PkTm zcM>2d1qmPy^1%RtPe_vm(6~cj3khI$TL6J%0@Tu!hmK)z3Z0^dYB1PG`}9zn+a diff --git a/Makefile b/Makefile index eff14b5..371b4e9 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ LDFLAGS=-lglfw -Ilib -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi -ltinyobjloade MAKEFLAGS += -j16 SRC = $(shell find . -name "*.cpp") CSRC = $(shell find . -name "*.c") -SHDRSRC = $(shell find . -name "*.frag" -o -name "*vert") +SHDRSRC = $(shell find . -name "*.frag" -o -name "*.vert") SPV = $(SHDRSRC:%.vert=%.spv) $(SHDRSRC:%.frag=%.spv) OBJ = $(SRC:%.cpp=%.o) COBJ=$(CSRC:%.c=%.o) @@ -48,6 +48,8 @@ $(BIN): $(OBJ) $(COBJ) $(SPV) glslc $< -o $@ %.spv: %.vert glslc $< -o $@ +%.spv: %.glsl + glslc $< -o $@ .PHONY: clean clean: diff --git a/imgui.ini b/imgui.ini index 42e0e92..ca80a3a 100644 --- a/imgui.ini +++ b/imgui.ini @@ -3,6 +3,6 @@ Pos=60,60 Size=400,400 [Window][Agnosia Debug] -Pos=522,0 -Size=583,202 +Pos=40,377 +Size=623,438 diff --git a/src/agnosiaimgui.cpp b/src/agnosiaimgui.cpp index 4404aa5..e9440d7 100644 --- a/src/agnosiaimgui.cpp +++ b/src/agnosiaimgui.cpp @@ -2,23 +2,32 @@ #include "devicelibrary.h" #include "entrypoint.h" #include "graphics/buffers.h" +#include "graphics/graphicspipeline.h" #include "graphics/texture.h" #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_vulkan.h" +#include #include VkDescriptorPool imGuiDescriptorPool; void initImGuiWindow() { + if (ImGui::TreeNode("Model Transforms")) { + for (Model *model : Model::getInstances()) { - ImGui::DragFloat3("Object Position", Buffers::getObjPos()); - ImGui::DragFloat3("Camera Position", Buffers::getCamPos()); - ImGui::DragFloat3("Center Position", Buffers::getCenterPos()); - ImGui::DragFloat3("Up Direction", Buffers::getUpDir()); - ImGui::DragFloat("Depth of Field", &Buffers::getDepthField(), 0.1f, 1.0f, + ImGui::DragFloat3(model->getID().c_str(), + const_cast(glm::value_ptr(model->getPos()))); + } + ImGui::TreePop(); + } + + ImGui::DragFloat3("Camera Position", Graphics::getCamPos()); + ImGui::DragFloat3("Center Position", Graphics::getCenterPos()); + ImGui::DragFloat3("Up Direction", Graphics::getUpDir()); + ImGui::DragFloat("Depth of Field", &Graphics::getDepthField(), 0.1f, 1.0f, 180.0f, NULL, ImGuiSliderFlags_AlwaysClamp); - ImGui::DragFloat2("Near and Far fields", Buffers::getDistanceField()); + ImGui::DragFloat2("Near and Far fields", Graphics::getDistanceField()); } void drawTabs() { @@ -37,8 +46,6 @@ void Gui::drawImGui() { ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); - // 2. Show a simple window that we create ourselves. We use a Begin/End pair - // to create a named window. ImGui::Begin("Agnosia Debug"); diff --git a/src/devicelibrary.cpp b/src/devicelibrary.cpp index 6d28958..a1f7cd4 100644 --- a/src/devicelibrary.cpp +++ b/src/devicelibrary.cpp @@ -4,7 +4,6 @@ #include #include #include -#include VkPhysicalDeviceProperties deviceProperties; VkDevice device; @@ -303,18 +302,31 @@ void DeviceControl::createLogicalDevice() { VkPhysicalDeviceVulkan12Features features12{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, .pNext = nullptr, + .shaderSampledImageArrayNonUniformIndexing = true, + .shaderStorageBufferArrayNonUniformIndexing = true, + .shaderStorageImageArrayNonUniformIndexing = true, + .descriptorBindingSampledImageUpdateAfterBind = true, + .descriptorBindingStorageImageUpdateAfterBind = true, + .descriptorBindingStorageBufferUpdateAfterBind = true, + .descriptorBindingUpdateUnusedWhilePending = true, + .descriptorBindingPartiallyBound = true, + .runtimeDescriptorArray = true, + .scalarBlockLayout = true, .bufferDeviceAddress = true, + }; VkPhysicalDeviceVulkan13Features features13{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, .pNext = &features12, .synchronization2 = true, .dynamicRendering = true, + }; VkPhysicalDeviceFeatures featuresBase{ .robustBufferAccess = true, .sampleRateShading = true, .samplerAnisotropy = true, + }; VkPhysicalDeviceFeatures2 deviceFeatures{ diff --git a/src/entrypoint.cpp b/src/entrypoint.cpp index b387701..b296faf 100644 --- a/src/entrypoint.cpp +++ b/src/entrypoint.cpp @@ -3,6 +3,7 @@ #include "entrypoint.h" #include "graphics/buffers.h" #include "graphics/graphicspipeline.h" + #include "graphics/model.h" #include "graphics/render.h" #include "graphics/texture.h" @@ -88,6 +89,22 @@ void createInstance() { } void initVulkan() { + Material *vikingRoomMaterial = + new Material("vikingRoomMaterial", "assets/textures/viking_room.png"); + Material *stanfordDragonMaterial = + new Material("stanfordDragonMaterial", "assets/textures/checkermap.png"); + Material *teapotMaterial = + new Material("teapotMaterial", "assets/textures/checkermap.png"); + Model *vikingRoom = + new Model("vikingRoom", *vikingRoomMaterial, + "assets/models/viking_room.obj", glm::vec3(0.0f, 0.0f, 0.0f)); + Model *stanfordDragon = new Model("stanfordDragon", *stanfordDragonMaterial, + "assets/models/StanfordDragon800k.obj", + glm::vec3(0.0f, 2.0f, 0.0f)); + Model *teapot = + new Model("teapot", *teapotMaterial, "assets/models/teapot.obj", + glm::vec3(1.0f, -3.0f, -1.0f)); + // Initialize volk and continue if successful. volkInitialize(); // Initialize vulkan and set up pipeline. @@ -98,20 +115,22 @@ void initVulkan() { DeviceControl::createLogicalDevice(); volkLoadDevice(DeviceControl::getDevice()); DeviceControl::createSwapChain(window); - Buffers::createMemoryAllocator(vulkaninstance); + Model::createMemoryAllocator(vulkaninstance); DeviceControl::createImageViews(); Buffers::createDescriptorSetLayout(); Graphics::createGraphicsPipeline(); Graphics::createCommandPool(); + // Image creation MUST be after command pool, because command + // buffers. + vikingRoom->populateData(); + stanfordDragon->populateData(); + teapot->populateData(); + Texture::createMaterialTextures(Model::getInstances()); Texture::createColorResources(); Texture::createDepthResources(); - Texture::createTextureImage(); - Texture::createTextureImageView(); - Texture::createTextureSampler(); - Model::loadModel(glm::vec3(0.0, 0.0, 0.0)); - Buffers::createUniformBuffers(); + Buffers::createDescriptorPool(); - Buffers::createDescriptorSets(); + Buffers::createDescriptorSet(Model::getInstances()); Graphics::createCommandBuffer(); Render::createSyncObject(); @@ -131,12 +150,7 @@ void mainLoop() { void cleanup() { Render::cleanupSwapChain(); Graphics::destroyGraphicsPipeline(); - Buffers::destroyUniformBuffer(); - Buffers::destroyDescriptorPool(); - Texture::destroyTextureSampler(); - Texture::destroyTextureImage(); - vkDestroyDescriptorSetLayout(DeviceControl::getDevice(), - Buffers::getDescriptorSetLayout(), nullptr); + Buffers::destroyBuffers(); Render::destroyFenceSemaphores(); Graphics::destroyCommandPool(); @@ -163,6 +177,7 @@ void EntryApp::initialize() { initialized = true; } bool EntryApp::isInitialized() const { return initialized; } GLFWwindow *EntryApp::getWindow() { return window; } void EntryApp::run() { + initWindow(); initVulkan(); mainLoop(); diff --git a/src/graphics/buffers.cpp b/src/graphics/buffers.cpp index 4494f83..78af9a7 100644 --- a/src/graphics/buffers.cpp +++ b/src/graphics/buffers.cpp @@ -1,58 +1,165 @@ #include "../devicelibrary.h" -#include "../types.h" -#include "buffers.h" -#include "texture.h" -#include +#include "buffers.h" +#include "model.h" + #include #include -#include + #include #include + #include #include -#define VMA_STATIC_VULKAN_FUNCTIONS 0 -#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 -#define VMA_IMPLEMENTATION -#include "vk_mem_alloc.h" +std::vector modelSetLayouts; VkBuffer vertexBuffer; VkDeviceMemory vertexBufferMemory; VkBuffer indexBuffer; VkDeviceMemory indexBufferMemory; -std::vector vertices; -// Index buffer definition, showing which points to reuse. -std::vector indices; +uint32_t indicesSize; -VmaAllocator _allocator; -Agnosia_T::GPUPushConstants pushConstants; +// Select a binding for each descriptor type +constexpr int STORAGE_BINDING = 0; +constexpr int SAMPLER_BINDING = 1; +constexpr int IMAGE_BINDING = 2; +// Max count of each descriptor type +// You can query the max values for these with +// physicalDevice.getProperties().limits.maxDescriptrorSet******* +constexpr int STORAGE_COUNT = 65536; +constexpr int SAMPLER_COUNT = 65536; +constexpr int IMAGE_COUNT = 65536; + +// Create descriptor pool VkDescriptorPool descriptorPool; VkDescriptorSetLayout descriptorSetLayout; -std::vector descriptorSets; +VkDescriptorSet descriptorSet; VkCommandPool commandPool; std::vector commandBuffers; -std::vector uniformBuffers; -std::vector uniformBuffersMemory; -std::vector uniformBuffersMapped; - -float objPos[4] = {0.0f, 0.0f, 0.0f, 0.44f}; -float camPos[4] = {2.0f, 2.0f, 2.0f, 0.44f}; -float centerPos[4] = {0.0f, 0.0f, 0.0f, 0.44f}; -float upDir[4] = {0.0f, 0.0f, 1.0f, 0.44f}; -float depthField = 45.0f; -float distanceField[2] = {0.1f, 100.0f}; const int MAX_FRAMES_IN_FLIGHT = 2; -struct UniformBufferObject { - float time; - alignas(16) glm::mat4 model; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 proj; -}; + +void Buffers::createDescriptorSetLayout() { + // Create a table of pointers to data, a Descriptor Set! + + VkDescriptorSetLayoutBinding storageLayoutBinding = { + .binding = STORAGE_BINDING, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = STORAGE_COUNT, + .stageFlags = VK_SHADER_STAGE_ALL, + .pImmutableSamplers = nullptr, + }; + + VkDescriptorSetLayoutBinding samplerLayoutBinding = { + .binding = SAMPLER_BINDING, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = SAMPLER_COUNT, + .stageFlags = VK_SHADER_STAGE_ALL, + .pImmutableSamplers = nullptr, + }; + + VkDescriptorSetLayoutBinding imageLayoutBinding = { + .binding = IMAGE_BINDING, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = IMAGE_COUNT, + .stageFlags = VK_SHADER_STAGE_ALL, + .pImmutableSamplers = nullptr, + }; + + std::vector bindings = { + storageLayoutBinding, imageLayoutBinding, samplerLayoutBinding}; + + std::vector bindingFlags = { + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT, + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT, + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT, + }; + VkDescriptorSetLayoutBindingFlagsCreateInfo setLayoutBindingsFlags = { + .sType = + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .bindingCount = 3, + .pBindingFlags = bindingFlags.data(), + }; + + VkDescriptorSetLayoutCreateInfo layoutInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + + .pNext = &setLayoutBindingsFlags, + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, + .bindingCount = static_cast(bindings.size()), + .pBindings = bindings.data(), + + }; + if (vkCreateDescriptorSetLayout(DeviceControl::getDevice(), &layoutInfo, + nullptr, + &descriptorSetLayout) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor set layout!"); + } +} +void Buffers::createDescriptorPool() { + + std::vector poolSizes = { + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, STORAGE_COUNT}, + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, SAMPLER_COUNT}, + {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, IMAGE_COUNT}, + }; + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = static_cast(poolSizes.size()); + poolInfo.pPoolSizes = poolSizes.data(); + poolInfo.maxSets = 1; + poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT; + + if (vkCreateDescriptorPool(DeviceControl::getDevice(), &poolInfo, nullptr, + &descriptorPool) != VK_SUCCESS) { + throw std::runtime_error("failed to create descriptor pool!"); + } +} +void Buffers::createDescriptorSet(std::vector models) { + VkDescriptorSetAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = descriptorPool; + allocInfo.descriptorSetCount = 1; + allocInfo.pSetLayouts = &descriptorSetLayout; + + if (vkAllocateDescriptorSets(DeviceControl::getDevice(), &allocInfo, + &descriptorSet) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate descriptor sets!"); + } + std::vector imageInfoSet; + imageInfoSet.resize(models.size()); + + for (int i = 0; i < models.size(); i++) { + imageInfoSet[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfoSet[i].imageView = models[i]->getMaterial().getTextureView(); + imageInfoSet[i].sampler = models[i]->getMaterial().getTextureSampler(); + } + + std::vector descriptorWrites{}; + descriptorWrites.resize(models.size()); + + for (int i = 0; i < models.size(); i++) { + descriptorWrites[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i].dstSet = descriptorSet; + descriptorWrites[i].dstBinding = SAMPLER_BINDING; + descriptorWrites[i].dstArrayElement = i; + descriptorWrites[i].descriptorType = + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrites[i].descriptorCount = 1; + descriptorWrites[i].pImageInfo = &imageInfoSet[i]; + } + + vkUpdateDescriptorSets(DeviceControl::getDevice(), + static_cast(descriptorWrites.size()), + descriptorWrites.data(), 0, nullptr); +} uint32_t Buffers::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) { @@ -73,40 +180,6 @@ uint32_t Buffers::findMemoryType(uint32_t typeFilter, throw std::runtime_error("failed to find suitable memory type!"); } -void immediate_submit(std::function &&function) { - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandPool = commandPool; - allocInfo.commandBufferCount = 1; - - VkCommandBuffer commandBuffer; - vkAllocateCommandBuffers(DeviceControl::getDevice(), &allocInfo, - &commandBuffer); - - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - vkBeginCommandBuffer(commandBuffer, &beginInfo); - - function(commandBuffer); - - vkEndCommandBuffer(commandBuffer); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - - vkQueueSubmit(DeviceControl::getGraphicsQueue(), 1, &submitInfo, - VK_NULL_HANDLE); - vkQueueWaitIdle(DeviceControl::getGraphicsQueue()); - - vkFreeCommandBuffers(DeviceControl::getDevice(), commandPool, 1, - &commandBuffer); -} - void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; @@ -142,101 +215,6 @@ void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { vkFreeCommandBuffers(DeviceControl::getDevice(), commandPool, 1, &commandBuffer); } -void Buffers::createMemoryAllocator(VkInstance vkInstance) { - VmaVulkanFunctions vulkanFuncs{ - .vkGetInstanceProcAddr = vkGetInstanceProcAddr, - .vkGetDeviceProcAddr = vkGetDeviceProcAddr, - }; - VmaAllocatorCreateInfo allocInfo{ - .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, - .physicalDevice = DeviceControl::getPhysicalDevice(), - .device = DeviceControl::getDevice(), - .pVulkanFunctions = &vulkanFuncs, - .instance = vkInstance, - .vulkanApiVersion = VK_API_VERSION_1_3, - - }; - vmaCreateAllocator(&allocInfo, &_allocator); -} -Agnosia_T::AllocatedBuffer Buffers::createBuffer(size_t allocSize, - VkBufferUsageFlags usage, - VmaMemoryUsage memUsage) { - // Allocate the buffer we will use for Device Addresses - VkBufferCreateInfo bufferInfo{.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .size = allocSize, - .usage = usage}; - VmaAllocationCreateInfo vmaAllocInfo{ - .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, .usage = memUsage}; - - Agnosia_T::AllocatedBuffer newBuffer; - if (vmaCreateBuffer(_allocator, &bufferInfo, &vmaAllocInfo, &newBuffer.buffer, - &newBuffer.allocation, &newBuffer.info) != VK_SUCCESS) { - throw std::runtime_error("Failed to allocate a buffer using VMA!"); - } - return newBuffer; -} - -Agnosia_T::GPUMeshBuffers -Buffers::sendMesh(std::span indices, - std::span vertices) { - - const size_t vertexBufferSize = vertices.size() * sizeof(Agnosia_T::Vertex); - const size_t indexBufferSize = indices.size() * sizeof(uint32_t); - - Agnosia_T::GPUMeshBuffers newSurface; - - // Create a Vertex Buffer here, infinitely easier than the old Vulkan method! - newSurface.vertexBuffer = createBuffer( - vertexBufferSize, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, - VMA_MEMORY_USAGE_GPU_ONLY); - // Find the address of the vertex buffer! - VkBufferDeviceAddressInfo deviceAddressInfo{ - .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, - .buffer = newSurface.vertexBuffer.buffer, - }; - newSurface.vertexBufferAddress = - vkGetBufferDeviceAddress(DeviceControl::getDevice(), &deviceAddressInfo); - - // Create the index buffer to iterate over and check for duplicate vertices - newSurface.indexBuffer = createBuffer(indexBufferSize, - VK_BUFFER_USAGE_INDEX_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VMA_MEMORY_USAGE_GPU_ONLY); - - Agnosia_T::AllocatedBuffer stagingBuffer = - createBuffer(vertexBufferSize + indexBufferSize, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY); - - void *data = stagingBuffer.allocation->GetMappedData(); - - // Copy the vertex buffer - memcpy(data, vertices.data(), vertexBufferSize); - // Copy the index buffer - memcpy((char *)data + vertexBufferSize, indices.data(), indexBufferSize); - - immediate_submit([&](VkCommandBuffer cmd) { - VkBufferCopy vertexCopy{0}; - vertexCopy.dstOffset = 0; - vertexCopy.srcOffset = 0; - vertexCopy.size = vertexBufferSize; - - vkCmdCopyBuffer(cmd, stagingBuffer.buffer, newSurface.vertexBuffer.buffer, - 1, &vertexCopy); - - VkBufferCopy indexCopy{0}; - indexCopy.dstOffset = 0; - indexCopy.srcOffset = vertexBufferSize; - indexCopy.size = indexBufferSize; - - vkCmdCopyBuffer(cmd, stagingBuffer.buffer, newSurface.indexBuffer.buffer, 1, - &indexCopy); - }); - vmaDestroyBuffer(_allocator, stagingBuffer.buffer, stagingBuffer.allocation); - return newSurface; -} void Buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer &buffer, @@ -269,6 +247,11 @@ void Buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, vkBindBufferMemory(DeviceControl::getDevice(), buffer, bufferMemory, 0); } +VkDescriptorPool &Buffers::getDescriptorPool() { return descriptorPool; } +VkDescriptorSet &Buffers::getDescriptorSet() { return descriptorSet; } +VkDescriptorSetLayout &Buffers::getDescriptorSetLayout() { + return descriptorSetLayout; +} void Buffers::destroyBuffers() { vkDestroyBuffer(DeviceControl::getDevice(), indexBuffer, nullptr); @@ -278,202 +261,10 @@ void Buffers::destroyBuffers() { vkFreeMemory(DeviceControl::getDevice(), vertexBufferMemory, nullptr); } -// ------------------------------ Uniform Buffer Setup -// -------------------------------- // -void Buffers::createDescriptorSetLayout() { - // Create a table of pointers to data, a Descriptor Set! - // --------------------- UBO Layout --------------------- // - VkDescriptorSetLayoutBinding uboLayoutBinding{}; - uboLayoutBinding.binding = 0; - uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - // Model-View-Projection matrix is in a single uniform buffer, so just 1 - // descriptor. - uboLayoutBinding.descriptorCount = 1; - // We are only using this buffer in the vertex shader, so set the flags thus. - uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - // Immutable Samplers is relevant for image sampling. - uboLayoutBinding.pImmutableSamplers = nullptr; - - // --------------- Texture Sampler Layout --------------- // - VkDescriptorSetLayoutBinding samplerLayoutBinding{}; - samplerLayoutBinding.binding = 1; - samplerLayoutBinding.descriptorCount = 1; - samplerLayoutBinding.descriptorType = - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - samplerLayoutBinding.pImmutableSamplers = nullptr; - samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - - std::array bindings = {uboLayoutBinding, - samplerLayoutBinding}; - VkDescriptorSetLayoutCreateInfo layoutInfo{}; - layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - layoutInfo.bindingCount = static_cast(bindings.size()); - layoutInfo.pBindings = bindings.data(); - - if (vkCreateDescriptorSetLayout(DeviceControl::getDevice(), &layoutInfo, - nullptr, - &descriptorSetLayout) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor set layout!"); - } -} -void Buffers::createUniformBuffers() { - // Map the uniform buffer to memory as a pointer we can use to write data to - // later. This stays mapped to memory for the applications lifetime. This - // technique is called "persistent mapping", not having to map the buffer - // every time we need to update it increases performance, though not free - VkDeviceSize bufferSize = sizeof(UniformBufferObject); - - uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); - uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT); - uniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT); - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - uniformBuffers[i], uniformBuffersMemory[i]); - vkMapMemory(DeviceControl::getDevice(), uniformBuffersMemory[i], 0, - bufferSize, 0, &uniformBuffersMapped[i]); - } -} -void Buffers::updateUniformBuffer(uint32_t currentImage) { - // Update the uniform buffer every frame to change the position, but notably, - // use chrono to know exactly how much to move so we aren't locked to the - // framerate as the world time. - - static auto startTime = std::chrono::high_resolution_clock::now(); - // Calculate the time in seconds since rendering has began to floating point - // precision. - auto currentTime = std::chrono::high_resolution_clock::now(); - float time = std::chrono::duration( - currentTime - startTime) - .count(); - - UniformBufferObject ubo{}; - ubo.time = time; - // Modify the model projection transformation to rotate around the Z over - // time. - ubo.model = glm::translate(glm::mat4(1.0f), - glm::vec3(objPos[0], objPos[1], objPos[2])); - // ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(30.0f), - // glm::vec3(0.0f, 0.0f, 1.0f)); - // Modify the view transformation to look at the object from above at a 45 - // degree angle. This takes the eye position, center position, and the up - // direction. - ubo.view = glm::lookAt(glm::vec3(camPos[0], camPos[1], camPos[2]), - glm::vec3(centerPos[0], centerPos[1], centerPos[2]), - glm::vec3(upDir[0], upDir[1], upDir[2])); - // 45 degree field of view, set aspect ratio, and near and far clipping range. - ubo.proj = - glm::perspective(glm::radians(depthField), - DeviceControl::getSwapChainExtent().width / - (float)DeviceControl::getSwapChainExtent().height, - distanceField[0], distanceField[1]); - - // GLM was created for OpenGL, where the Y coordinate was inverted. This - // simply flips the sign. - ubo.proj[1][1] *= -1; - - memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); -} -void Buffers::destroyUniformBuffer() { - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroyBuffer(DeviceControl::getDevice(), uniformBuffers[i], nullptr); - vkFreeMemory(DeviceControl::getDevice(), uniformBuffersMemory[i], nullptr); - } -} -void Buffers::createDescriptorPool() { - // Create a pool to be used to allocate descriptor sets. - std::array poolSizes{}; - poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); - poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - poolSizes[1].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); - - VkDescriptorPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - poolInfo.poolSizeCount = static_cast(poolSizes.size()); - poolInfo.pPoolSizes = poolSizes.data(); - poolInfo.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); - - if (vkCreateDescriptorPool(DeviceControl::getDevice(), &poolInfo, nullptr, - &descriptorPool) != VK_SUCCESS) { - throw std::runtime_error("failed to create descriptor pool!"); - } -} -void Buffers::createDescriptorSets() { - std::vector layouts(MAX_FRAMES_IN_FLIGHT, - descriptorSetLayout); - VkDescriptorSetAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocInfo.descriptorPool = descriptorPool; - allocInfo.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); - allocInfo.pSetLayouts = layouts.data(); - - descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); - if (vkAllocateDescriptorSets(DeviceControl::getDevice(), &allocInfo, - descriptorSets.data()) != VK_SUCCESS) { - throw std::runtime_error("failed to allocate descriptor sets!"); - } - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - VkDescriptorBufferInfo bufferInfo{}; - bufferInfo.buffer = uniformBuffers[i]; - bufferInfo.offset = 0; - bufferInfo.range = sizeof(UniformBufferObject); - - VkDescriptorImageInfo imageInfo{}; - imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - imageInfo.imageView = Texture::getTextureImageView(); - imageInfo.sampler = Texture::getTextureSampler(); - - std::array descriptorWrites{}; - - descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[0].dstSet = descriptorSets[i]; - descriptorWrites[0].dstBinding = 0; - descriptorWrites[0].dstArrayElement = 0; - descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrites[0].descriptorCount = 1; - descriptorWrites[0].pBufferInfo = &bufferInfo; - - descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[1].dstSet = descriptorSets[i]; - descriptorWrites[1].dstBinding = 1; - descriptorWrites[1].dstArrayElement = 0; - descriptorWrites[1].descriptorType = - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - descriptorWrites[1].descriptorCount = 1; - descriptorWrites[1].pImageInfo = &imageInfo; - - vkUpdateDescriptorSets(DeviceControl::getDevice(), - static_cast(descriptorWrites.size()), - descriptorWrites.data(), 0, nullptr); - } -} -void Buffers::destroyDescriptorPool() { - vkDestroyDescriptorPool(DeviceControl::getDevice(), descriptorPool, nullptr); -} -VkDescriptorPool &Buffers::getDescriptorPool() { return descriptorPool; } -std::vector &Buffers::getDescriptorSets() { - return descriptorSets; -} -float *Buffers::getObjPos() { return objPos; } -float *Buffers::getCamPos() { return camPos; } -float *Buffers::getCenterPos() { return centerPos; } -float *Buffers::getUpDir() { return upDir; } -float &Buffers::getDepthField() { return depthField; } -float *Buffers::getDistanceField() { return distanceField; } uint32_t Buffers::getMaxFramesInFlight() { return MAX_FRAMES_IN_FLIGHT; } std::vector &Buffers::getCommandBuffers() { return commandBuffers; } -std::vector &Buffers::getUniformBuffers() { return uniformBuffers; } -std::vector &Buffers::getUniformBuffersMemory() { - return uniformBuffersMemory; -} + VkCommandPool &Buffers::getCommandPool() { return commandPool; } -VkDescriptorSetLayout &Buffers::getDescriptorSetLayout() { - return descriptorSetLayout; -} -std::vector &Buffers::getVertices() { return vertices; } -std::vector &Buffers::getIndices() { return indices; } +uint32_t Buffers::getIndicesSize() { return indicesSize; } diff --git a/src/graphics/buffers.h b/src/graphics/buffers.h index df86cba..be65a6d 100644 --- a/src/graphics/buffers.h +++ b/src/graphics/buffers.h @@ -1,51 +1,37 @@ #pragma once #include -#include + #define VK_NO_PROTOTYPES #include "../types.h" +#include "model.h" #include "volk.h" #include #define GLFW_INCLUDE_VULKAN #include class Buffers { - public: static Agnosia_T::AllocatedBuffer createBuffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memUsage); static void createMemoryAllocator(VkInstance vkInstance); - static Agnosia_T::GPUMeshBuffers - sendMesh(std::span indices, std::span vertices); + static void createDescriptorSetLayout(); + static void createDescriptorSet(std::vector models); + static void createDescriptorPool(); static void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer &buffer, VkDeviceMemory &bufferMemory); static uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags); static void destroyBuffers(); - static void createDescriptorSetLayout(); - static void createUniformBuffers(); - static void updateUniformBuffer(uint32_t currentImage); - static void destroyUniformBuffer(); - static void createDescriptorPool(); - static void createDescriptorSets(); - static void destroyDescriptorPool(); static VkDescriptorPool &getDescriptorPool(); - static VkDescriptorSetLayout &getDescriptorSetLayout(); - static std::vector &getDescriptorSets(); + static VkDescriptorSet &getDescriptorSet(); - static float *getObjPos(); - static float *getCamPos(); - static float *getCenterPos(); - static float *getUpDir(); - static float &getDepthField(); - static float *getDistanceField(); + static VkDescriptorSetLayout &getDescriptorSetLayout(); static uint32_t getMaxFramesInFlight(); static std::vector &getCommandBuffers(); - static std::vector &getUniformBuffers(); - static std::vector &getUniformBuffersMemory(); + static VkCommandPool &getCommandPool(); - static std::vector &getVertices(); - static std::vector &getIndices(); + static uint32_t getIndicesSize(); }; diff --git a/src/graphics/graphicspipeline.cpp b/src/graphics/graphicspipeline.cpp index 68d78b0..0548fe2 100644 --- a/src/graphics/graphicspipeline.cpp +++ b/src/graphics/graphicspipeline.cpp @@ -6,7 +6,18 @@ #include "render.h" #include "texture.h" #include +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + #include +#include + +float camPos[4] = {3.0f, 3.0f, 3.0f, 0.44f}; +float centerPos[4] = {0.0f, 0.0f, 0.0f, 0.44f}; +float upDir[4] = {0.0f, 0.0f, 1.0f, 0.44f}; +float depthField = 45.0f; +float distanceField[2] = {0.1f, 100.0f}; std::vector dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; @@ -84,15 +95,6 @@ void Graphics::createGraphicsPipeline() { vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - auto bindingDescription = Agnosia_T::Vertex::getBindingDescription(); - auto attributeDescriptions = Agnosia_T::Vertex::getAttributeDescriptions(); - - vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; - vertexInputInfo.vertexAttributeDescriptionCount = - static_cast(attributeDescriptions.size()); - vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); - // ------------------- STAGE 5 - RASTERIZATION ----------------- // // Take Vertex shader vertices and fragmentize them for the frament shader. // The rasterizer also can perform depth testing, face culling, and scissor @@ -176,7 +178,7 @@ void Graphics::createGraphicsPipeline() { dynamicState.pDynamicStates = dynamicStates.data(); VkPushConstantRange pushConstant{ - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .stageFlags = VK_SHADER_STAGE_ALL, .offset = 0, .size = sizeof(Agnosia_T::GPUPushConstants), }; @@ -351,26 +353,45 @@ void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer, scissor.offset = {0, 0}; scissor.extent = DeviceControl::getSwapChainExtent(); vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + int texID = 0; + for (Model *model : Model::getInstances()) { - Agnosia_T::GPUMeshBuffers Model = - Buffers::sendMesh(Buffers::getIndices(), Buffers::getVertices()); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, + pipelineLayout, 0, 1, &Buffers::getDescriptorSet(), + 0, nullptr); - Agnosia_T::GPUPushConstants pushConsts; - pushConsts.vertexBuffer = Model.vertexBufferAddress; + Agnosia_T::GPUPushConstants pushConsts; + pushConsts.vertexBuffer = model->getBuffers().vertexBufferAddress; + pushConsts.objPosition = model->getPos(); + pushConsts.textureID = texID; - vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, - 0, sizeof(Agnosia_T::GPUPushConstants), &pushConsts); - vkCmdBindIndexBuffer(commandBuffer, Model.indexBuffer.buffer, 0, - VK_INDEX_TYPE_UINT32); + pushConsts.model = + glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); - vkCmdBindDescriptorSets( - commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, - &Buffers::getDescriptorSets()[Render::getCurrentFrame()], 0, nullptr); + pushConsts.view = + glm::lookAt(glm::vec3(camPos[0], camPos[1], camPos[2]), + glm::vec3(centerPos[0], centerPos[1], centerPos[2]), + glm::vec3(upDir[0], upDir[1], upDir[2])); - vkCmdDrawIndexed(commandBuffer, - static_cast(Buffers::getIndices().size()), 1, 0, 0, - 0); + pushConsts.proj = + glm::perspective(glm::radians(depthField), + DeviceControl::getSwapChainExtent().width / + (float)DeviceControl::getSwapChainExtent().height, + distanceField[0], distanceField[1]); + // GLM was created for OpenGL, where the Y coordinate was inverted. This + // simply flips the sign. + pushConsts.proj[1][1] *= -1; + vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_ALL, 0, + sizeof(Agnosia_T::GPUPushConstants), &pushConsts); + + vkCmdBindIndexBuffer(commandBuffer, model->getBuffers().indexBuffer.buffer, + 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(commandBuffer, static_cast(model->getIndices()), + 1, 0, 0, 0); + texID++; + } ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commandBuffer); vkCmdEndRendering(commandBuffer); @@ -403,10 +424,16 @@ void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer, .pImageMemoryBarriers = &prePresentImageBarrier, }; - vkCmdPipelineBarrier2( - Buffers ::getCommandBuffers()[Render::getCurrentFrame()], &depInfo); + vkCmdPipelineBarrier2(Buffers::getCommandBuffers()[Render::getCurrentFrame()], + &depInfo); if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { throw std::runtime_error("failed to record command buffer!"); } } + +float *Graphics::getCamPos() { return camPos; } +float *Graphics::getCenterPos() { return centerPos; } +float *Graphics::getUpDir() { return upDir; } +float &Graphics::getDepthField() { return depthField; } +float *Graphics::getDistanceField() { return distanceField; } diff --git a/src/graphics/graphicspipeline.h b/src/graphics/graphicspipeline.h index e177a45..ad64512 100644 --- a/src/graphics/graphicspipeline.h +++ b/src/graphics/graphicspipeline.h @@ -13,4 +13,10 @@ public: static void createCommandBuffer(); static void recordCommandBuffer(VkCommandBuffer cmndBuffer, uint32_t imageIndex); + + static float *getCamPos(); + static float *getCenterPos(); + static float *getUpDir(); + static float &getDepthField(); + static float *getDistanceField(); }; diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp new file mode 100644 index 0000000..04fd909 --- /dev/null +++ b/src/graphics/material.cpp @@ -0,0 +1,19 @@ +#include "material.h" + +Material::Material(const std::string &matID, const std::string &texPath) + : ID(matID), texturePath(texPath) {} + +std::string Material::getID() const { return ID; } +std::string Material::getTexturePath() const { return texturePath; } + +VkImage &Material::getTextureImage() { return this->textureImage; } +VkImageView &Material::getTextureView() { return this->textureImageView; } +VkSampler &Material::getTextureSampler() { return this->textureSampler; } + +void Material::setTextureImage(VkImage image) { this->textureImage = image; } +void Material::setTextureView(VkImageView imageView) { + this->textureImageView = imageView; +} +void Material::setTextureSampler(VkSampler sampler) { + this->textureSampler = sampler; +} diff --git a/src/graphics/material.h b/src/graphics/material.h new file mode 100644 index 0000000..18f3128 --- /dev/null +++ b/src/graphics/material.h @@ -0,0 +1,26 @@ + +#define VK_NO_PROTOTYPES +#include "volk.h" +#include + +class Material { +protected: + std::string ID; + std::string texturePath; + + VkImage textureImage; + VkImageView textureImageView; + VkSampler textureSampler; + +public: + Material(const std::string &matID, const std::string &texPath); + std::string getID() const; + std::string getTexturePath() const; + VkImage &getTextureImage(); + VkImageView &getTextureView(); + VkSampler &getTextureSampler(); + + void setTextureImage(VkImage image); + void setTextureView(VkImageView imageView); + void setTextureSampler(VkSampler sampler); +}; diff --git a/src/graphics/model.cpp b/src/graphics/model.cpp index bef6691..3a39550 100644 --- a/src/graphics/model.cpp +++ b/src/graphics/model.cpp @@ -1,13 +1,25 @@ #include "buffers.h" #include "model.h" -#include +#include +#include +#include #define TINY_OBJ_IMPLEMENTATION +#include #include - #define GLM_ENABLE_EXPERIMENTAL +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include "../devicelibrary.h" +#include #include +#define VMA_STATIC_VULKAN_FUNCTIONS 0 +#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 +#define VMA_IMPLEMENTATION +#include "vk_mem_alloc.h" + +std::vector Model::instances; +VmaAllocator _allocator; namespace std { template <> struct hash { size_t operator()(Agnosia_T::Vertex const &vertex) const { @@ -18,15 +30,90 @@ template <> struct hash { } }; } // namespace std +void Model::createMemoryAllocator(VkInstance vkInstance) { + VmaVulkanFunctions vulkanFuncs{ + .vkGetInstanceProcAddr = vkGetInstanceProcAddr, + .vkGetDeviceProcAddr = vkGetDeviceProcAddr, + }; + VmaAllocatorCreateInfo allocInfo{ + .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, + .physicalDevice = DeviceControl::getPhysicalDevice(), + .device = DeviceControl::getDevice(), + .pVulkanFunctions = &vulkanFuncs, + .instance = vkInstance, + .vulkanApiVersion = VK_API_VERSION_1_3, -const std::string MODEL_PATH = "assets/models/viking_room.obj"; + }; + vmaCreateAllocator(&allocInfo, &_allocator); +} +Agnosia_T::AllocatedBuffer createBuffer(size_t allocSize, + VkBufferUsageFlags usage, + VmaMemoryUsage memUsage) { + // Allocate the buffer we will use for Device Addresses + VkBufferCreateInfo bufferInfo{.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .size = allocSize, + .usage = usage}; + VmaAllocationCreateInfo vmaAllocInfo{ + .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, .usage = memUsage}; -void Model::loadModel(glm::vec3 position) { + Agnosia_T::AllocatedBuffer newBuffer; + if (vmaCreateBuffer(_allocator, &bufferInfo, &vmaAllocInfo, &newBuffer.buffer, + &newBuffer.allocation, &newBuffer.info) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate a buffer using VMA!"); + } + return newBuffer; +} +void immediate_submit(std::function &&function) { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = Buffers::getCommandPool(); + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(DeviceControl::getDevice(), &allocInfo, + &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + function(commandBuffer); + + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(DeviceControl::getGraphicsQueue(), 1, &submitInfo, + VK_NULL_HANDLE); + vkQueueWaitIdle(DeviceControl::getGraphicsQueue()); + + vkFreeCommandBuffers(DeviceControl::getDevice(), Buffers::getCommandPool(), 1, + &commandBuffer); +} + +Model::Model(const std::string &modelID, const Material &material, + const std::string &modelPath, const glm::vec3 &objPos) + : ID(modelID), material(material), objPosition(objPos), + modelPath(modelPath) { + instances.push_back(this); +} + +void Model::populateData() { + + std::vector vertices; + // Index buffer definition, showing which points to reuse. + std::vector indices; tinyobj::ObjReaderConfig readerConfig; - tinyobj::ObjReader reader; - if (!reader.ParseFromFile(MODEL_PATH, readerConfig)) { + if (!reader.ParseFromFile(modelPath, readerConfig)) { if (!reader.Error().empty()) { throw std::runtime_error(reader.Error()); } @@ -38,15 +125,16 @@ void Model::loadModel(glm::vec3 position) { auto &attrib = reader.GetAttrib(); auto &shapes = reader.GetShapes(); auto &materials = reader.GetMaterials(); + std::unordered_map uniqueVertices{}; for (const auto &shape : shapes) { for (const auto &index : shape.mesh.indices) { Agnosia_T::Vertex vertex{}; - vertex.pos = {attrib.vertices[3 * index.vertex_index + 0] + position.x, - attrib.vertices[3 * index.vertex_index + 1] + position.y, - attrib.vertices[3 * index.vertex_index + 2] + position.z}; + vertex.pos = {attrib.vertices[3 * index.vertex_index + 0], + attrib.vertices[3 * index.vertex_index + 1], + attrib.vertices[3 * index.vertex_index + 2]}; // TODO: Small fix here, handle if there are no UV's unwrapped for the // model. @@ -58,11 +146,76 @@ void Model::loadModel(glm::vec3 position) { vertex.color = {1.0f, 1.0f, 1.0f}; if (uniqueVertices.count(vertex) == 0) { - uniqueVertices[vertex] = - static_cast(Buffers::getVertices().size()); - Buffers::getVertices().push_back(vertex); + uniqueVertices[vertex] = static_cast(vertices.size()); + vertices.push_back(vertex); } - Buffers::getIndices().push_back(uniqueVertices[vertex]); + indices.push_back(uniqueVertices[vertex]); } } + + const size_t vertexBufferSize = vertices.size() * sizeof(Agnosia_T::Vertex); + const size_t indexBufferSize = indices.size() * sizeof(uint32_t); + + Agnosia_T::GPUMeshBuffers newSurface; + + // Create a Vertex Buffer here, infinitely easier than the old Vulkan method! + newSurface.vertexBuffer = createBuffer( + vertexBufferSize, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VMA_MEMORY_USAGE_GPU_ONLY); + // Find the address of the vertex buffer! + VkBufferDeviceAddressInfo deviceAddressInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = newSurface.vertexBuffer.buffer, + }; + newSurface.vertexBufferAddress = + vkGetBufferDeviceAddress(DeviceControl::getDevice(), &deviceAddressInfo); + + // Create the index buffer to iterate over and check for duplicate vertices + newSurface.indexBuffer = createBuffer(indexBufferSize, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VMA_MEMORY_USAGE_GPU_ONLY); + + Agnosia_T::AllocatedBuffer stagingBuffer = + createBuffer(vertexBufferSize + indexBufferSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + + void *data = stagingBuffer.allocation->GetMappedData(); + + // Copy the vertex buffer + memcpy(data, vertices.data(), vertexBufferSize); + // Copy the index buffer + memcpy((char *)data + vertexBufferSize, indices.data(), indexBufferSize); + + immediate_submit([&](VkCommandBuffer cmd) { + VkBufferCopy vertexCopy{0}; + vertexCopy.dstOffset = 0; + vertexCopy.srcOffset = 0; + vertexCopy.size = vertexBufferSize; + + vkCmdCopyBuffer(cmd, stagingBuffer.buffer, newSurface.vertexBuffer.buffer, + 1, &vertexCopy); + + VkBufferCopy indexCopy{0}; + indexCopy.dstOffset = 0; + indexCopy.srcOffset = vertexBufferSize; + indexCopy.size = indexBufferSize; + + vkCmdCopyBuffer(cmd, stagingBuffer.buffer, newSurface.indexBuffer.buffer, 1, + &indexCopy); + }); + vmaDestroyBuffer(_allocator, stagingBuffer.buffer, stagingBuffer.allocation); + + this->buffers = newSurface; + this->objPosition = objPosition; + this->indiceCount = indices.size(); } + +std::string Model::getID() { return this->ID; } +glm::vec3 &Model::getPos() { return this->objPosition; } +Material &Model::getMaterial() { return this->material; } +Agnosia_T::GPUMeshBuffers Model::getBuffers() { return this->buffers; } +uint32_t Model::getIndices() { return this->indiceCount; } +const std::vector &Model::getInstances() { return instances; } diff --git a/src/graphics/model.h b/src/graphics/model.h index d00517b..d2bdd0f 100644 --- a/src/graphics/model.h +++ b/src/graphics/model.h @@ -1,8 +1,39 @@ #pragma once -#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include +#include + +#define VK_NO_PROTOTYPES + +#include "../types.h" + +#include "material.h" +#include "volk.h" #include +#include class Model { +protected: + std::string ID; + Agnosia_T::GPUMeshBuffers buffers; + Material material; + glm::vec3 objPosition; + uint32_t indiceCount; + std::string modelPath; + static std::vector instances; + public: - static void loadModel(glm::vec3 position); + Model(const std::string &modelID, const Material &material, + const std::string &modelPath, const glm::vec3 &opjPos); + + static void createMemoryAllocator(VkInstance instance); + static const std::vector &getInstances(); + + void populateData(); + + Agnosia_T::GPUMeshBuffers getBuffers(); + std::string getID(); + glm::vec3 &getPos(); + Material &getMaterial(); + std::string getModelPath(); + uint32_t getIndices(); }; diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 40aacf2..ea5bacb 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -59,16 +59,12 @@ void Render::drawFrame() { throw std::runtime_error("failed to acquire swap chain image!"); } - Buffers::updateUniformBuffer(currentFrame); - vkResetFences(DeviceControl::getDevice(), 1, &inFlightFences[currentFrame]); vkResetCommandBuffer(Buffers::getCommandBuffers()[currentFrame], /*VkCommandBufferResetFlagBits*/ 0); Graphics::recordCommandBuffer(Buffers::getCommandBuffers()[currentFrame], imageIndex); - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), - Buffers::getCommandBuffers()[currentFrame]); VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp index 04027c3..db109a6 100644 --- a/src/graphics/texture.cpp +++ b/src/graphics/texture.cpp @@ -1,6 +1,7 @@ #include "../devicelibrary.h" #include "buffers.h" #include "texture.h" +#include #include #include #define STB_IMAGE_IMPLEMENTATION @@ -8,14 +9,10 @@ uint32_t mipLevels; -VkImage textureImage; VkDeviceMemory textureImageMemory; VkPipelineStageFlags sourceStage; VkPipelineStageFlags destinationStage; -VkImageView textureImageView; -VkSampler textureSampler; - VkImage colorImage; VkImageView colorImageView; VkDeviceMemory colorImageMemory; @@ -24,8 +21,6 @@ VkImage depthImage; VkImageView depthImageView; VkDeviceMemory depthImageMemory; -std::string TEXTURE_PATH = "assets/textures/viking_room.png"; - void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits sampleNum, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, @@ -290,118 +285,119 @@ void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t textureWidth, } // -------------------------------- Image Libraries // ------------------------------- // -void Texture::createTextureImage() { +void Texture::createMaterialTextures(std::vector models) { // Import pixels from image with data on color channels, width and height, and // colorspace! Its a lot of kind of complicated memory calls to bring it from // a file -> to a buffer -> to a image object. - int textureWidth, textureHeight, textureChannels; - stbi_uc *pixels = stbi_load(TEXTURE_PATH.c_str(), &textureWidth, - &textureHeight, &textureChannels, STBI_rgb_alpha); - mipLevels = static_cast(std::floor( - std::log2(std::max(textureWidth, textureHeight)))) + - 1; + for (Model *model : models) { - VkDeviceSize imageSize = textureWidth * textureHeight * 4; + int textureWidth, textureHeight, textureChannels; - if (!pixels) { - throw std::runtime_error("Failed to load texture!"); - } - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - Buffers::createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - stagingBuffer, stagingBufferMemory); + stbi_uc *pixels = + stbi_load(model->getMaterial().getTexturePath().c_str(), &textureWidth, + &textureHeight, &textureChannels, STBI_rgb_alpha); - void *data; - vkMapMemory(DeviceControl::getDevice(), stagingBufferMemory, 0, imageSize, 0, - &data); - memcpy(data, pixels, static_cast(imageSize)); - vkUnmapMemory(DeviceControl::getDevice(), stagingBufferMemory); + mipLevels = static_cast(std::floor( + std::log2(std::max(textureWidth, textureHeight)))) + + 1; - stbi_image_free(pixels); + VkDeviceSize imageSize = textureWidth * textureHeight * 4; - createImage(textureWidth, textureHeight, mipLevels, VK_SAMPLE_COUNT_1_BIT, - VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, - textureImageMemory); + if (!pixels) { + throw std::runtime_error("Failed to load texture!"); + } + VkBuffer stagingBuffer; + VkDeviceMemory stagingBufferMemory; + Buffers::createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + stagingBuffer, stagingBufferMemory); - transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels); - copyBufferToImage(stagingBuffer, textureImage, - static_cast(textureWidth), - static_cast(textureHeight)); + void *data; + vkMapMemory(DeviceControl::getDevice(), stagingBufferMemory, 0, imageSize, + 0, &data); + memcpy(data, pixels, static_cast(imageSize)); + vkUnmapMemory(DeviceControl::getDevice(), stagingBufferMemory); - vkDestroyBuffer(DeviceControl::getDevice(), stagingBuffer, nullptr); - vkFreeMemory(DeviceControl::getDevice(), stagingBufferMemory, nullptr); + stbi_image_free(pixels); - generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_SRGB, textureWidth, - textureHeight, mipLevels); -} -void Texture::createTextureImageView() { - // Create a texture image view, which is a struct of information about the - // image. - textureImageView = - DeviceControl::createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, - VK_IMAGE_ASPECT_COLOR_BIT, mipLevels); -} -void Texture::createTextureSampler() { - // Create a sampler to access and parse the texture object. - VkSamplerCreateInfo samplerInfo{}; - samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - // These two options define the filtering method when sampling the texture. - // It also handles zooming in versus out, min vs mag! - samplerInfo.magFilter = VK_FILTER_LINEAR; // TODO: CUBIC - samplerInfo.minFilter = VK_FILTER_LINEAR; // TODO: CUBIC + createImage(textureWidth, textureHeight, mipLevels, VK_SAMPLE_COUNT_1_BIT, + VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + model->getMaterial().getTextureImage(), textureImageMemory); - // These options define UVW edge modes, ClampToEdge extends the last pixels to - // the edges when larger than the UVW. - samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + transitionImageLayout(model->getMaterial().getTextureImage(), + VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels); + copyBufferToImage(stagingBuffer, model->getMaterial().getTextureImage(), + static_cast(textureWidth), + static_cast(textureHeight)); - VkPhysicalDeviceProperties properties{}; - vkGetPhysicalDeviceProperties(DeviceControl::getPhysicalDevice(), - &properties); - // Enable or Disable Anisotropy, and set the amount. - samplerInfo.anisotropyEnable = VK_TRUE; - samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; + vkDestroyBuffer(DeviceControl::getDevice(), stagingBuffer, nullptr); + vkFreeMemory(DeviceControl::getDevice(), stagingBufferMemory, nullptr); - // When sampling with Clamp to Border, the border color is defined here. - samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - // Normalizing coordinates changes texCoords from [0, texWidth] to [0, 1]. - // This is what should ALWAYS be used, because it means you can use varying - // texture sizes. Another TODO: Normalizing - samplerInfo.unnormalizedCoordinates = VK_FALSE; - // Compare texels to a value and use the output in filtering! - // This is mainly used in percentage-closer filtering on shadow maps, this - // will be revisted eventually... - samplerInfo.compareEnable = VK_FALSE; - samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + generateMipmaps(model->getMaterial().getTextureImage(), + VK_FORMAT_R8G8B8A8_SRGB, textureWidth, textureHeight, + mipLevels); + // Create a texture image view, which is a struct of information about the + // image. + model->getMaterial().setTextureView(DeviceControl::createImageView( + model->getMaterial().getTextureImage(), VK_FORMAT_R8G8B8A8_SRGB, + VK_IMAGE_ASPECT_COLOR_BIT, mipLevels)); - // Mipmaps are basically LoD's for textures, different resolutions to load - // based on distance. These settings simply describe how to apply mipmapping. - samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerInfo.mipLodBias = 0.0f; - samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = VK_LOD_CLAMP_NONE; + // Create a sampler to access and parse the texture object. - if (vkCreateSampler(DeviceControl::getDevice(), &samplerInfo, nullptr, - &textureSampler) != VK_SUCCESS) { - throw std::runtime_error("failed to create texture sampler!"); + VkSamplerCreateInfo samplerInfo{}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + // These two options define the filtering method when sampling the texture. + // It also handles zooming in versus out, min vs mag! + samplerInfo.magFilter = VK_FILTER_LINEAR; // TODO: CUBIC + samplerInfo.minFilter = VK_FILTER_LINEAR; // TODO: CUBIC + + // These options define UVW edge modes, ClampToEdge extends the last pixels + // to the edges when larger than the UVW. + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + + VkPhysicalDeviceProperties properties{}; + vkGetPhysicalDeviceProperties(DeviceControl::getPhysicalDevice(), + &properties); + // Enable or Disable Anisotropy, and set the amount. + samplerInfo.anisotropyEnable = VK_TRUE; + samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; + + // When sampling with Clamp to Border, the border color is defined here. + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + // Normalizing coordinates changes texCoords from [0, texWidth] to [0, 1]. + // This is what should ALWAYS be used, because it means you can use varying + // texture sizes. Another TODO: Normalizing + samplerInfo.unnormalizedCoordinates = VK_FALSE; + // Compare texels to a value and use the output in filtering! + // This is mainly used in percentage-closer filtering on shadow maps, this + // will be revisted eventually... + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + + // Mipmaps are basically LoD's for textures, different resolutions to load + // based on distance. These settings simply describe how to apply + // mipmapping. + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = VK_LOD_CLAMP_NONE; + + if (vkCreateSampler(DeviceControl::getDevice(), &samplerInfo, nullptr, + &model->getMaterial().getTextureSampler()) != + VK_SUCCESS) { + throw std::runtime_error("failed to create texture sampler!"); + } } } -void Texture::destroyTextureSampler() { - vkDestroySampler(DeviceControl::getDevice(), textureSampler, nullptr); - vkDestroyImageView(DeviceControl::getDevice(), textureImageView, nullptr); -} -void Texture::destroyTextureImage() { - vkDestroyImage(DeviceControl::getDevice(), textureImage, nullptr); - vkFreeMemory(DeviceControl::getDevice(), textureImageMemory, nullptr); -} + VkFormat Texture::findDepthFormat() { return findSupportedFormat( {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, @@ -438,8 +434,6 @@ void Texture::createDepthResources() { // ---------------------------- Getters & Setters // ---------------------------------// uint32_t Texture::getMipLevels() { return mipLevels; } -VkImageView &Texture::getTextureImageView() { return textureImageView; } -VkSampler &Texture::getTextureSampler() { return textureSampler; } VkImage &Texture::getColorImage() { return colorImage; } VkImageView &Texture::getColorImageView() { return colorImageView; } diff --git a/src/graphics/texture.h b/src/graphics/texture.h index 940bc2e..f55d19d 100644 --- a/src/graphics/texture.h +++ b/src/graphics/texture.h @@ -1,12 +1,14 @@ #pragma once +#include "model.h" + #define VK_NO_PROTOTYPES + #include "volk.h" #include class Texture { public: - static void createTextureImage(); - static void createTextureImageView(); - static void createTextureSampler(); + static const uint32_t TEXTURE_COUNT = 2; + static void createMaterialTextures(std::vector models); static void destroyTextureImage(); static void destroyTextureSampler(); static VkFormat findDepthFormat(); @@ -14,8 +16,6 @@ public: static void createColorResources(); // ------------ Getters & Setters ------------ // static uint32_t getMipLevels(); - static VkImageView &getTextureImageView(); - static VkSampler &getTextureSampler(); static VkImage &getColorImage(); static VkImageView &getColorImageView(); diff --git a/src/shaders/common.glsl b/src/shaders/common.glsl new file mode 100644 index 0000000..f117a1d --- /dev/null +++ b/src/shaders/common.glsl @@ -0,0 +1,21 @@ +#extension GL_EXT_buffer_reference : require +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_nonuniform_qualifier : require + +struct Vertex { + vec3 pos; + vec3 color; + vec2 texCoord; +}; + +layout(buffer_reference, scalar) readonly buffer VertexBuffer{ + Vertex vertices[]; +}; +layout( push_constant, scalar ) uniform constants { + VertexBuffer vertBuffer; + vec3 objPos; + int textureID; + mat4 model; + mat4 view; + mat4 proj; +} PushConstants; diff --git a/src/shaders/fragment.frag b/src/shaders/fragment.frag index ea2e60b..27d6329 100644 --- a/src/shaders/fragment.frag +++ b/src/shaders/fragment.frag @@ -1,5 +1,9 @@ #version 450 -layout(binding = 1) uniform sampler2D texSampler; +#extension GL_GOOGLE_include_directive : enable +#include "common.glsl" + +layout(binding = 1) uniform sampler2D texSampler[]; + layout(location = 0) in vec3 fragColor; layout(location = 1) in vec2 fragTexCoord; @@ -7,6 +11,5 @@ layout(location = 1) in vec2 fragTexCoord; layout(location = 0) out vec4 outColor; void main() { - - outColor = vec4(texture(texSampler, fragTexCoord).rgb, 1.0); + outColor = texture(texSampler[PushConstants.textureID], fragTexCoord); } diff --git a/src/shaders/vertex.vert b/src/shaders/vertex.vert index dab8105..ab0b4d2 100644 --- a/src/shaders/vertex.vert +++ b/src/shaders/vertex.vert @@ -1,32 +1,15 @@ #version 450 -#extension GL_EXT_buffer_reference : require - -layout(binding = 0) uniform UniformBufferObject { - float time; - mat4 model; - mat4 view; - mat4 proj; -} ubo; -struct Vertex { - - vec3 pos; - vec3 color; - vec2 texCoord; -}; - -layout(buffer_reference, std430) readonly buffer VertexBuffer{ - Vertex vertices[]; -}; -layout( push_constant ) uniform constants { - VertexBuffer vertBuffer; -}PushConstants; +#extension GL_GOOGLE_include_directive : enable +#include "common.glsl" layout(location = 0) out vec3 fragColor; layout(location = 1) out vec2 fragTexCoord; void main() { - Vertex v = PushConstants.vertBuffer.vertices[gl_VertexIndex]; - gl_Position = ubo.proj * ubo.view * ubo.model * vec4(v.pos, 1.0f); - fragColor = v.color.xyz; - fragTexCoord = v.texCoord; + Vertex vertex = PushConstants.vertBuffer.vertices[gl_VertexIndex]; + + gl_Position = PushConstants.proj * PushConstants.view * PushConstants.model * + vec4(vertex.pos + PushConstants.objPos, 1.0f); + fragColor = vertex.color.rgb; + fragTexCoord = vertex.texCoord; } diff --git a/src/types.h b/src/types.h index 858bba4..5108fc4 100644 --- a/src/types.h +++ b/src/types.h @@ -1,9 +1,7 @@ #pragma once #define GLM_FORCE_DEPTH_ZERO_TO_ONE - #include "vk_mem_alloc.h" -#include #include class Agnosia_T { @@ -11,38 +9,10 @@ public: struct Vertex { // This defines what a vertex is! // We control the position, color and texture coordinate here! - alignas(16) glm::vec3 pos; - alignas(16) glm::vec3 color; - alignas(8) glm::vec2 texCoord; + glm::vec3 pos; + glm::vec3 color; + glm::vec2 texCoord; - static VkVertexInputBindingDescription getBindingDescription() { - VkVertexInputBindingDescription bindingDescription{}; - bindingDescription.binding = 0; - bindingDescription.stride = sizeof(Vertex); - bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - return bindingDescription; - } - static std::array - getAttributeDescriptions() { - std::array attributeDescriptions{}; - - attributeDescriptions[0].binding = 0; - attributeDescriptions[0].location = 0; - attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[0].offset = offsetof(Vertex, pos); - - attributeDescriptions[1].binding = 0; - attributeDescriptions[1].location = 1; - attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[1].offset = offsetof(Vertex, color); - - attributeDescriptions[2].binding = 0; - attributeDescriptions[2].location = 2; - attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; - attributeDescriptions[2].offset = offsetof(Vertex, texCoord); - return attributeDescriptions; - } bool operator==(const Vertex &other) const { return pos == other.pos && color == other.color && texCoord == other.texCoord; @@ -60,5 +30,10 @@ public: }; struct GPUPushConstants { VkDeviceAddress vertexBuffer; + glm::vec3 objPosition; + int textureID; + glm::mat4 model; + glm::mat4 view; + glm::mat4 proj; }; };