From d5190c8207e3e001e28a90d9ef5c19fa3c11a75d Mon Sep 17 00:00:00 2001 From: Lillian Salehi Date: Wed, 27 Nov 2024 15:54:35 -0600 Subject: [PATCH] Replaced Index and Vertex buffer descriptions using descriptor sets with the VMA Library, massively simplifying the creation of the Vertex and Index buffer, and uses push constants --- .../agnosiaimgui.cpp.D917B7EB41E8A56F.idx | Bin 0 -> 3384 bytes .../index/agnosiaimgui.h.C442C0E7D513336B.idx | Bin 388 -> 378 bytes .../index/buffers.cpp.FC2CC275D910BCB7.idx | Bin 9970 -> 15906 bytes .../index/buffers.h.CC15E40B6084C10D.idx | Bin 1476 -> 2930 bytes .../devicelibrary.cpp.A6A50BF3BD186A09.idx | Bin 11772 -> 11922 bytes .../index/entrypoint.cpp.9286A9B0BD8A276E.idx | Bin 4590 -> 5252 bytes .../index/entrypoint.h.1BBE0276DA7F0CE2.idx | Bin 1024 -> 1044 bytes .../graphicspipeline.cpp.F94E3ACB17FA6762.idx | Bin 10508 -> 11786 bytes .../graphicspipeline.h.093A3C67F46BDBAE.idx | Bin 908 -> 808 bytes .../index/main.cpp.6FA2B2FABDE63972.idx | Bin 630 -> 710 bytes .../index/model.cpp.56D1ED026EF1D5F1.idx | Bin 2122 -> 2466 bytes .../clangd/index/model.h.C3ECCCBE7C04E4C8.idx | Bin 436 -> 348 bytes .../index/render.cpp.657B8B8CBD5B3B6B.idx | Bin 7400 -> 5986 bytes .../index/render.h.A0C955D8D0DA424C.idx | Bin 762 -> 662 bytes .../index/texture.cpp.14763AFB742F8112.idx | Bin 11076 -> 12892 bytes .../index/texture.h.712506A996DB5236.idx | Bin 962 -> 1518 bytes .../clangd/index/types.h.2A872A9A515562E6.idx | Bin 0 -> 2510 bytes .../index/vk_mem_alloc.h.F667345C57A24872.idx | Bin 0 -> 54346 bytes Makefile | 1 - assets/models/untitled.mtl | 12 + assets/models/untitled.obj | 30 + assets/textures/aza.png | Bin 0 -> 591476 bytes imgui.ini | 4 +- lib/vk_mem_alloc.h | 18676 ++++++++++++++++ src/devicelibrary.cpp | 9 +- src/entrypoint.cpp | 15 +- src/entrypoint.h | 1 + src/graphics/buffers.cpp | 210 +- src/graphics/buffers.h | 59 +- src/graphics/graphicspipeline.cpp | 27 +- src/graphics/model.cpp | 16 +- src/graphics/model.h | 4 +- src/graphics/render.cpp | 1 + src/main.cpp | 1 + src/shaders/vertex.vert | 25 +- src/types.h | 64 + 36 files changed, 19001 insertions(+), 154 deletions(-) create mode 100644 .cache/clangd/index/agnosiaimgui.cpp.D917B7EB41E8A56F.idx create mode 100644 .cache/clangd/index/types.h.2A872A9A515562E6.idx create mode 100644 .cache/clangd/index/vk_mem_alloc.h.F667345C57A24872.idx create mode 100644 assets/models/untitled.mtl create mode 100644 assets/models/untitled.obj create mode 100644 assets/textures/aza.png create mode 100644 lib/vk_mem_alloc.h create mode 100644 src/types.h diff --git a/.cache/clangd/index/agnosiaimgui.cpp.D917B7EB41E8A56F.idx b/.cache/clangd/index/agnosiaimgui.cpp.D917B7EB41E8A56F.idx new file mode 100644 index 0000000000000000000000000000000000000000..b61d11b557691b9f06ce76efe1fa7a19fc13157b GIT binary patch literal 3384 zcmZ9P30PBC7RMif@JPs>7n1-1g|HNe1Ox=cjU6HiK}4)b3JC-x5CK6zD@E%RwaDNC zqzj0_8t zGchb!9l1iM$^Xg>!!SPl<>lvQC`>Tyds7TsQClB(Kg!gTbE9LlRUO>KRz3}`uXlfY zSlcw(y7xa>0X5Ov78j3|6dtoL3(|cnzwVemH_dO-7G6~Sd5PbmL|X7x>Dr2xvHTr^ zp1?YR$FrURZLIsY&=0MPWeGWBkN?p*XJAk*Uf^oKt1Kq;N5|XW9P6U(jCzFb(M z&}I|Q`uM-Du~{$urJr~7h`>93-`}kVcc>gR&EYdV>CD=uLh;vE^EX9=CT*@*wr1Ut z&2t=B<{<&6I~p&&JSpg3q1?UBBy~u1dT?4#eu?A3_QpHk-g@$(PF4_mChxZqr!)JX zE_xo?%qwu&DL%KX@FauobUanpwf36+>g(bS=USrO75WcR`K7_%u}<&$GIAF;AWHRf z!=IftciI$xWNh-uT(_n)fRiQgHTST&bM{2VFZK9AUvo(qmViiCv`zI0!{z?@|+ z#4P?ly)D`yzPqL=nMYy6v_0LPFQBrh}WF+vxaFpmoEKyLDN{SCM~a&j$t{2p#z#qe~Jl%NcK#giDFXtJ~TU|u%H7Dqy$bx zILueOvh;!0(t<&-oLQ5}2PD|e>~R*ynKRDfO1W-ECC`Z`Gg^cj;bpY4gju%eRt!Q% z=&X???08uDxTT6kV-S8Wem+4JgPqEd?fV!+f+WEeiQp0JlRb=65@HhujhCs5~6^2l8FsMY^^coRiP>Unv;_fI@mv$LJh%T03LQ#nlelV8HxI{OKslq5hiTZD#_*08gP!VWWIu z`C-B%o-gJAo{P_wBj1HF%9y^E);Kdbq%;;EIe`bKvBy~)8fTovrEy7X;N`?_zoYT< z!7q=-;~@vDEzaMXzw|&nU6z_X^mXcIm1;igdE29g0`+IOvia_r3*3Co zXISXQJS~inq=WA`JdTBYCt90C71ai50S8zHkS%Uwl{K}u(JKS6%0)F5snIsWF*NjA zbT}A<2gQSq9?DA0bB(CA(ZSmJ_LB0~{iA?mUTbFCK7rPndpbn-5PeAzggK76% z=B+ef5mO{W2@#DPotB*P-A2GUc#aiP|5WTYFT+Rny+Q3P3P$Q(n!b|r(NPtEBWaN$ zq|U#+=lTAK>`K5%coJDGKc@~2?eB@K0vwCSlK5cp`<=04JCykiF4;XvrpO?=u5lICEhnQjg>eZ}Yw=+XGl-r*cAAx3)nt z;MHNko-|JeO02`cyN8)?j@JX`%D6sguO3+TeYv3U>mLAT*k(8&C+=$Zy~p2f{1LD< z(^`mfl9DAcdGR)(2{1t;$Ww@Vf+_uzH#daqypVi5Fo>n1rEcg3=XCt?TPrj7fhSu_ zTLRqx)e7WmhJ1!6Qm>X`9bGcZL!e%QFCoh^_*9Eo6j5%#iZ2viNZrXD_*;Kf(qX_V zTt%i)vg)S(@%9_7fce&ZJ9I9VG;TUsY~$MocqzV=Y|c>|+HDUsI-Ui*n6lUo`POWB zwE8MPRlSF@25_NzzOVULtr_zPytQO@GAUAnFnyjx3 zMU$7CnpX(lCPZ@5>)kg>;)ZB~2||&W%i~*NR;a8njAtB*jYE-fC@>Bu7>7dR(C}Z+ fUmq?SU|6b7oy7*zlF6i)({VGZsf8`ggn|7JGh>1t literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/agnosiaimgui.h.C442C0E7D513336B.idx b/.cache/clangd/index/agnosiaimgui.h.C442C0E7D513336B.idx index 2988329a42ee6779b8b50b0f19986e1bfc72c05b..56552e50fa5ec9ea70a55ecf056da7436beb3d34 100644 GIT binary patch literal 378 zcmWIYbaN|WWMFVk@vO*AElFfyU|ZGJ*>pyxcvgf0)ABuQ=gnSF*_>_a z@Y?vp%v8gVs{=c$IfE<`YBcxX^$2)V`?iFcd%N33hT_WHqXbkzyXv4f@!|Wp_gZ@ju&EL21+Y&DG9T&^0C6DFV|e;s*bsMhMfuM zAvR_*`*C7J|m(Rv=##<^* zt!?_kzUQ=B@5@7K6I&j<&yMC_sxCOKappta#mA0yIh~sN@|>Z}Ttj#Jbq_;clqyGG zZ9Xc^P~0bXNXdtDs|AmAnQ!CeOA%}M&8~i4|6o#$_#a2Mlhaa;Z;3i^(sQ=Wot67* z`0hr2-{rhyc@=Z8cw9O|ab<2&JkT}r>seD)vb^;cVqyh36bRTk_&CHEI3S$)-+5Ek ztz22e#KZ&j4$R z>|x{tn-9|nqZzrt8e!HIrRF4pY$z^DF4hMzpw0m?p;m*K?DuU;jf-zc^DuL;aWb%h HgdhL_duwsz diff --git a/.cache/clangd/index/buffers.cpp.FC2CC275D910BCB7.idx b/.cache/clangd/index/buffers.cpp.FC2CC275D910BCB7.idx index c6d053187c83d139fb4eb401cdb5d78171896309..73d3a19b5c25bc679d80d9128d089cf979b2ed9d 100644 GIT binary patch literal 15906 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 literal 9970 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 Jjcq_7`9Hhl$)*4R diff --git a/.cache/clangd/index/buffers.h.CC15E40B6084C10D.idx b/.cache/clangd/index/buffers.h.CC15E40B6084C10D.idx index 98a1a8c80e9b4c6e5396c37044db2c50642999ed..90ecd48d303aaba1cdaa79b6265482fabb27dd7e 100644 GIT binary patch literal 2930 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 yH*ZPl26Z6+%RW(erVmaDF2`F-;u6>JtUg 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 B`=|f_ diff --git a/.cache/clangd/index/entrypoint.h.1BBE0276DA7F0CE2.idx b/.cache/clangd/index/entrypoint.h.1BBE0276DA7F0CE2.idx index d43304632b59a1182e09aa094ea61cb99e62bff5..7e093dde6bc3e8adcdd2d2678dbe041f4b9ca63f 100644 GIT binary patch literal 1044 zcmWIYbaUfjVPJ4h@vO*AElC728H9njxTGj^E|C5P#1(ULdp7bNGT?Ff>spbp`pAWa zu2W}3ShXMX)LrWLL}k^3*9$!s@ZG&P_nvG?(3hLbSnM;T3S?F|9?9do>6+} zn)jI;tB*Qgax9oiy)jjuq3Dy?8t`xqa^JT@kiT5-=ck5JW{Y|v? z&~sC}Rgto>kX1}C=g{#R3t7({);sEw<#mNSL-a)Z_xx#*k(U^XD|3^kGBPlvnX`QS zvB!)>h>4vE=wKjVW)o(UW8i>rR30cB7;(O~6kuY5NprJv%fO^}OE^~(OiV*NBV;d0VYnEG&?)HAWXNgw7BBO zR}ZrUn7Com9PAt-FzN4~`cH2U}j-s;R6#4oZv8L zWM*Y3N=+;7K#CnkPH?QiG{Ry9C=7}Xn6Qm{%)+B`H8zYq%siYt!Z26BB8ia;962!6 zu*d-#0*VxvFf3Al!l1~23Bw`-C=7}Om@q67fWn}_hY7<1A7~{gUQLQ5iI1(#w^<{|~1Ln{_W zh`(B~(%_!Src1LfTtC%$RsXS|zE@TtpH*AslXZU-i`63csb?>{a``5w+pWoSr_7ya zciZZA!J;xbwoUA!b;0}-|LmJ>7A3P)IUzNDit7CY{+L;3ry5CCZPLE6CZNpQewyX3 z{^H8qq%xpe)67{u{@7#2BE-ba1av+SFmnoX$}w<2I4Td64U9NnTM967!KC@v`D9?y zyK*C2IW>A6_?S4E7?{P_#U#N5O#03LLzg+jcf|-W3Bq*qvh%`qui>v-wIlsOh5!=} zOqz?GOAw}8SXx~1N9I5t#J%PjWm<4W<=}F)_lV1(^i7VA9{z_JmjK zidXV6@dBMD&MqzlCScO0(|=D|>F3-oz{C%f7GW3R2NN*qnE%!OL2vH$f(>UDW)kKC z6ENuoF<3za1hB&x j3=E*`3k7U21_LaXK?2NIp5DB>&+E_{Ru)z^b`Ayr4nYqm diff --git a/.cache/clangd/index/graphicspipeline.cpp.F94E3ACB17FA6762.idx b/.cache/clangd/index/graphicspipeline.cpp.F94E3ACB17FA6762.idx index ac3ede35e72e58db8503dc090d0333f988bc7a36..f912b136cdf50749fb152fe0820c4dd833ef443e 100644 GIT binary patch literal 11786 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 literal 10508 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 J#zKyi{|8h|ya)gQ diff --git a/.cache/clangd/index/graphicspipeline.h.093A3C67F46BDBAE.idx b/.cache/clangd/index/graphicspipeline.h.093A3C67F46BDBAE.idx index f0baa8055afb1a4652597b3b8aeb62d13e9a19b9..95d67bdddb26c530326445578555851e7da0806f 100644 GIT binary patch literal 808 zcmWIYbaPW+W?*nm@vO*AElFfyU|TtJe{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@@M3b_BSmGCfs|VJ7S2U8V zoqs9r$faK2O;N73R}SlE=`VEsdGTa;>zzaM%Wn4Ud~AQ(r(}Lw?e^y@4%|DD!+9oa zvp@H>+f~8q7kY12D+%4W{^2?9L{1U!O5f~pK05RO3cqUzcMn~H>(SYgs!LR=a!>6YcKnn$Z<77H

U0dB!!NkN5F^++Uor_(XffK^Jw0VWvte(FWLQG6B-2y@aaJzks z-q>iWCH4t1vBRW!g?Qo8@yrYP+KNRc2r;q2r1^#TRbY1ih+Np(t)Xy-orzI^fro>G zLjX)b?Pla*?b>|QJ>}YqyFyGXF#SA2JaGNb*xz_m?%()Gh=~~{%_qdC3^TqcHLdtB z&>OJuW8?&f8_ZT%xB-Ph;RO?hg%?m56izT=UK>+|0h6Mmn802x7FwEmXVUUMm z!ljRubOYR?w=?qa2&xI1!W;&ZX5<3<9~LDr-vbQ+`5LAQ=4+rZ$hRU10w@N#hj`A2eS?v@U(q3vSxKv zxtqvS^ddqsF;{q7*@obP+^eD%Wg8|RyjSOYyy`BS6Z_v^Ulbi!T!f!CD}Os!kh+=Y z!^V3yZ(pywx8?P_-2HWNdAoBjZ`D7$?&jX@tHTf6I{9$-Lu-uJLTAR z%;0;J`R{>7-}}ygMGb!??AsOU&iMa-xbBgB+PCiOc}70Jmep8@i*B!+DXyZ}J28~s zm?hlmfOuVgy?=3KZjwCErv~CzBp$yjisWGsWMJTDWntw26F?aVC`wH$b_a$=nmNnI zAA8JLn0WZvc-X|@+W0y6I3!`bbIR2nO?JWjj6D2YN?d9%VZnOqo^!JttU0;zUamgC~x>Y;UA7lVkvq`b3z*P0_f44r$`N{&IFfS*s zD9jL@j%EAr|9QL$WF;pTFHH4<@`apVzA<|Kj~SzMG{tPElZfCw;P?MzV$RgQjeLg;cwGM4Pt0cI zc6xQ-z=8B7ot)EzmKQ~-{F94XvhF}qWO@32W*3JZHGVT4)1vuvnD?Yf+D%@vB$9zS zfoV;{v9;&KBo;^b7aKbv^5L51=q7H>WtvMwl)} zE)EO77#SY_`OYAYncZ0hlmDQEE;i$dScG$;BWqGx9YB oecrV0rPyRfUIt#6^BEYDb5nBofLtyf9wt^!Hf9!f0S-oP0CtDQegFUf diff --git a/.cache/clangd/index/model.cpp.56D1ED026EF1D5F1.idx b/.cache/clangd/index/model.cpp.56D1ED026EF1D5F1.idx index eec1bf35796e1a5d66961f22ee46ceda019e5b01..5bc649ef1c71fc64df2fbac8db8a6f940dd5e53b 100644 GIT binary patch literal 2466 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 AuK)l5 diff --git a/.cache/clangd/index/model.h.C3ECCCBE7C04E4C8.idx b/.cache/clangd/index/model.h.C3ECCCBE7C04E4C8.idx index 87ebf7cafe163223a815293366d4ff3e9753f455..d234b7023f7394ac5dc4b8864488a5f8b54a667c 100644 GIT binary patch literal 348 zcmWIYbaM+~WMFVk@vO*AElFfyU|{>pxA?8=-^s|XaZFQD(bP7(e>7i8F8j<$%EC?}4(Q%9LO1Oh7lYu&}Xkg9!#suq-1BYf)-iaSV_H zGl!8AtRJRN=&8*PhyAa2F!Hdl$+9WJRJm;S>Nx)A(sM>G7A6iRWw2pLwgH7fR>6de hQgaeP?Bb&2VsQ|IW#iWe2CJvPImyV%zzPz8006;mWkvu1 literal 436 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 OaNf`sch+L$U;qH#JIclY literal 762 zcmWIYbaVT}#K7R3;#rZKT9U}Zz`!63#Kk2=nd^XbC?f+y#hj%9hJ1$%1WtFKxW_Bt z>f`Z+@x`oJPXdZM^&D61U#=zX)A2Uv^rxH8m}V|G<~qUV)Q2UT_>UybX_9J-yi>X7 zu&MDvv$Mtgg%T51w5(Z?7F6}ki}Q7<*KzjQ((WbKZcPx*c_K0MNLXpvYt`^&%@ty6 zdipDu?BKMoEa6NKU%PI9evIb-8_gdxYR_CLThi8gd+Ox)tUX_|BeR@tvA@}+|H$^K z(jErO=9+WGmAOgEKo@I89%$;9|iJ6aqV>Ki1YDQU3c0P7F1`eQ~fZ#x!;d@ny zGlD`)EInCKyATsAP=Nujffy$nADcK#`s979qw!yM zMG7%-0i`D~@l9fq11Ad`3lErJ-~?OG$jMq2aR1hl z`8VpAn0UZK3=EviT+9+s1q=Z(9-EdWuU{_2#0|9I3M20oMsZFqJ}wEE1&0E(h09WY zofBeW11hNGtrX+r=HrGtaMmNidZ(L`Z-khbfYK)!c~1gEfRm3C9s&$SscFUMfmXx9 zijfl>LNJHJLI@}f3Ky6#EL?!XpzweR!@>h732>6B^Qf>7@V#L(tEa4 O^cpa-Gq8gMAOHYxL+2|1 diff --git a/.cache/clangd/index/texture.cpp.14763AFB742F8112.idx b/.cache/clangd/index/texture.cpp.14763AFB742F8112.idx index d6278c9abc8c3fbb0412b30b2451aeae93eceda0..7fe761ef8667b0cce3c64de188a261c1229e7a84 100644 GIT binary patch literal 12892 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 diff --git a/.cache/clangd/index/texture.h.712506A996DB5236.idx b/.cache/clangd/index/texture.h.712506A996DB5236.idx index b951bb4e489284d58103c3f78adeba583e20560b..7c0ee1cebe29903d08529c1ddbc0178a9dd3ca1e 100644 GIT binary patch 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& literal 962 zcmWIYbaUIq%)sEB;#rZKT9U}Zz`!63#Kk2=nYVy6GZO? zsk!|fH{0!0?cIFG>mGB6Sf41{{_l6IUh~p7mm2lnH_J7uY(Ad8lIyX^wg+aOm&Bih zTvgQnz!xle?%^ZbKC=h>Uyl8VcpPr+$7CwEGs9}#AJgA2-$Y&zx;VjY!Um4p9+9$d zoqT4#t>uv^RFY#a6L@#!)kaCZz~&|APPO*k%1oHPa&;~L+5cNkf8V$N&8(>wRx9P6 ztlMT@eDT??n`Y8QJ1hE9^Lqb%nb^K$v98(zo6lcbm{n${KKfnLS(J z{W>HknANtg5n^J2NptdXD!`<_PYTadZ2xtHiAezBR|Xz7E;cc!@eH@wv`(M>sQ6BZ zi4UfmPlylhI2~!%Wl0_xe}$MhVA8yNyvi`c8H!TVibasZmyr`3t}s8r!WAeC3Qw3Y zEIfh2pm2l7!+tQVXieF&f3iS_YCL~AxScBqyR7<04Zw$XaE2J diff --git a/.cache/clangd/index/types.h.2A872A9A515562E6.idx b/.cache/clangd/index/types.h.2A872A9A515562E6.idx new file mode 100644 index 0000000000000000000000000000000000000000..f4ca67de1db49ebbc6877a874b1a43431286e167 GIT binary patch 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/vk_mem_alloc.h.F667345C57A24872.idx b/.cache/clangd/index/vk_mem_alloc.h.F667345C57A24872.idx new file mode 100644 index 0000000000000000000000000000000000000000..7a8933f47a327d9521bb3dbed476e9b51e0d6f1d GIT binary patch literal 54346 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

D>1fVbJchhUjUly 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]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 FV1i8quX_Lh literal 0 HcmV?d00001 diff --git a/imgui.ini b/imgui.ini index 7e47892..42e0e92 100644 --- a/imgui.ini +++ b/imgui.ini @@ -3,6 +3,6 @@ Pos=60,60 Size=400,400 [Window][Agnosia Debug] -Pos=194,438 -Size=583,225 +Pos=522,0 +Size=583,202 diff --git a/lib/vk_mem_alloc.h b/lib/vk_mem_alloc.h new file mode 100644 index 0000000..2307325 --- /dev/null +++ b/lib/vk_mem_alloc.h @@ -0,0 +1,18676 @@ +// +// Copyright (c) 2017-2024 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H +#define AMD_VULKAN_MEMORY_ALLOCATOR_H + +/** \mainpage Vulkan Memory Allocator + +Version 3.1.0 + +Copyright (c) 2017-2024 Advanced Micro Devices, Inc. All rights reserved. \n +License: MIT \n +See also: [product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/), +[repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) + + +API documentation divided into groups: [Topics](topics.html) + +General documentation chapters: + +- User guide + - \subpage quick_start + - [Project setup](@ref quick_start_project_setup) + - [Initialization](@ref quick_start_initialization) + - [Resource allocation](@ref quick_start_resource_allocation) + - \subpage choosing_memory_type + - [Usage](@ref choosing_memory_type_usage) + - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags) + - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types) + - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools) + - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations) + - \subpage memory_mapping + - [Copy functions](@ref memory_mapping_copy_functions) + - [Mapping functions](@ref memory_mapping_mapping_functions) + - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory) + - [Cache flush and invalidate](@ref memory_mapping_cache_control) + - \subpage staying_within_budget + - [Querying for budget](@ref staying_within_budget_querying_for_budget) + - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage) + - \subpage resource_aliasing + - \subpage custom_memory_pools + - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex) + - [When not to use custom pools](@ref custom_memory_pools_when_not_use) + - [Linear allocation algorithm](@ref linear_algorithm) + - [Free-at-once](@ref linear_algorithm_free_at_once) + - [Stack](@ref linear_algorithm_stack) + - [Double stack](@ref linear_algorithm_double_stack) + - [Ring buffer](@ref linear_algorithm_ring_buffer) + - \subpage defragmentation + - \subpage statistics + - [Numeric statistics](@ref statistics_numeric_statistics) + - [JSON dump](@ref statistics_json_dump) + - \subpage allocation_annotation + - [Allocation user data](@ref allocation_user_data) + - [Allocation names](@ref allocation_names) + - \subpage virtual_allocator + - \subpage debugging_memory_usage + - [Memory initialization](@ref debugging_memory_usage_initialization) + - [Margins](@ref debugging_memory_usage_margins) + - [Corruption detection](@ref debugging_memory_usage_corruption_detection) + - [Leak detection features](@ref debugging_memory_usage_leak_detection) + - \subpage other_api_interop +- \subpage usage_patterns + - [GPU-only resource](@ref usage_patterns_gpu_only) + - [Staging copy for upload](@ref usage_patterns_staging_copy_upload) + - [Readback](@ref usage_patterns_readback) + - [Advanced data uploading](@ref usage_patterns_advanced_data_uploading) + - [Other use cases](@ref usage_patterns_other_use_cases) +- \subpage configuration + - [Pointers to Vulkan functions](@ref config_Vulkan_functions) + - [Custom host memory allocator](@ref custom_memory_allocator) + - [Device memory allocation callbacks](@ref allocation_callbacks) + - [Device heap memory limit](@ref heap_memory_limit) +- Extension support + - \subpage vk_khr_dedicated_allocation + - \subpage enabling_buffer_device_address + - \subpage vk_ext_memory_priority + - \subpage vk_amd_device_coherent_memory +- \subpage general_considerations + - [Thread safety](@ref general_considerations_thread_safety) + - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility) + - [Validation layer warnings](@ref general_considerations_validation_layer_warnings) + - [Allocation algorithm](@ref general_considerations_allocation_algorithm) + - [Features not supported](@ref general_considerations_features_not_supported) + +\defgroup group_init Library initialization + +\brief API elements related to the initialization and management of the entire library, especially #VmaAllocator object. + +\defgroup group_alloc Memory allocation + +\brief API elements related to the allocation, deallocation, and management of Vulkan memory, buffers, images. +Most basic ones being: vmaCreateBuffer(), vmaCreateImage(). + +\defgroup group_virtual Virtual allocator + +\brief API elements related to the mechanism of \ref virtual_allocator - using the core allocation algorithm +for user-defined purpose without allocating any real GPU memory. + +\defgroup group_stats Statistics + +\brief API elements that query current status of the allocator, from memory usage, budget, to full dump of the internal state in JSON format. +See documentation chapter: \ref statistics. +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if !defined(VMA_VULKAN_VERSION) + #if defined(VK_VERSION_1_3) + #define VMA_VULKAN_VERSION 1003000 + #elif defined(VK_VERSION_1_2) + #define VMA_VULKAN_VERSION 1002000 + #elif defined(VK_VERSION_1_1) + #define VMA_VULKAN_VERSION 1001000 + #else + #define VMA_VULKAN_VERSION 1000000 + #endif +#endif + +#if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS + extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; + extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; + extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; + extern PFN_vkAllocateMemory vkAllocateMemory; + extern PFN_vkFreeMemory vkFreeMemory; + extern PFN_vkMapMemory vkMapMemory; + extern PFN_vkUnmapMemory vkUnmapMemory; + extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges; + extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges; + extern PFN_vkBindBufferMemory vkBindBufferMemory; + extern PFN_vkBindImageMemory vkBindImageMemory; + extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; + extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; + extern PFN_vkCreateBuffer vkCreateBuffer; + extern PFN_vkDestroyBuffer vkDestroyBuffer; + extern PFN_vkCreateImage vkCreateImage; + extern PFN_vkDestroyImage vkDestroyImage; + extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer; + #if VMA_VULKAN_VERSION >= 1001000 + extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2; + extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2; + extern PFN_vkBindBufferMemory2 vkBindBufferMemory2; + extern PFN_vkBindImageMemory2 vkBindImageMemory2; + extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2; + #endif // #if VMA_VULKAN_VERSION >= 1001000 +#endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES + +#if !defined(VMA_DEDICATED_ALLOCATION) + #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation + #define VMA_DEDICATED_ALLOCATION 1 + #else + #define VMA_DEDICATED_ALLOCATION 0 + #endif +#endif + +#if !defined(VMA_BIND_MEMORY2) + #if VK_KHR_bind_memory2 + #define VMA_BIND_MEMORY2 1 + #else + #define VMA_BIND_MEMORY2 0 + #endif +#endif + +#if !defined(VMA_MEMORY_BUDGET) + #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000) + #define VMA_MEMORY_BUDGET 1 + #else + #define VMA_MEMORY_BUDGET 0 + #endif +#endif + +// Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers. +#if !defined(VMA_BUFFER_DEVICE_ADDRESS) + #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000 + #define VMA_BUFFER_DEVICE_ADDRESS 1 + #else + #define VMA_BUFFER_DEVICE_ADDRESS 0 + #endif +#endif + +// Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers. +#if !defined(VMA_MEMORY_PRIORITY) + #if VK_EXT_memory_priority + #define VMA_MEMORY_PRIORITY 1 + #else + #define VMA_MEMORY_PRIORITY 0 + #endif +#endif + +// Defined to 1 when VK_KHR_maintenance4 device extension is defined in Vulkan headers. +#if !defined(VMA_KHR_MAINTENANCE4) + #if VK_KHR_maintenance4 + #define VMA_KHR_MAINTENANCE4 1 + #else + #define VMA_KHR_MAINTENANCE4 0 + #endif +#endif + +// Defined to 1 when VK_KHR_maintenance5 device extension is defined in Vulkan headers. +#if !defined(VMA_KHR_MAINTENANCE5) + #if VK_KHR_maintenance5 + #define VMA_KHR_MAINTENANCE5 1 + #else + #define VMA_KHR_MAINTENANCE5 0 + #endif +#endif + + +// Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers. +#if !defined(VMA_EXTERNAL_MEMORY) + #if VK_KHR_external_memory + #define VMA_EXTERNAL_MEMORY 1 + #else + #define VMA_EXTERNAL_MEMORY 0 + #endif +#endif + +// Define these macros to decorate all public functions with additional code, +// before and after returned type, appropriately. This may be useful for +// exporting the functions when compiling VMA as a separate library. Example: +// #define VMA_CALL_PRE __declspec(dllexport) +// #define VMA_CALL_POST __cdecl +#ifndef VMA_CALL_PRE + #define VMA_CALL_PRE +#endif +#ifndef VMA_CALL_POST + #define VMA_CALL_POST +#endif + +// Define this macro to decorate pNext pointers with an attribute specifying the Vulkan +// structure that will be extended via the pNext chain. +#ifndef VMA_EXTENDS_VK_STRUCT + #define VMA_EXTENDS_VK_STRUCT(vkStruct) +#endif + +// Define this macro to decorate pointers with an attribute specifying the +// length of the array they point to if they are not null. +// +// The length may be one of +// - The name of another parameter in the argument list where the pointer is declared +// - The name of another member in the struct where the pointer is declared +// - The name of a member of a struct type, meaning the value of that member in +// the context of the call. For example +// VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"), +// this means the number of memory heaps available in the device associated +// with the VmaAllocator being dealt with. +#ifndef VMA_LEN_IF_NOT_NULL + #define VMA_LEN_IF_NOT_NULL(len) +#endif + +// The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang. +// see: https://clang.llvm.org/docs/AttributeReference.html#nullable +#ifndef VMA_NULLABLE + #ifdef __clang__ + #define VMA_NULLABLE _Nullable + #else + #define VMA_NULLABLE + #endif +#endif + +// The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang. +// see: https://clang.llvm.org/docs/AttributeReference.html#nonnull +#ifndef VMA_NOT_NULL + #ifdef __clang__ + #define VMA_NOT_NULL _Nonnull + #else + #define VMA_NOT_NULL + #endif +#endif + +// If non-dispatchable handles are represented as pointers then we can give +// then nullability annotations +#ifndef VMA_NOT_NULL_NON_DISPATCHABLE + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL + #else + #define VMA_NOT_NULL_NON_DISPATCHABLE + #endif +#endif + +#ifndef VMA_NULLABLE_NON_DISPATCHABLE + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE + #else + #define VMA_NULLABLE_NON_DISPATCHABLE + #endif +#endif + +#ifndef VMA_STATS_STRING_ENABLED + #define VMA_STATS_STRING_ENABLED 1 +#endif + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// INTERFACE +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// Sections for managing code placement in file, only for development purposes e.g. for convenient folding inside an IDE. +#ifndef _VMA_ENUM_DECLARATIONS + +/** +\addtogroup group_init +@{ +*/ + +/// Flags for created #VmaAllocator. +typedef enum VmaAllocatorCreateFlagBits +{ + /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you. + + Using this flag may increase performance because internal mutexes are not used. + */ + VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001, + /** \brief Enables usage of VK_KHR_dedicated_allocation extension. + + The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`. + When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1. + + Using this extension will automatically allocate dedicated blocks of memory for + some buffers and images instead of suballocating place for them out of bigger + memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT + flag) when it is recommended by the driver. It may improve performance on some + GPUs. + + You may set this flag only if you found out that following device extensions are + supported, you enabled them while creating Vulkan device passed as + VmaAllocatorCreateInfo::device, and you want them to be used internally by this + library: + + - VK_KHR_get_memory_requirements2 (device extension) + - VK_KHR_dedicated_allocation (device extension) + + When this flag is set, you can experience following warnings reported by Vulkan + validation layer. You can ignore them. + + > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer. + */ + VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002, + /** + Enables usage of VK_KHR_bind_memory2 extension. + + The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`. + When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1. + + You may set this flag only if you found out that this device extension is supported, + you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + and you want it to be used internally by this library. + + The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`, + which allow to pass a chain of `pNext` structures while binding. + This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2(). + */ + VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004, + /** + Enables usage of VK_EXT_memory_budget extension. + + You may set this flag only if you found out that this device extension is supported, + you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + and you want it to be used internally by this library, along with another instance extension + VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted). + + The extension provides query for current memory usage and budget, which will probably + be more accurate than an estimation used by the library otherwise. + */ + VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008, + /** + Enables usage of VK_AMD_device_coherent_memory extension. + + You may set this flag only if you: + + - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device, + - want it to be used internally by this library. + + The extension and accompanying device feature provide access to memory types with + `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags. + They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR. + + When the extension is not enabled, such memory types are still enumerated, but their usage is illegal. + To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type, + returning `VK_ERROR_FEATURE_NOT_PRESENT`. + */ + VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010, + /** + Enables usage of "buffer device address" feature, which allows you to use function + `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader. + + You may set this flag only if you: + + 1. (For Vulkan version < 1.2) Found as available and enabled device extension + VK_KHR_buffer_device_address. + This extension is promoted to core Vulkan 1.2. + 2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`. + + When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA. + The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to + allocated memory blocks wherever it might be needed. + + For more information, see documentation chapter \ref enabling_buffer_device_address. + */ + VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020, + /** + Enables usage of VK_EXT_memory_priority extension in the library. + + You may set this flag only if you found available and enabled this device extension, + along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`, + while creating Vulkan device passed as VmaAllocatorCreateInfo::device. + + When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority + are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored. + + A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. + Larger values are higher priority. The granularity of the priorities is implementation-dependent. + It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`. + The value to be used for default priority is 0.5. + For more details, see the documentation of the VK_EXT_memory_priority extension. + */ + VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040, + /** + Enables usage of VK_KHR_maintenance4 extension in the library. + + You may set this flag only if you found available and enabled this device extension, + while creating Vulkan device passed as VmaAllocatorCreateInfo::device. + */ + VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT = 0x00000080, + /** + Enables usage of VK_KHR_maintenance5 extension in the library. + + You should set this flag if you found available and enabled this device extension, + while creating Vulkan device passed as VmaAllocatorCreateInfo::device. + */ + VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT = 0x00000100, + + VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocatorCreateFlagBits; +/// See #VmaAllocatorCreateFlagBits. +typedef VkFlags VmaAllocatorCreateFlags; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/// \brief Intended usage of the allocated memory. +typedef enum VmaMemoryUsage +{ + /** No intended memory usage specified. + Use other members of VmaAllocationCreateInfo to specify your requirements. + */ + VMA_MEMORY_USAGE_UNKNOWN = 0, + /** + \deprecated Obsolete, preserved for backward compatibility. + Prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + */ + VMA_MEMORY_USAGE_GPU_ONLY = 1, + /** + \deprecated Obsolete, preserved for backward compatibility. + Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`. + */ + VMA_MEMORY_USAGE_CPU_ONLY = 2, + /** + \deprecated Obsolete, preserved for backward compatibility. + Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + */ + VMA_MEMORY_USAGE_CPU_TO_GPU = 3, + /** + \deprecated Obsolete, preserved for backward compatibility. + Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`. + */ + VMA_MEMORY_USAGE_GPU_TO_CPU = 4, + /** + \deprecated Obsolete, preserved for backward compatibility. + Prefers not `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + */ + VMA_MEMORY_USAGE_CPU_COPY = 5, + /** + Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`. + Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation. + + Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`. + + Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + */ + VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6, + /** + Selects best memory type automatically. + This flag is recommended for most common use cases. + + When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), + you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT + in VmaAllocationCreateInfo::flags. + + It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. + vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() + and not with generic memory allocation functions. + */ + VMA_MEMORY_USAGE_AUTO = 7, + /** + Selects best memory type automatically with preference for GPU (device) memory. + + When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), + you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT + in VmaAllocationCreateInfo::flags. + + It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. + vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() + and not with generic memory allocation functions. + */ + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE = 8, + /** + Selects best memory type automatically with preference for CPU (host) memory. + + When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), + you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT + in VmaAllocationCreateInfo::flags. + + It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. + vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() + and not with generic memory allocation functions. + */ + VMA_MEMORY_USAGE_AUTO_PREFER_HOST = 9, + + VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF +} VmaMemoryUsage; + +/// Flags to be passed as VmaAllocationCreateInfo::flags. +typedef enum VmaAllocationCreateFlagBits +{ + /** \brief Set this flag if the allocation should have its own memory block. + + Use it for special, big resources, like fullscreen images used as attachments. + + If you use this flag while creating a buffer or an image, `VkMemoryDedicatedAllocateInfo` + structure is applied if possible. + */ + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001, + + /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block. + + If new allocation cannot be placed in any of the existing blocks, allocation + fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + + You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and + #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense. + */ + VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002, + /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. + + Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData. + + It is valid to use this flag for allocation made from memory type that is not + `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is + useful if you need an allocation that is efficient to use on GPU + (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that + support it (e.g. Intel GPU). + */ + VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004, + /** \deprecated Preserved for backward compatibility. Consider using vmaSetAllocationName() instead. + + Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a + null-terminated string. Instead of copying pointer value, a local copy of the + string is made and stored in allocation's `pName`. The string is automatically + freed together with the allocation. It is also used in vmaBuildStatsString(). + */ + VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020, + /** Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag. + */ + VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040, + /** Create both buffer/image and allocation, but don't bind them together. + It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions. + The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage(). + Otherwise it is ignored. + + If you want to make sure the new buffer/image is not tied to the new memory allocation + through `VkMemoryDedicatedAllocateInfoKHR` structure in case the allocation ends up in its own memory block, + use also flag #VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT. + */ + VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080, + /** Create allocation only if additional device memory required for it, if any, won't exceed + memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + */ + VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100, + /** \brief Set this flag if the allocated memory will have aliasing resources. + + Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified. + Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors. + */ + VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT = 0x00000200, + /** + Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT). + + - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value, + you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect. + - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`. + This includes allocations created in \ref custom_memory_pools. + + Declares that mapped memory will only be written sequentially, e.g. using `memcpy()` or a loop writing number-by-number, + never read or accessed randomly, so a memory type can be selected that is uncached and write-combined. + + \warning Violating this declaration may work correctly, but will likely be very slow. + Watch out for implicit reads introduced by doing e.g. `pMappedData[i] += x;` + Better prepare your data in a local variable and `memcpy()` it to the mapped pointer all at once. + */ + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT = 0x00000400, + /** + Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT). + + - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value, + you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect. + - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`. + This includes allocations created in \ref custom_memory_pools. + + Declares that mapped memory can be read, written, and accessed in random order, + so a `HOST_CACHED` memory type is preferred. + */ + VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT = 0x00000800, + /** + Together with #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, + it says that despite request for host access, a not-`HOST_VISIBLE` memory type can be selected + if it may improve performance. + + By using this flag, you declare that you will check if the allocation ended up in a `HOST_VISIBLE` memory type + (e.g. using vmaGetAllocationMemoryProperties()) and if not, you will create some "staging" buffer and + issue an explicit transfer to write/read your data. + To prepare for this possibility, don't forget to add appropriate flags like + `VK_BUFFER_USAGE_TRANSFER_DST_BIT`, `VK_BUFFER_USAGE_TRANSFER_SRC_BIT` to the parameters of created buffer or image. + */ + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT = 0x00001000, + /** Allocation strategy that chooses smallest possible free range for the allocation + to minimize memory usage and fragmentation, possibly at the expense of allocation time. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = 0x00010000, + /** Allocation strategy that chooses first suitable free range for the allocation - + not necessarily in terms of the smallest offset but the one that is easiest and fastest to find + to minimize allocation time, possibly at the expense of allocation quality. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = 0x00020000, + /** Allocation strategy that chooses always the lowest offset in available space. + This is not the most efficient strategy but achieves highly packed data. + Used internally by defragmentation, not recommended in typical usage. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = 0x00040000, + /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT. + */ + VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT, + /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT. + */ + VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT, + /** A bit mask to extract only `STRATEGY` bits from entire set of flags. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MASK = + VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + + VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocationCreateFlagBits; +/// See #VmaAllocationCreateFlagBits. +typedef VkFlags VmaAllocationCreateFlags; + +/// Flags to be passed as VmaPoolCreateInfo::flags. +typedef enum VmaPoolCreateFlagBits +{ + /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored. + + This is an optional optimization flag. + + If you always allocate using vmaCreateBuffer(), vmaCreateImage(), + vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator + knows exact type of your allocations so it can handle Buffer-Image Granularity + in the optimal way. + + If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(), + exact type of such allocations is not known, so allocator must be conservative + in handling Buffer-Image Granularity, which can lead to suboptimal allocation + (wasted memory). In that case, if you can make sure you always allocate only + buffers and linear images or only optimal images out of this pool, use this flag + to make allocator disregard Buffer-Image Granularity and so make allocations + faster and more optimal. + */ + VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002, + + /** \brief Enables alternative, linear allocation algorithm in this pool. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + */ + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004, + + /** Bit mask to extract only `ALGORITHM` bits from entire set of flags. + */ + VMA_POOL_CREATE_ALGORITHM_MASK = + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT, + + VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaPoolCreateFlagBits; +/// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits. +typedef VkFlags VmaPoolCreateFlags; + +/// Flags to be passed as VmaDefragmentationInfo::flags. +typedef enum VmaDefragmentationFlagBits +{ + /* \brief Use simple but fast algorithm for defragmentation. + May not achieve best results but will require least time to compute and least allocations to copy. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT = 0x1, + /* \brief Default defragmentation algorithm, applied also when no `ALGORITHM` flag is specified. + Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT = 0x2, + /* \brief Perform full defragmentation of memory. + Can result in notably more time to compute and allocations to copy, but will achieve best memory packing. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT = 0x4, + /** \brief Use the most roboust algorithm at the cost of time to compute and number of copies to make. + Only available when bufferImageGranularity is greater than 1, since it aims to reduce + alignment issues between different types of resources. + Otherwise falls back to same behavior as #VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT = 0x8, + + /// A bit mask to extract only `ALGORITHM` bits from entire set of flags. + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK = + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT | + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT | + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT | + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT, + + VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaDefragmentationFlagBits; +/// See #VmaDefragmentationFlagBits. +typedef VkFlags VmaDefragmentationFlags; + +/// Operation performed on single defragmentation move. See structure #VmaDefragmentationMove. +typedef enum VmaDefragmentationMoveOperation +{ + /// Buffer/image has been recreated at `dstTmpAllocation`, data has been copied, old buffer/image has been destroyed. `srcAllocation` should be changed to point to the new place. This is the default value set by vmaBeginDefragmentationPass(). + VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY = 0, + /// Set this value if you cannot move the allocation. New place reserved at `dstTmpAllocation` will be freed. `srcAllocation` will remain unchanged. + VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE = 1, + /// Set this value if you decide to abandon the allocation and you destroyed the buffer/image. New place reserved at `dstTmpAllocation` will be freed, along with `srcAllocation`, which will be destroyed. + VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY = 2, +} VmaDefragmentationMoveOperation; + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. +typedef enum VmaVirtualBlockCreateFlagBits +{ + /** \brief Enables alternative, linear allocation algorithm in this virtual block. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + */ + VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001, + + /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags. + */ + VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK = + VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT, + + VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaVirtualBlockCreateFlagBits; +/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits. +typedef VkFlags VmaVirtualBlockCreateFlags; + +/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. +typedef enum VmaVirtualAllocationCreateFlagBits +{ + /** \brief Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT, + /** \brief Allocation strategy that tries to minimize memory usage. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT, + /** \brief Allocation strategy that tries to minimize allocation time. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT, + /** Allocation strategy that chooses always the lowest offset in available space. + This is not the most efficient strategy but achieves highly packed data. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags. + + These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK, + + VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaVirtualAllocationCreateFlagBits; +/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits. +typedef VkFlags VmaVirtualAllocationCreateFlags; + +/** @} */ + +#endif // _VMA_ENUM_DECLARATIONS + +#ifndef _VMA_DATA_TYPES_DECLARATIONS + +/** +\addtogroup group_init +@{ */ + +/** \struct VmaAllocator +\brief Represents main object of this library initialized. + +Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it. +Call function vmaDestroyAllocator() to destroy it. + +It is recommended to create just one object of this type per `VkDevice` object, +right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed. +*/ +VK_DEFINE_HANDLE(VmaAllocator) + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \struct VmaPool +\brief Represents custom memory pool + +Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it. +Call function vmaDestroyPool() to destroy it. + +For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools). +*/ +VK_DEFINE_HANDLE(VmaPool) + +/** \struct VmaAllocation +\brief Represents single memory allocation. + +It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type +plus unique offset. + +There are multiple ways to create such object. +You need to fill structure VmaAllocationCreateInfo. +For more information see [Choosing memory type](@ref choosing_memory_type). + +Although the library provides convenience functions that create Vulkan buffer or image, +allocate memory for it and bind them together, +binding of the allocation to a buffer or an image is out of scope of the allocation itself. +Allocation object can exist without buffer/image bound, +binding can be done manually by the user, and destruction of it can be done +independently of destruction of the allocation. + +The object also remembers its size and some other information. +To retrieve this information, use function vmaGetAllocationInfo() and inspect +returned structure VmaAllocationInfo. +*/ +VK_DEFINE_HANDLE(VmaAllocation) + +/** \struct VmaDefragmentationContext +\brief An opaque object that represents started defragmentation process. + +Fill structure #VmaDefragmentationInfo and call function vmaBeginDefragmentation() to create it. +Call function vmaEndDefragmentation() to destroy it. +*/ +VK_DEFINE_HANDLE(VmaDefragmentationContext) + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \struct VmaVirtualAllocation +\brief Represents single memory allocation done inside VmaVirtualBlock. + +Use it as a unique identifier to virtual allocation within the single block. + +Use value `VK_NULL_HANDLE` to represent a null/invalid allocation. +*/ +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaVirtualAllocation) + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \struct VmaVirtualBlock +\brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory. + +Fill in #VmaVirtualBlockCreateInfo structure and use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it. +For more information, see documentation chapter \ref virtual_allocator. + +This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally. +*/ +VK_DEFINE_HANDLE(VmaVirtualBlock) + +/** @} */ + +/** +\addtogroup group_init +@{ +*/ + +/// Callback function called after successful vkAllocateMemory. +typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryType, + VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory, + VkDeviceSize size, + void* VMA_NULLABLE pUserData); + +/// Callback function called before vkFreeMemory. +typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryType, + VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory, + VkDeviceSize size, + void* VMA_NULLABLE pUserData); + +/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`. + +Provided for informative purpose, e.g. to gather statistics about number of +allocations or total amount of memory allocated in Vulkan. + +Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. +*/ +typedef struct VmaDeviceMemoryCallbacks +{ + /// Optional, can be null. + PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate; + /// Optional, can be null. + PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree; + /// Optional, can be null. + void* VMA_NULLABLE pUserData; +} VmaDeviceMemoryCallbacks; + +/** \brief Pointers to some Vulkan functions - a subset used by the library. + +Used in VmaAllocatorCreateInfo::pVulkanFunctions. +*/ +typedef struct VmaVulkanFunctions +{ + /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS. + PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr; + /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS. + PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr; + PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties; + PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties; + PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory; + PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory; + PFN_vkMapMemory VMA_NULLABLE vkMapMemory; + PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory; + PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges; + PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges; + PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory; + PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory; + PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements; + PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements; + PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer; + PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer; + PFN_vkCreateImage VMA_NULLABLE vkCreateImage; + PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage; + PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer; +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension. + PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR; + /// Fetch "vkGetImageMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension. + PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR; +#endif +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension. + PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR; + /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension. + PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR; +#endif +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + /// Fetch from "vkGetPhysicalDeviceMemoryProperties2" on Vulkan >= 1.1, but you can also fetch it from "vkGetPhysicalDeviceMemoryProperties2KHR" if you enabled extension VK_KHR_get_physical_device_properties2. + PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR; +#endif +#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000 + /// Fetch from "vkGetDeviceBufferMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceBufferMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4. + PFN_vkGetDeviceBufferMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceBufferMemoryRequirements; + /// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4. + PFN_vkGetDeviceImageMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceImageMemoryRequirements; +#endif +} VmaVulkanFunctions; + +/// Description of a Allocator to be created. +typedef struct VmaAllocatorCreateInfo +{ + /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum. + VmaAllocatorCreateFlags flags; + /// Vulkan physical device. + /** It must be valid throughout whole lifetime of created allocator. */ + VkPhysicalDevice VMA_NOT_NULL physicalDevice; + /// Vulkan device. + /** It must be valid throughout whole lifetime of created allocator. */ + VkDevice VMA_NOT_NULL device; + /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional. + /** Set to 0 to use default, which is currently 256 MiB. */ + VkDeviceSize preferredLargeHeapBlockSize; + /// Custom CPU memory allocation callbacks. Optional. + /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */ + const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks; + /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional. + /** Optional, can be null. */ + const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks; + /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap. + + If not NULL, it must be a pointer to an array of + `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on + maximum number of bytes that can be allocated out of particular Vulkan memory + heap. + + Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that + heap. This is also the default in case of `pHeapSizeLimit` = NULL. + + If there is a limit defined for a heap: + + - If user tries to allocate more memory from that heap using this allocator, + the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the + value of this limit will be reported instead when using vmaGetMemoryProperties(). + + Warning! Using this feature may not be equivalent to installing a GPU with + smaller amount of memory, because graphics driver doesn't necessary fail new + allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is + exceeded. It may return success and just silently migrate some device memory + blocks to system RAM. This driver behavior can also be controlled using + VK_AMD_memory_overallocation_behavior extension. + */ + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit; + + /** \brief Pointers to Vulkan functions. Can be null. + + For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions). + */ + const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions; + /** \brief Handle to Vulkan instance object. + + Starting from version 3.0.0 this member is no longer optional, it must be set! + */ + VkInstance VMA_NOT_NULL instance; + /** \brief Optional. Vulkan version that the application uses. + + It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`. + The patch version number specified is ignored. Only the major and minor versions are considered. + Only versions 1.0, 1.1, 1.2, 1.3 are supported by the current implementation. + Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`. + It must match the Vulkan version used by the application and supported on the selected physical device, + so it must be no higher than `VkApplicationInfo::apiVersion` passed to `vkCreateInstance` + and no higher than `VkPhysicalDeviceProperties::apiVersion` found on the physical device used. + */ + uint32_t vulkanApiVersion; +#if VMA_EXTERNAL_MEMORY + /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type. + + If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount` + elements, defining external memory handle types of particular Vulkan memory type, + to be passed using `VkExportMemoryAllocateInfoKHR`. + + Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type. + This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL. + */ + const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes; +#endif // #if VMA_EXTERNAL_MEMORY +} VmaAllocatorCreateInfo; + +/// Information about existing #VmaAllocator object. +typedef struct VmaAllocatorInfo +{ + /** \brief Handle to Vulkan instance object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::instance. + */ + VkInstance VMA_NOT_NULL instance; + /** \brief Handle to Vulkan physical device object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice. + */ + VkPhysicalDevice VMA_NOT_NULL physicalDevice; + /** \brief Handle to Vulkan device object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::device. + */ + VkDevice VMA_NOT_NULL device; +} VmaAllocatorInfo; + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Calculated statistics of memory usage e.g. in a specific memory type, heap, custom pool, or total. + +These are fast to calculate. +See functions: vmaGetHeapBudgets(), vmaGetPoolStatistics(). +*/ +typedef struct VmaStatistics +{ + /** \brief Number of `VkDeviceMemory` objects - Vulkan memory blocks allocated. + */ + uint32_t blockCount; + /** \brief Number of #VmaAllocation objects allocated. + + Dedicated allocations have their own blocks, so each one adds 1 to `allocationCount` as well as `blockCount`. + */ + uint32_t allocationCount; + /** \brief Number of bytes allocated in `VkDeviceMemory` blocks. + + \note To avoid confusion, please be aware that what Vulkan calls an "allocation" - a whole `VkDeviceMemory` object + (e.g. as in `VkPhysicalDeviceLimits::maxMemoryAllocationCount`) is called a "block" in VMA, while VMA calls + "allocation" a #VmaAllocation object that represents a memory region sub-allocated from such block, usually for a single buffer or image. + */ + VkDeviceSize blockBytes; + /** \brief Total number of bytes occupied by all #VmaAllocation objects. + + Always less or equal than `blockBytes`. + Difference `(blockBytes - allocationBytes)` is the amount of memory allocated from Vulkan + but unused by any #VmaAllocation. + */ + VkDeviceSize allocationBytes; +} VmaStatistics; + +/** \brief More detailed statistics than #VmaStatistics. + +These are slower to calculate. Use for debugging purposes. +See functions: vmaCalculateStatistics(), vmaCalculatePoolStatistics(). + +Previous version of the statistics API provided averages, but they have been removed +because they can be easily calculated as: + +\code +VkDeviceSize allocationSizeAvg = detailedStats.statistics.allocationBytes / detailedStats.statistics.allocationCount; +VkDeviceSize unusedBytes = detailedStats.statistics.blockBytes - detailedStats.statistics.allocationBytes; +VkDeviceSize unusedRangeSizeAvg = unusedBytes / detailedStats.unusedRangeCount; +\endcode +*/ +typedef struct VmaDetailedStatistics +{ + /// Basic statistics. + VmaStatistics statistics; + /// Number of free ranges of memory between allocations. + uint32_t unusedRangeCount; + /// Smallest allocation size. `VK_WHOLE_SIZE` if there are 0 allocations. + VkDeviceSize allocationSizeMin; + /// Largest allocation size. 0 if there are 0 allocations. + VkDeviceSize allocationSizeMax; + /// Smallest empty range size. `VK_WHOLE_SIZE` if there are 0 empty ranges. + VkDeviceSize unusedRangeSizeMin; + /// Largest empty range size. 0 if there are 0 empty ranges. + VkDeviceSize unusedRangeSizeMax; +} VmaDetailedStatistics; + +/** \brief General statistics from current state of the Allocator - +total memory usage across all memory heaps and types. + +These are slower to calculate. Use for debugging purposes. +See function vmaCalculateStatistics(). +*/ +typedef struct VmaTotalStatistics +{ + VmaDetailedStatistics memoryType[VK_MAX_MEMORY_TYPES]; + VmaDetailedStatistics memoryHeap[VK_MAX_MEMORY_HEAPS]; + VmaDetailedStatistics total; +} VmaTotalStatistics; + +/** \brief Statistics of current memory usage and available budget for a specific memory heap. + +These are fast to calculate. +See function vmaGetHeapBudgets(). +*/ +typedef struct VmaBudget +{ + /** \brief Statistics fetched from the library. + */ + VmaStatistics statistics; + /** \brief Estimated current memory usage of the program, in bytes. + + Fetched from system using VK_EXT_memory_budget extension if enabled. + + It might be different than `statistics.blockBytes` (usually higher) due to additional implicit objects + also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or + `VkDeviceMemory` blocks allocated outside of this library, if any. + */ + VkDeviceSize usage; + /** \brief Estimated amount of memory available to the program, in bytes. + + Fetched from system using VK_EXT_memory_budget extension if enabled. + + It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors + external to the program, decided by the operating system. + Difference `budget - usage` is the amount of additional memory that can probably + be allocated without problems. Exceeding the budget may result in various problems. + */ + VkDeviceSize budget; +} VmaBudget; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \brief Parameters of new #VmaAllocation. + +To be used with functions like vmaCreateBuffer(), vmaCreateImage(), and many others. +*/ +typedef struct VmaAllocationCreateInfo +{ + /// Use #VmaAllocationCreateFlagBits enum. + VmaAllocationCreateFlags flags; + /** \brief Intended usage of memory. + + You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n + If `pool` is not null, this member is ignored. + */ + VmaMemoryUsage usage; + /** \brief Flags that must be set in a Memory Type chosen for an allocation. + + Leave 0 if you specify memory requirements in other way. \n + If `pool` is not null, this member is ignored.*/ + VkMemoryPropertyFlags requiredFlags; + /** \brief Flags that preferably should be set in a memory type chosen for an allocation. + + Set to 0 if no additional flags are preferred. \n + If `pool` is not null, this member is ignored. */ + VkMemoryPropertyFlags preferredFlags; + /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation. + + Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if + it meets other requirements specified by this structure, with no further + restrictions on memory type index. \n + If `pool` is not null, this member is ignored. + */ + uint32_t memoryTypeBits; + /** \brief Pool that this allocation should be created in. + + Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members: + `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored. + */ + VmaPool VMA_NULLABLE pool; + /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). + + If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either + null or pointer to a null-terminated string. The string will be then copied to + internal buffer, so it doesn't need to be valid after allocation call. + */ + void* VMA_NULLABLE pUserData; + /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. + + It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object + and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + Otherwise, it has the priority of a memory block where it is placed and this variable is ignored. + */ + float priority; +} VmaAllocationCreateInfo; + +/// Describes parameter of created #VmaPool. +typedef struct VmaPoolCreateInfo +{ + /** \brief Vulkan memory type index to allocate this pool from. + */ + uint32_t memoryTypeIndex; + /** \brief Use combination of #VmaPoolCreateFlagBits. + */ + VmaPoolCreateFlags flags; + /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional. + + Specify nonzero to set explicit, constant size of memory blocks used by this + pool. + + Leave 0 to use default and let the library manage block sizes automatically. + Sizes of particular blocks may vary. + In this case, the pool will also support dedicated allocations. + */ + VkDeviceSize blockSize; + /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty. + + Set to 0 to have no preallocated blocks and allow the pool be completely empty. + */ + size_t minBlockCount; + /** \brief Maximum number of blocks that can be allocated in this pool. Optional. + + Set to 0 to use default, which is `SIZE_MAX`, which means no limit. + + Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated + throughout whole lifetime of this pool. + */ + size_t maxBlockCount; + /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations. + + It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object. + Otherwise, this variable is ignored. + */ + float priority; + /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0. + + Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two. + It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough, + e.g. when doing interop with OpenGL. + */ + VkDeviceSize minAllocationAlignment; + /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional. + + Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`. + It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`. + Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool. + + Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`, + can be attached automatically by this library when using other, more convenient of its features. + */ + void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkMemoryAllocateInfo) pMemoryAllocateNext; +} VmaPoolCreateInfo; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** +Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). + +There is also an extended version of this structure that carries additional parameters: #VmaAllocationInfo2. +*/ +typedef struct VmaAllocationInfo +{ + /** \brief Memory type index that this allocation was allocated from. + + It never changes. + */ + uint32_t memoryType; + /** \brief Handle to Vulkan memory object. + + Same memory object can be shared by multiple allocations. + + It can change after the allocation is moved during \ref defragmentation. + */ + VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory; + /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation. + + You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function + vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image, + not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation + and apply this offset automatically. + + It can change after the allocation is moved during \ref defragmentation. + */ + VkDeviceSize offset; + /** \brief Size of this allocation, in bytes. + + It never changes. + + \note Allocation size returned in this variable may be greater than the size + requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the + allocation is accessible for operations on memory e.g. using a pointer after + mapping with vmaMapMemory(), but operations on the resource e.g. using + `vkCmdCopyBuffer` must be limited to the size of the resource. + */ + VkDeviceSize size; + /** \brief Pointer to the beginning of this allocation as mapped data. + + If the allocation hasn't been mapped using vmaMapMemory() and hasn't been + created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null. + + It can change after call to vmaMapMemory(), vmaUnmapMemory(). + It can also change after the allocation is moved during \ref defragmentation. + */ + void* VMA_NULLABLE pMappedData; + /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData(). + + It can change after call to vmaSetAllocationUserData() for this allocation. + */ + void* VMA_NULLABLE pUserData; + /** \brief Custom allocation name that was set with vmaSetAllocationName(). + + It can change after call to vmaSetAllocationName() for this allocation. + + Another way to set custom name is to pass it in VmaAllocationCreateInfo::pUserData with + additional flag #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT set [DEPRECATED]. + */ + const char* VMA_NULLABLE pName; +} VmaAllocationInfo; + +/// Extended parameters of a #VmaAllocation object that can be retrieved using function vmaGetAllocationInfo2(). +typedef struct VmaAllocationInfo2 +{ + /** \brief Basic parameters of the allocation. + + If you need only these, you can use function vmaGetAllocationInfo() and structure #VmaAllocationInfo instead. + */ + VmaAllocationInfo allocationInfo; + /** \brief Size of the `VkDeviceMemory` block that the allocation belongs to. + + In case of an allocation with dedicated memory, it will be equal to `allocationInfo.size`. + */ + VkDeviceSize blockSize; + /** \brief `VK_TRUE` if the allocation has dedicated memory, `VK_FALSE` if it was placed as part of a larger memory block. + + When `VK_TRUE`, it also means `VkMemoryDedicatedAllocateInfo` was used when creating the allocation + (if VK_KHR_dedicated_allocation extension or Vulkan version >= 1.1 is enabled). + */ + VkBool32 dedicatedMemory; +} VmaAllocationInfo2; + +/** Callback function called during vmaBeginDefragmentation() to check custom criterion about ending current defragmentation pass. + +Should return true if the defragmentation needs to stop current pass. +*/ +typedef VkBool32 (VKAPI_PTR* PFN_vmaCheckDefragmentationBreakFunction)(void* VMA_NULLABLE pUserData); + +/** \brief Parameters for defragmentation. + +To be used with function vmaBeginDefragmentation(). +*/ +typedef struct VmaDefragmentationInfo +{ + /// \brief Use combination of #VmaDefragmentationFlagBits. + VmaDefragmentationFlags flags; + /** \brief Custom pool to be defragmented. + + If null then default pools will undergo defragmentation process. + */ + VmaPool VMA_NULLABLE pool; + /** \brief Maximum numbers of bytes that can be copied during single pass, while moving allocations to different places. + + `0` means no limit. + */ + VkDeviceSize maxBytesPerPass; + /** \brief Maximum number of allocations that can be moved during single pass to a different place. + + `0` means no limit. + */ + uint32_t maxAllocationsPerPass; + /** \brief Optional custom callback for stopping vmaBeginDefragmentation(). + + Have to return true for breaking current defragmentation pass. + */ + PFN_vmaCheckDefragmentationBreakFunction VMA_NULLABLE pfnBreakCallback; + /// \brief Optional data to pass to custom callback for stopping pass of defragmentation. + void* VMA_NULLABLE pBreakCallbackUserData; +} VmaDefragmentationInfo; + +/// Single move of an allocation to be done for defragmentation. +typedef struct VmaDefragmentationMove +{ + /// Operation to be performed on the allocation by vmaEndDefragmentationPass(). Default value is #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY. You can modify it. + VmaDefragmentationMoveOperation operation; + /// Allocation that should be moved. + VmaAllocation VMA_NOT_NULL srcAllocation; + /** \brief Temporary allocation pointing to destination memory that will replace `srcAllocation`. + + \warning Do not store this allocation in your data structures! It exists only temporarily, for the duration of the defragmentation pass, + to be used for binding new buffer/image to the destination memory using e.g. vmaBindBufferMemory(). + vmaEndDefragmentationPass() will destroy it and make `srcAllocation` point to this memory. + */ + VmaAllocation VMA_NOT_NULL dstTmpAllocation; +} VmaDefragmentationMove; + +/** \brief Parameters for incremental defragmentation steps. + +To be used with function vmaBeginDefragmentationPass(). +*/ +typedef struct VmaDefragmentationPassMoveInfo +{ + /// Number of elements in the `pMoves` array. + uint32_t moveCount; + /** \brief Array of moves to be performed by the user in the current defragmentation pass. + + Pointer to an array of `moveCount` elements, owned by VMA, created in vmaBeginDefragmentationPass(), destroyed in vmaEndDefragmentationPass(). + + For each element, you should: + + 1. Create a new buffer/image in the place pointed by VmaDefragmentationMove::dstMemory + VmaDefragmentationMove::dstOffset. + 2. Copy data from the VmaDefragmentationMove::srcAllocation e.g. using `vkCmdCopyBuffer`, `vkCmdCopyImage`. + 3. Make sure these commands finished executing on the GPU. + 4. Destroy the old buffer/image. + + Only then you can finish defragmentation pass by calling vmaEndDefragmentationPass(). + After this call, the allocation will point to the new place in memory. + + Alternatively, if you cannot move specific allocation, you can set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. + + Alternatively, if you decide you want to completely remove the allocation: + + 1. Destroy its buffer/image. + 2. Set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY. + + Then, after vmaEndDefragmentationPass() the allocation will be freed. + */ + VmaDefragmentationMove* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(moveCount) pMoves; +} VmaDefragmentationPassMoveInfo; + +/// Statistics returned for defragmentation process in function vmaEndDefragmentation(). +typedef struct VmaDefragmentationStats +{ + /// Total number of bytes that have been copied while moving allocations to different places. + VkDeviceSize bytesMoved; + /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects. + VkDeviceSize bytesFreed; + /// Number of allocations that have been moved to different places. + uint32_t allocationsMoved; + /// Number of empty `VkDeviceMemory` objects that have been released to the system. + uint32_t deviceMemoryBlocksFreed; +} VmaDefragmentationStats; + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock(). +typedef struct VmaVirtualBlockCreateInfo +{ + /** \brief Total size of the virtual block. + + Sizes can be expressed in bytes or any units you want as long as you are consistent in using them. + For example, if you allocate from some array of structures, 1 can mean single instance of entire structure. + */ + VkDeviceSize size; + + /** \brief Use combination of #VmaVirtualBlockCreateFlagBits. + */ + VmaVirtualBlockCreateFlags flags; + + /** \brief Custom CPU memory allocation callbacks. Optional. + + Optional, can be null. When specified, they will be used for all CPU-side memory allocations. + */ + const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks; +} VmaVirtualBlockCreateInfo; + +/// Parameters of created virtual allocation to be passed to vmaVirtualAllocate(). +typedef struct VmaVirtualAllocationCreateInfo +{ + /** \brief Size of the allocation. + + Cannot be zero. + */ + VkDeviceSize size; + /** \brief Required alignment of the allocation. Optional. + + Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset. + */ + VkDeviceSize alignment; + /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits. + */ + VmaVirtualAllocationCreateFlags flags; + /** \brief Custom pointer to be associated with the allocation. Optional. + + It can be any value and can be used for user-defined purposes. It can be fetched or changed later. + */ + void* VMA_NULLABLE pUserData; +} VmaVirtualAllocationCreateInfo; + +/// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo(). +typedef struct VmaVirtualAllocationInfo +{ + /** \brief Offset of the allocation. + + Offset at which the allocation was made. + */ + VkDeviceSize offset; + /** \brief Size of the allocation. + + Same value as passed in VmaVirtualAllocationCreateInfo::size. + */ + VkDeviceSize size; + /** \brief Custom pointer associated with the allocation. + + Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData(). + */ + void* VMA_NULLABLE pUserData; +} VmaVirtualAllocationInfo; + +/** @} */ + +#endif // _VMA_DATA_TYPES_DECLARATIONS + +#ifndef _VMA_FUNCTION_HEADERS + +/** +\addtogroup group_init +@{ +*/ + +/// Creates #VmaAllocator object. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator( + const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator); + +/// Destroys allocator object. +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator( + VmaAllocator VMA_NULLABLE allocator); + +/** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc. + +It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to +`VkPhysicalDevice`, `VkDevice` etc. every time using this function. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo); + +/** +PhysicalDeviceProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties( + VmaAllocator VMA_NOT_NULL allocator, + const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties); + +/** +PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties); + +/** +\brief Given Memory Type Index, returns Property Flags of this memory type. + +This is just a convenience function. Same information can be obtained using +vmaGetMemoryProperties(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeIndex, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); + +/** \brief Sets index of the current frame. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t frameIndex); + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Retrieves statistics from current state of the Allocator. + +This function is called "calculate" not "get" because it has to traverse all +internal data structures, so it may be quite slow. Use it for debugging purposes. +For faster but more brief statistics suitable to be called every frame or every allocation, +use vmaGetHeapBudgets(). + +Note that when using allocator from multiple threads, returned information may immediately +become outdated. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics( + VmaAllocator VMA_NOT_NULL allocator, + VmaTotalStatistics* VMA_NOT_NULL pStats); + +/** \brief Retrieves information about current memory usage and budget for all memory heaps. + +\param allocator +\param[out] pBudgets Must point to array with number of elements at least equal to number of memory heaps in physical device used. + +This function is called "get" not "calculate" because it is very fast, suitable to be called +every frame or every allocation. For more detailed statistics use vmaCalculateStatistics(). + +Note that when using allocator from multiple threads, returned information may immediately +become outdated. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets( + VmaAllocator VMA_NOT_NULL allocator, + VmaBudget* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pBudgets); + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** +\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo. + +This algorithm tries to find a memory type that: + +- Is allowed by memoryTypeBits. +- Contains all the flags from pAllocationCreateInfo->requiredFlags. +- Matches intended usage. +- Has as many flags from pAllocationCreateInfo->preferredFlags as possible. + +\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result +from this function or any other allocating function probably means that your +device doesn't support any memory type with requested features for the specific +type of resource you want to use it for. Please check parameters of your +resource, like image layout (OPTIMAL versus LINEAR) or mip level count. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); + +/** +\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo. + +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy buffer that never has memory bound. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); + +/** +\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo. + +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy image that never has memory bound. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator VMA_NOT_NULL allocator, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); + +/** \brief Allocates Vulkan device memory and creates #VmaPool object. + +\param allocator Allocator object. +\param pCreateInfo Parameters of pool to create. +\param[out] pPool Handle to created pool. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool( + VmaAllocator VMA_NOT_NULL allocator, + const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool); + +/** \brief Destroys #VmaPool object and frees Vulkan device memory. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NULLABLE pool); + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Retrieves statistics of existing #VmaPool object. + +\param allocator Allocator object. +\param pool Pool object. +\param[out] pPoolStats Statistics of specified pool. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + VmaStatistics* VMA_NOT_NULL pPoolStats); + +/** \brief Retrieves detailed statistics of existing #VmaPool object. + +\param allocator Allocator object. +\param pool Pool object. +\param[out] pPoolStats Statistics of specified pool. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + VmaDetailedStatistics* VMA_NOT_NULL pPoolStats); + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions. + +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). + +Possible return values: + +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool); + +/** \brief Retrieves name of a custom pool. + +After the call `ppName` is either null or points to an internally-owned null-terminated string +containing name of the pool that was previously set. The pointer becomes invalid when the pool is +destroyed or its name is changed using vmaSetPoolName(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + const char* VMA_NULLABLE* VMA_NOT_NULL ppName); + +/** \brief Sets name of a custom pool. + +`pName` can be either null or pointer to a null-terminated string with new name for the pool. +Function makes internal copy of the string, so it can be changed or freed immediately after this call. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + const char* VMA_NULLABLE pName); + +/** \brief General purpose memory allocation. + +\param allocator +\param pVkMemoryRequirements +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). + +It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(), +vmaCreateBuffer(), vmaCreateImage() instead whenever possible. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory( + VmaAllocator VMA_NOT_NULL allocator, + const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief General purpose memory allocation for multiple allocation objects at once. + +\param allocator Allocator object. +\param pVkMemoryRequirements Memory requirements for each allocation. +\param pCreateInfo Creation parameters for each allocation. +\param allocationCount Number of allocations to make. +\param[out] pAllocations Pointer to array that will be filled with handles to created allocations. +\param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations. + +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). + +Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding. +It is just a general purpose allocation function able to make multiple allocations at once. +It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times. + +All allocations are made using same parameters. All of them are created out of the same memory pool and type. +If any allocation fails, all allocations already made within this function call are also freed, so that when +returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages( + VmaAllocator VMA_NOT_NULL allocator, + const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements, + const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo, + size_t allocationCount, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations, + VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo); + +/** \brief Allocates memory suitable for given `VkBuffer`. + +\param allocator +\param buffer +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindBufferMemory(). + +This is a special-purpose function. In most cases you should use vmaCreateBuffer(). + +You must free the allocation using vmaFreeMemory() when no longer needed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Allocates memory suitable for given `VkImage`. + +\param allocator +\param image +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindImageMemory(). + +This is a special-purpose function. In most cases you should use vmaCreateImage(). + +You must free the allocation using vmaFreeMemory() when no longer needed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). + +Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory( + VmaAllocator VMA_NOT_NULL allocator, + const VmaAllocation VMA_NULLABLE allocation); + +/** \brief Frees memory and destroys multiple allocations. + +Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding. +It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(), +vmaAllocateMemoryPages() and other functions. +It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times. + +Allocations in `pAllocations` array can come from any memory pools and types. +Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages( + VmaAllocator VMA_NOT_NULL allocator, + size_t allocationCount, + const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations); + +/** \brief Returns current information about specified allocation. + +Current parameters of given allocation are returned in `pAllocationInfo`. + +Although this function doesn't lock any mutex, so it should be quite efficient, +you should avoid calling it too often. +You can retrieve same VmaAllocationInfo structure while creating your resource, from function +vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change +(e.g. due to defragmentation). + +There is also a new function vmaGetAllocationInfo2() that offers extended information +about the allocation, returned using new structure #VmaAllocationInfo2. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo); + +/** \brief Returns extended information about specified allocation. + +Current parameters of given allocation are returned in `pAllocationInfo`. +Extended parameters in structure #VmaAllocationInfo2 include memory block size +and a flag telling whether the allocation has dedicated memory. +It can be useful e.g. for interop with OpenGL. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VmaAllocationInfo2* VMA_NOT_NULL pAllocationInfo); + +/** \brief Sets pUserData in given allocation to new value. + +The value of pointer `pUserData` is copied to allocation's `pUserData`. +It is opaque, so you can use it however you want - e.g. +as a pointer, ordinal number or some handle to you own data. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + void* VMA_NULLABLE pUserData); + +/** \brief Sets pName in given allocation to new value. + +`pName` must be either null, or pointer to a null-terminated string. The function +makes local copy of the string and sets it as allocation's `pName`. String +passed as pName doesn't need to be valid for whole lifetime of the allocation - +you can free it after this call. String previously pointed by allocation's +`pName` is freed from memory. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const char* VMA_NULLABLE pName); + +/** +\brief Given an allocation, returns Property Flags of its memory type. + +This is just a convenience function. Same information can be obtained using +vmaGetAllocationInfo() + vmaGetMemoryProperties(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); + +/** \brief Maps memory represented by given allocation and returns pointer to it. + +Maps memory represented by given allocation to make it accessible to CPU code. +When succeeded, `*ppData` contains pointer to first byte of this memory. + +\warning +If the allocation is part of a bigger `VkDeviceMemory` block, returned pointer is +correctly offsetted to the beginning of region assigned to this particular allocation. +Unlike the result of `vkMapMemory`, it points to the allocation, not to the beginning of the whole block. +You should not add VmaAllocationInfo::offset to it! + +Mapping is internally reference-counted and synchronized, so despite raw Vulkan +function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory` +multiple times simultaneously, it is safe to call this function on allocations +assigned to the same memory block. Actual Vulkan memory will be mapped on first +mapping and unmapped on last unmapping. + +If the function succeeded, you must call vmaUnmapMemory() to unmap the +allocation when mapping is no longer needed or before freeing the allocation, at +the latest. + +It also safe to call this function multiple times on the same allocation. You +must call vmaUnmapMemory() same number of times as you called vmaMapMemory(). + +It is also safe to call this function on allocation created with +#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time. +You must still call vmaUnmapMemory() same number of times as you called +vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the +"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. + +This function fails when used on allocation made in memory type that is not +`HOST_VISIBLE`. + +This function doesn't automatically flush or invalidate caches. +If the allocation is made from a memory types that is not `HOST_COHERENT`, +you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + void* VMA_NULLABLE* VMA_NOT_NULL ppData); + +/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory(). + +For details, see description of vmaMapMemory(). + +This function doesn't automatically flush or invalidate caches. +If the allocation is made from a memory types that is not `HOST_COHERENT`, +you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation); + +/** \brief Flushes memory of given allocation. + +Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation. +It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`. +Unmap operation doesn't do that automatically. + +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. + +Warning! `offset` and `size` are relative to the contents of given `allocation`. +If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively. +Do not pass allocation's offset as `offset`!!! + +This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize offset, + VkDeviceSize size); + +/** \brief Invalidates memory of given allocation. + +Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation. +It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`. +Map operation doesn't do that automatically. + +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. + +Warning! `offset` and `size` are relative to the contents of given `allocation`. +If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively. +Do not pass allocation's offset as `offset`!!! + +This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if +it is called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize offset, + VkDeviceSize size); + +/** \brief Flushes memory of given set of allocations. + +Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations. +For more information, see documentation of vmaFlushAllocation(). + +\param allocator +\param allocationCount +\param allocations +\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all offsets are zero. +\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations. + +This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t allocationCount, + const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes); + +/** \brief Invalidates memory of given set of allocations. + +Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations. +For more information, see documentation of vmaInvalidateAllocation(). + +\param allocator +\param allocationCount +\param allocations +\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all offsets are zero. +\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations. + +This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t allocationCount, + const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes); + +/** \brief Maps the allocation temporarily if needed, copies data from specified host pointer to it, and flushes the memory from the host caches if needed. + +\param allocator +\param pSrcHostPointer Pointer to the host data that become source of the copy. +\param dstAllocation Handle to the allocation that becomes destination of the copy. +\param dstAllocationLocalOffset Offset within `dstAllocation` where to write copied data, in bytes. +\param size Number of bytes to copy. + +This is a convenience function that allows to copy data from a host pointer to an allocation easily. +Same behavior can be achieved by calling vmaMapMemory(), `memcpy()`, vmaUnmapMemory(), vmaFlushAllocation(). + +This function can be called only for allocations created in a memory type that has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag. +It can be ensured e.g. by using #VMA_MEMORY_USAGE_AUTO and #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or +#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +Otherwise, the function will fail and generate a Validation Layers error. + +`dstAllocationLocalOffset` is relative to the contents of given `dstAllocation`. +If you mean whole allocation, you should pass 0. +Do not pass allocation's offset within device memory block this parameter! +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyMemoryToAllocation( + VmaAllocator VMA_NOT_NULL allocator, + const void* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(size) pSrcHostPointer, + VmaAllocation VMA_NOT_NULL dstAllocation, + VkDeviceSize dstAllocationLocalOffset, + VkDeviceSize size); + +/** \brief Invalidates memory in the host caches if needed, maps the allocation temporarily if needed, and copies data from it to a specified host pointer. + +\param allocator +\param srcAllocation Handle to the allocation that becomes source of the copy. +\param srcAllocationLocalOffset Offset within `srcAllocation` where to read copied data, in bytes. +\param pDstHostPointer Pointer to the host memory that become destination of the copy. +\param size Number of bytes to copy. + +This is a convenience function that allows to copy data from an allocation to a host pointer easily. +Same behavior can be achieved by calling vmaInvalidateAllocation(), vmaMapMemory(), `memcpy()`, vmaUnmapMemory(). + +This function should be called only for allocations created in a memory type that has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` +and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT` flag. +It can be ensured e.g. by using #VMA_MEMORY_USAGE_AUTO and #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +Otherwise, the function may fail and generate a Validation Layers error. +It may also work very slowly when reading from an uncached memory. + +`srcAllocationLocalOffset` is relative to the contents of given `srcAllocation`. +If you mean whole allocation, you should pass 0. +Do not pass allocation's offset within device memory block as this parameter! +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyAllocationToMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL srcAllocation, + VkDeviceSize srcAllocationLocalOffset, + void* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(size) pDstHostPointer, + VkDeviceSize size); + +/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions. + +\param allocator +\param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked. + +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). + +Possible return values: + +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeBits); + +/** \brief Begins defragmentation process. + +\param allocator Allocator object. +\param pInfo Structure filled with parameters of defragmentation. +\param[out] pContext Context object that must be passed to vmaEndDefragmentation() to finish defragmentation. +\returns +- `VK_SUCCESS` if defragmentation can begin. +- `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported. + +For more information about defragmentation, see documentation chapter: +[Defragmentation](@ref defragmentation). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation( + VmaAllocator VMA_NOT_NULL allocator, + const VmaDefragmentationInfo* VMA_NOT_NULL pInfo, + VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext); + +/** \brief Ends defragmentation process. + +\param allocator Allocator object. +\param context Context object that has been created by vmaBeginDefragmentation(). +\param[out] pStats Optional stats for the defragmentation. Can be null. + +Use this function to finish defragmentation started by vmaBeginDefragmentation(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationStats* VMA_NULLABLE pStats); + +/** \brief Starts single defragmentation pass. + +\param allocator Allocator object. +\param context Context object that has been created by vmaBeginDefragmentation(). +\param[out] pPassInfo Computed information for current pass. +\returns +- `VK_SUCCESS` if no more moves are possible. Then you can omit call to vmaEndDefragmentationPass() and simply end whole defragmentation. +- `VK_INCOMPLETE` if there are pending moves returned in `pPassInfo`. You need to perform them, call vmaEndDefragmentationPass(), + and then preferably try another pass with vmaBeginDefragmentationPass(). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo); + +/** \brief Ends single defragmentation pass. + +\param allocator Allocator object. +\param context Context object that has been created by vmaBeginDefragmentation(). +\param pPassInfo Computed information for current pass filled by vmaBeginDefragmentationPass() and possibly modified by you. + +Returns `VK_SUCCESS` if no more moves are possible or `VK_INCOMPLETE` if more defragmentations are possible. + +Ends incremental defragmentation pass and commits all defragmentation moves from `pPassInfo`. +After this call: + +- Allocations at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY + (which is the default) will be pointing to the new destination place. +- Allocation at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY + will be freed. + +If no more moves are possible you can end whole defragmentation. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo); + +/** \brief Binds buffer to allocation. + +Binds specified buffer to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create a buffer, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindBufferMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateBuffer() instead of this one. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer); + +/** \brief Binds buffer to allocation with additional parameters. + +\param allocator +\param allocation +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. +\param buffer +\param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null. + +This function is similar to vmaBindBufferMemory(), but it provides additional parameters. + +If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag +or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer, + const void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkBindBufferMemoryInfoKHR) pNext); + +/** \brief Binds image to allocation. + +Binds specified image to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create an image, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindImageMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateImage() instead of this one. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image); + +/** \brief Binds image to allocation with additional parameters. + +\param allocator +\param allocation +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. +\param image +\param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null. + +This function is similar to vmaBindImageMemory(), but it provides additional parameters. + +If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag +or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image, + const void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkBindImageMemoryInfoKHR) pNext); + +/** \brief Creates a new `VkBuffer`, allocates and binds memory for it. + +\param allocator +\param pBufferCreateInfo +\param pAllocationCreateInfo +\param[out] pBuffer Buffer that was created. +\param[out] pAllocation Allocation that was created. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +This function automatically: + +-# Creates buffer. +-# Allocates appropriate memory for it. +-# Binds the buffer with the memory. + +If any of these operations fail, buffer and allocation are not created, +returned value is negative error code, `*pBuffer` and `*pAllocation` are null. + +If the function succeeded, you must destroy both buffer and allocation when you +no longer need them using either convenience function vmaDestroyBuffer() or +separately, using `vkDestroyBuffer()` and vmaFreeMemory(). + +If #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used, +VK_KHR_dedicated_allocation extension is used internally to query driver whether +it requires or prefers the new buffer to have dedicated allocation. If yes, +and if dedicated allocation is possible +(#VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated +allocation for this buffer, just like when using +#VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + +\note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer, +although recommended as a good practice, is out of scope of this library and could be implemented +by the user as a higher-level logic on top of VMA. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Creates a buffer with additional minimum alignment. + +Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom, +minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g. +for interop with OpenGL. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Creates a new `VkBuffer`, binds already created memory for it. + +\param allocator +\param allocation Allocation that provides memory to be used for binding new buffer to it. +\param pBufferCreateInfo +\param[out] pBuffer Buffer that was created. + +This function automatically: + +-# Creates buffer. +-# Binds the buffer with the supplied memory. + +If any of these operations fail, buffer is not created, +returned value is negative error code and `*pBuffer` is null. + +If the function succeeded, you must destroy the buffer when you +no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding +allocation you can use convenience function vmaDestroyBuffer(). + +\note There is a new version of this function augmented with parameter `allocationLocalOffset` - see vmaCreateAliasingBuffer2(). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer); + +/** \brief Creates a new `VkBuffer`, binds already created memory for it. + +\param allocator +\param allocation Allocation that provides memory to be used for binding new buffer to it. +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the allocation. Normally it should be 0. +\param pBufferCreateInfo +\param[out] pBuffer Buffer that was created. + +This function automatically: + +-# Creates buffer. +-# Binds the buffer with the supplied memory. + +If any of these operations fail, buffer is not created, +returned value is negative error code and `*pBuffer` is null. + +If the function succeeded, you must destroy the buffer when you +no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding +allocation you can use convenience function vmaDestroyBuffer(). + +\note This is a new version of the function augmented with parameter `allocationLocalOffset`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer); + +/** \brief Destroys Vulkan buffer and frees allocated memory. + +This is just a convenience function equivalent to: + +\code +vkDestroyBuffer(device, buffer, allocationCallbacks); +vmaFreeMemory(allocator, allocation); +\endcode + +It is safe to pass null as buffer and/or allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer, + VmaAllocation VMA_NULLABLE allocation); + +/// Function similar to vmaCreateBuffer(). +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage( + VmaAllocator VMA_NOT_NULL allocator, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/// Function similar to vmaCreateAliasingBuffer() but for images. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage); + +/// Function similar to vmaCreateAliasingBuffer2() but for images. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage); + +/** \brief Destroys Vulkan image and frees allocated memory. + +This is just a convenience function equivalent to: + +\code +vkDestroyImage(device, image, allocationCallbacks); +vmaFreeMemory(allocator, allocation); +\endcode + +It is safe to pass null as image and/or allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NULLABLE_NON_DISPATCHABLE image, + VmaAllocation VMA_NULLABLE allocation); + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \brief Creates new #VmaVirtualBlock object. + +\param pCreateInfo Parameters for creation. +\param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( + const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock); + +/** \brief Destroys #VmaVirtualBlock object. + +Please note that you should consciously handle virtual allocations that could remain unfreed in the block. +You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock() +if you are sure this is what you want. If you do neither, an assert is called. + +If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`, +don't forget to free them. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock( + VmaVirtualBlock VMA_NULLABLE virtualBlock); + +/** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations. +*/ +VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty( + VmaVirtualBlock VMA_NOT_NULL virtualBlock); + +/** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo); + +/** \brief Allocates new virtual allocation inside given #VmaVirtualBlock. + +If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned +(despite the function doesn't ever allocate actual GPU memory). +`pAllocation` is then set to `VK_NULL_HANDLE` and `pOffset`, if not null, it set to `UINT64_MAX`. + +\param virtualBlock Virtual block +\param pCreateInfo Parameters for the allocation +\param[out] pAllocation Returned handle of the new allocation +\param[out] pOffset Returned offset of the new allocation. Optional, can be null. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation, + VkDeviceSize* VMA_NULLABLE pOffset); + +/** \brief Frees virtual allocation inside given #VmaVirtualBlock. + +It is correct to call this function with `allocation == VK_NULL_HANDLE` - it does nothing. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation); + +/** \brief Frees all virtual allocations inside given #VmaVirtualBlock. + +You must either call this function or free each virtual allocation individually with vmaVirtualFree() +before destroying a virtual block. Otherwise, an assert is called. + +If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`, +don't forget to free it as well. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock( + VmaVirtualBlock VMA_NOT_NULL virtualBlock); + +/** \brief Changes custom pointer associated with given virtual allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, + void* VMA_NULLABLE pUserData); + +/** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock. + +This function is fast to call. For more detailed statistics, see vmaCalculateVirtualBlockStatistics(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaStatistics* VMA_NOT_NULL pStats); + +/** \brief Calculates and returns detailed statistics about virtual allocations and memory usage in given #VmaVirtualBlock. + +This function is slow to call. Use for debugging purposes. +For less detailed statistics, see vmaGetVirtualBlockStatistics(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaDetailedStatistics* VMA_NOT_NULL pStats); + +/** @} */ + +#if VMA_STATS_STRING_ENABLED +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock. +\param virtualBlock Virtual block. +\param[out] ppStatsString Returned string. +\param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStatistics(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces. + +Returned string must be freed using vmaFreeVirtualBlockStatsString(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString, + VkBool32 detailedMap); + +/// Frees a string returned by vmaBuildVirtualBlockStatsString(). +VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE pStatsString); + +/** \brief Builds and returns statistics as a null-terminated string in JSON format. +\param allocator +\param[out] ppStatsString Must be freed using vmaFreeStatsString() function. +\param detailedMap +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString( + VmaAllocator VMA_NOT_NULL allocator, + char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString, + VkBool32 detailedMap); + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString( + VmaAllocator VMA_NOT_NULL allocator, + char* VMA_NULLABLE pStatsString); + +/** @} */ + +#endif // VMA_STATS_STRING_ENABLED + +#endif // _VMA_FUNCTION_HEADERS + +#ifdef __cplusplus +} +#endif + +#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// For Visual Studio IntelliSense. +#if defined(__cplusplus) && defined(__INTELLISENSE__) +#define VMA_IMPLEMENTATION +#endif + +#ifdef VMA_IMPLEMENTATION +#undef VMA_IMPLEMENTATION + +#include +#include +#include +#include +#include +#include + +#if !defined(VMA_CPP20) + #if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20 + #define VMA_CPP20 1 + #else + #define VMA_CPP20 0 + #endif +#endif + +#ifdef _MSC_VER + #include // For functions like __popcnt, _BitScanForward etc. +#endif +#if VMA_CPP20 + #include +#endif + +#if VMA_STATS_STRING_ENABLED + #include // For snprintf +#endif + +/******************************************************************************* +CONFIGURATION SECTION + +Define some of these macros before each #include of this header or change them +here if you need other then default behavior depending on your environment. +*/ +#ifndef _VMA_CONFIGURATION + +/* +Define this macro to 1 to make the library fetch pointers to Vulkan functions +internally, like: + + vulkanFunctions.vkAllocateMemory = &vkAllocateMemory; +*/ +#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES) + #define VMA_STATIC_VULKAN_FUNCTIONS 1 +#endif + +/* +Define this macro to 1 to make the library fetch pointers to Vulkan functions +internally, like: + + vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(device, "vkAllocateMemory"); + +To use this feature in new versions of VMA you now have to pass +VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as +VmaAllocatorCreateInfo::pVulkanFunctions. Other members can be null. +*/ +#if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS) + #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 +#endif + +#ifndef VMA_USE_STL_SHARED_MUTEX + #if __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17 + #define VMA_USE_STL_SHARED_MUTEX 1 + // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus + // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2. + #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L + #define VMA_USE_STL_SHARED_MUTEX 1 + #else + #define VMA_USE_STL_SHARED_MUTEX 0 + #endif +#endif + +/* +Define this macro to include custom header files without having to edit this file directly, e.g.: + + // Inside of "my_vma_configuration_user_includes.h": + + #include "my_custom_assert.h" // for MY_CUSTOM_ASSERT + #include "my_custom_min.h" // for my_custom_min + #include + #include + + // Inside a different file, which includes "vk_mem_alloc.h": + + #define VMA_CONFIGURATION_USER_INCLUDES_H "my_vma_configuration_user_includes.h" + #define VMA_ASSERT(expr) MY_CUSTOM_ASSERT(expr) + #define VMA_MIN(v1, v2) (my_custom_min(v1, v2)) + #include "vk_mem_alloc.h" + ... + +The following headers are used in this CONFIGURATION section only, so feel free to +remove them if not needed. +*/ +#if !defined(VMA_CONFIGURATION_USER_INCLUDES_H) + #include // for assert + #include // for min, max, swap + #include +#else + #include VMA_CONFIGURATION_USER_INCLUDES_H +#endif + +#ifndef VMA_NULL + // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0. + #define VMA_NULL nullptr +#endif + +#ifndef VMA_FALLTHROUGH + #if __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17 + #define VMA_FALLTHROUGH [[fallthrough]] + #else + #define VMA_FALLTHROUGH + #endif +#endif + +// Normal assert to check for programmer's errors, especially in Debug configuration. +#ifndef VMA_ASSERT + #ifdef NDEBUG + #define VMA_ASSERT(expr) + #else + #define VMA_ASSERT(expr) assert(expr) + #endif +#endif + +// Assert that will be called very often, like inside data structures e.g. operator[]. +// Making it non-empty can make program slow. +#ifndef VMA_HEAVY_ASSERT + #ifdef NDEBUG + #define VMA_HEAVY_ASSERT(expr) + #else + #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr) + #endif +#endif + +// Assert used for reporting memory leaks - unfreed allocations. +#ifndef VMA_ASSERT_LEAK + #define VMA_ASSERT_LEAK(expr) VMA_ASSERT(expr) +#endif + +// If your compiler is not compatible with C++17 and definition of +// aligned_alloc() function is missing, uncommenting following line may help: + +//#include + +#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16) +#include +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + return memalign(alignment, size); +} +#elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC)) +#include + +#if defined(__APPLE__) +#include +#endif + +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + // Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4) + // Therefore, for now disable this specific exception until a proper solution is found. + //#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0)) + //#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0 + // // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only + // // with the MacOSX11.0 SDK in Xcode 12 (which is what adds + // // MAC_OS_X_VERSION_10_16), even though the function is marked + // // available for 10.15. That is why the preprocessor checks for 10.16 but + // // the __builtin_available checks for 10.15. + // // People who use C++17 could call aligned_alloc with the 10.15 SDK already. + // if (__builtin_available(macOS 10.15, iOS 13, *)) + // return aligned_alloc(alignment, size); + //#endif + //#endif + + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + void *pointer; + if(posix_memalign(&pointer, alignment, size) == 0) + return pointer; + return VMA_NULL; +} +#elif defined(_WIN32) +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + return _aligned_malloc(size, alignment); +} +#elif __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17 +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} +#else +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + VMA_ASSERT(0 && "Could not implement aligned_alloc automatically. Please enable C++17 or later in your compiler or provide custom implementation of macro VMA_SYSTEM_ALIGNED_MALLOC (and VMA_SYSTEM_ALIGNED_FREE if needed) using the API of your system."); + return VMA_NULL; +} +#endif + +#if defined(_WIN32) +static void vma_aligned_free(void* ptr) +{ + _aligned_free(ptr); +} +#else +static void vma_aligned_free(void* VMA_NULLABLE ptr) +{ + free(ptr); +} +#endif + +#ifndef VMA_ALIGN_OF + #define VMA_ALIGN_OF(type) (alignof(type)) +#endif + +#ifndef VMA_SYSTEM_ALIGNED_MALLOC + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size)) +#endif + +#ifndef VMA_SYSTEM_ALIGNED_FREE + // VMA_SYSTEM_FREE is the old name, but might have been defined by the user + #if defined(VMA_SYSTEM_FREE) + #define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr) + #else + #define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr) + #endif +#endif + +#ifndef VMA_COUNT_BITS_SET + // Returns number of bits set to 1 in (v) + #define VMA_COUNT_BITS_SET(v) VmaCountBitsSet(v) +#endif + +#ifndef VMA_BITSCAN_LSB + // Scans integer for index of first nonzero value from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX + #define VMA_BITSCAN_LSB(mask) VmaBitScanLSB(mask) +#endif + +#ifndef VMA_BITSCAN_MSB + // Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX + #define VMA_BITSCAN_MSB(mask) VmaBitScanMSB(mask) +#endif + +#ifndef VMA_MIN + #define VMA_MIN(v1, v2) ((std::min)((v1), (v2))) +#endif + +#ifndef VMA_MAX + #define VMA_MAX(v1, v2) ((std::max)((v1), (v2))) +#endif + +#ifndef VMA_SORT + #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp) +#endif + +#ifndef VMA_DEBUG_LOG_FORMAT + #define VMA_DEBUG_LOG_FORMAT(format, ...) + /* + #define VMA_DEBUG_LOG_FORMAT(format, ...) do { \ + printf((format), __VA_ARGS__); \ + printf("\n"); \ + } while(false) + */ +#endif + +#ifndef VMA_DEBUG_LOG + #define VMA_DEBUG_LOG(str) VMA_DEBUG_LOG_FORMAT("%s", (str)) +#endif + +#ifndef VMA_LEAK_LOG_FORMAT + #define VMA_LEAK_LOG_FORMAT(format, ...) VMA_DEBUG_LOG_FORMAT(format, __VA_ARGS__) +#endif + +#ifndef VMA_CLASS_NO_COPY + #define VMA_CLASS_NO_COPY(className) \ + private: \ + className(const className&) = delete; \ + className& operator=(const className&) = delete; +#endif +#ifndef VMA_CLASS_NO_COPY_NO_MOVE + #define VMA_CLASS_NO_COPY_NO_MOVE(className) \ + private: \ + className(const className&) = delete; \ + className(className&&) = delete; \ + className& operator=(const className&) = delete; \ + className& operator=(className&&) = delete; +#endif + +// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString. +#if VMA_STATS_STRING_ENABLED + static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num) + { + snprintf(outStr, strLen, "%" PRIu32, num); + } + static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num) + { + snprintf(outStr, strLen, "%" PRIu64, num); + } + static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr) + { + snprintf(outStr, strLen, "%p", ptr); + } +#endif + +#ifndef VMA_MUTEX + class VmaMutex + { + VMA_CLASS_NO_COPY_NO_MOVE(VmaMutex) + public: + VmaMutex() { } + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + bool TryLock() { return m_Mutex.try_lock(); } + private: + std::mutex m_Mutex; + }; + #define VMA_MUTEX VmaMutex +#endif + +// Read-write mutex, where "read" is shared access, "write" is exclusive access. +#ifndef VMA_RW_MUTEX + #if VMA_USE_STL_SHARED_MUTEX + // Use std::shared_mutex from C++17. + #include + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.lock_shared(); } + void UnlockRead() { m_Mutex.unlock_shared(); } + bool TryLockRead() { return m_Mutex.try_lock_shared(); } + void LockWrite() { m_Mutex.lock(); } + void UnlockWrite() { m_Mutex.unlock(); } + bool TryLockWrite() { return m_Mutex.try_lock(); } + private: + std::shared_mutex m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600 + // Use SRWLOCK from WinAPI. + // Minimum supported client = Windows Vista, server = Windows Server 2008. + class VmaRWMutex + { + public: + VmaRWMutex() { InitializeSRWLock(&m_Lock); } + void LockRead() { AcquireSRWLockShared(&m_Lock); } + void UnlockRead() { ReleaseSRWLockShared(&m_Lock); } + bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; } + void LockWrite() { AcquireSRWLockExclusive(&m_Lock); } + void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); } + bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; } + private: + SRWLOCK m_Lock; + }; + #define VMA_RW_MUTEX VmaRWMutex + #else + // Less efficient fallback: Use normal mutex. + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.Lock(); } + void UnlockRead() { m_Mutex.Unlock(); } + bool TryLockRead() { return m_Mutex.TryLock(); } + void LockWrite() { m_Mutex.Lock(); } + void UnlockWrite() { m_Mutex.Unlock(); } + bool TryLockWrite() { return m_Mutex.TryLock(); } + private: + VMA_MUTEX m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #endif // #if VMA_USE_STL_SHARED_MUTEX +#endif // #ifndef VMA_RW_MUTEX + +/* +If providing your own implementation, you need to implement a subset of std::atomic. +*/ +#ifndef VMA_ATOMIC_UINT32 + #include + #define VMA_ATOMIC_UINT32 std::atomic +#endif + +#ifndef VMA_ATOMIC_UINT64 + #include + #define VMA_ATOMIC_UINT64 std::atomic +#endif + +#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY + /** + Every allocation will have its own memory block. + Define to 1 for debugging purposes only. + */ + #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0) +#endif + +#ifndef VMA_MIN_ALIGNMENT + /** + Minimum alignment of all allocations, in bytes. + Set to more than 1 for debugging purposes. Must be power of two. + */ + #ifdef VMA_DEBUG_ALIGNMENT // Old name + #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT + #else + #define VMA_MIN_ALIGNMENT (1) + #endif +#endif + +#ifndef VMA_DEBUG_MARGIN + /** + Minimum margin after every allocation, in bytes. + Set nonzero for debugging purposes only. + */ + #define VMA_DEBUG_MARGIN (0) +#endif + +#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS + /** + Define this macro to 1 to automatically fill new allocations and destroyed + allocations with some bit pattern. + */ + #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0) +#endif + +#ifndef VMA_DEBUG_DETECT_CORRUPTION + /** + Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to + enable writing magic value to the margin after every allocation and + validating it, so that memory corruptions (out-of-bounds writes) are detected. + */ + #define VMA_DEBUG_DETECT_CORRUPTION (0) +#endif + +#ifndef VMA_DEBUG_GLOBAL_MUTEX + /** + Set this to 1 for debugging purposes only, to enable single mutex protecting all + entry calls to the library. Can be useful for debugging multithreading issues. + */ + #define VMA_DEBUG_GLOBAL_MUTEX (0) +#endif + +#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY + /** + Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity. + Set to more than 1 for debugging purposes only. Must be power of two. + */ + #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1) +#endif + +#ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + /* + Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount + and return error instead of leaving up to Vulkan implementation what to do in such cases. + */ + #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0) +#endif + +#ifndef VMA_SMALL_HEAP_MAX_SIZE + /// Maximum size of a memory heap in Vulkan to consider it "small". + #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024) +#endif + +#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE + /// Default size of a block allocated as single VkDeviceMemory from a "large" heap. + #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024) +#endif + +/* +Mapping hysteresis is a logic that launches when vmaMapMemory/vmaUnmapMemory is called +or a persistently mapped allocation is created and destroyed several times in a row. +It keeps additional +1 mapping of a device memory block to prevent calling actual +vkMapMemory/vkUnmapMemory too many times, which may improve performance and help +tools like RenderDoc. +*/ +#ifndef VMA_MAPPING_HYSTERESIS_ENABLED + #define VMA_MAPPING_HYSTERESIS_ENABLED 1 +#endif + +#define VMA_VALIDATE(cond) do { if(!(cond)) { \ + VMA_ASSERT(0 && "Validation failed: " #cond); \ + return false; \ + } } while(false) + +/******************************************************************************* +END OF CONFIGURATION +*/ +#endif // _VMA_CONFIGURATION + + +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC; +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF; +// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F. +static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666; + +// Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants. +static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040; +static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080; +static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000; +static const uint32_t VK_IMAGE_CREATE_DISJOINT_BIT_COPY = 0x00000200; +static const int32_t VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY = 1000158000; +static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u; +static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32; +static const uint32_t VMA_VENDOR_ID_AMD = 4098; + +// This one is tricky. Vulkan specification defines this code as available since +// Vulkan 1.0, but doesn't actually define it in Vulkan SDK earlier than 1.2.131. +// See pull request #207. +#define VK_ERROR_UNKNOWN_COPY ((VkResult)-13) + + +#if VMA_STATS_STRING_ENABLED +// Correspond to values of enum VmaSuballocationType. +static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = +{ + "FREE", + "UNKNOWN", + "BUFFER", + "IMAGE_UNKNOWN", + "IMAGE_LINEAR", + "IMAGE_OPTIMAL", +}; +#endif + +static VkAllocationCallbacks VmaEmptyAllocationCallbacks = + { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL }; + + +#ifndef _VMA_ENUM_DECLARATIONS + +enum VmaSuballocationType +{ + VMA_SUBALLOCATION_TYPE_FREE = 0, + VMA_SUBALLOCATION_TYPE_UNKNOWN = 1, + VMA_SUBALLOCATION_TYPE_BUFFER = 2, + VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3, + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4, + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5, + VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF +}; + +enum VMA_CACHE_OPERATION +{ + VMA_CACHE_FLUSH, + VMA_CACHE_INVALIDATE +}; + +enum class VmaAllocationRequestType +{ + Normal, + TLSF, + // Used by "Linear" algorithm. + UpperAddress, + EndOf1st, + EndOf2nd, +}; + +#endif // _VMA_ENUM_DECLARATIONS + +#ifndef _VMA_FORWARD_DECLARATIONS +// Opaque handle used by allocation algorithms to identify single allocation in any conforming way. +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle); + +struct VmaMutexLock; +struct VmaMutexLockRead; +struct VmaMutexLockWrite; + +template +struct AtomicTransactionalIncrement; + +template +struct VmaStlAllocator; + +template +class VmaVector; + +template +class VmaSmallVector; + +template +class VmaPoolAllocator; + +template +struct VmaListItem; + +template +class VmaRawList; + +template +class VmaList; + +template +class VmaIntrusiveLinkedList; + +#if VMA_STATS_STRING_ENABLED +class VmaStringBuilder; +class VmaJsonWriter; +#endif + +class VmaDeviceMemoryBlock; + +struct VmaDedicatedAllocationListItemTraits; +class VmaDedicatedAllocationList; + +struct VmaSuballocation; +struct VmaSuballocationOffsetLess; +struct VmaSuballocationOffsetGreater; +struct VmaSuballocationItemSizeLess; + +typedef VmaList> VmaSuballocationList; + +struct VmaAllocationRequest; + +class VmaBlockMetadata; +class VmaBlockMetadata_Linear; +class VmaBlockMetadata_TLSF; + +class VmaBlockVector; + +struct VmaPoolListItemTraits; + +struct VmaCurrentBudgetData; + +class VmaAllocationObjectAllocator; + +#endif // _VMA_FORWARD_DECLARATIONS + + +#ifndef _VMA_FUNCTIONS + +/* +Returns number of bits set to 1 in (v). + +On specific platforms and compilers you can use intrinsics like: + +Visual Studio: + return __popcnt(v); +GCC, Clang: + return static_cast(__builtin_popcount(v)); + +Define macro VMA_COUNT_BITS_SET to provide your optimized implementation. +But you need to check in runtime whether user's CPU supports these, as some old processors don't. +*/ +static inline uint32_t VmaCountBitsSet(uint32_t v) +{ +#if VMA_CPP20 + return std::popcount(v); +#else + uint32_t c = v - ((v >> 1) & 0x55555555); + c = ((c >> 2) & 0x33333333) + (c & 0x33333333); + c = ((c >> 4) + c) & 0x0F0F0F0F; + c = ((c >> 8) + c) & 0x00FF00FF; + c = ((c >> 16) + c) & 0x0000FFFF; + return c; +#endif +} + +static inline uint8_t VmaBitScanLSB(uint64_t mask) +{ +#if defined(_MSC_VER) && defined(_WIN64) + unsigned long pos; + if (_BitScanForward64(&pos, mask)) + return static_cast(pos); + return UINT8_MAX; +#elif VMA_CPP20 + if(mask) + return static_cast(std::countr_zero(mask)); + return UINT8_MAX; +#elif defined __GNUC__ || defined __clang__ + return static_cast(__builtin_ffsll(mask)) - 1U; +#else + uint8_t pos = 0; + uint64_t bit = 1; + do + { + if (mask & bit) + return pos; + bit <<= 1; + } while (pos++ < 63); + return UINT8_MAX; +#endif +} + +static inline uint8_t VmaBitScanLSB(uint32_t mask) +{ +#ifdef _MSC_VER + unsigned long pos; + if (_BitScanForward(&pos, mask)) + return static_cast(pos); + return UINT8_MAX; +#elif VMA_CPP20 + if(mask) + return static_cast(std::countr_zero(mask)); + return UINT8_MAX; +#elif defined __GNUC__ || defined __clang__ + return static_cast(__builtin_ffs(mask)) - 1U; +#else + uint8_t pos = 0; + uint32_t bit = 1; + do + { + if (mask & bit) + return pos; + bit <<= 1; + } while (pos++ < 31); + return UINT8_MAX; +#endif +} + +static inline uint8_t VmaBitScanMSB(uint64_t mask) +{ +#if defined(_MSC_VER) && defined(_WIN64) + unsigned long pos; + if (_BitScanReverse64(&pos, mask)) + return static_cast(pos); +#elif VMA_CPP20 + if(mask) + return 63 - static_cast(std::countl_zero(mask)); +#elif defined __GNUC__ || defined __clang__ + if (mask) + return 63 - static_cast(__builtin_clzll(mask)); +#else + uint8_t pos = 63; + uint64_t bit = 1ULL << 63; + do + { + if (mask & bit) + return pos; + bit >>= 1; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} + +static inline uint8_t VmaBitScanMSB(uint32_t mask) +{ +#ifdef _MSC_VER + unsigned long pos; + if (_BitScanReverse(&pos, mask)) + return static_cast(pos); +#elif VMA_CPP20 + if(mask) + return 31 - static_cast(std::countl_zero(mask)); +#elif defined __GNUC__ || defined __clang__ + if (mask) + return 31 - static_cast(__builtin_clz(mask)); +#else + uint8_t pos = 31; + uint32_t bit = 1UL << 31; + do + { + if (mask & bit) + return pos; + bit >>= 1; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} + +/* +Returns true if given number is a power of two. +T must be unsigned integer number or signed integer but always nonnegative. +For 0 returns true. +*/ +template +inline bool VmaIsPow2(T x) +{ + return (x & (x - 1)) == 0; +} + +// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16. +// Use types like uint32_t, uint64_t as T. +template +static inline T VmaAlignUp(T val, T alignment) +{ + VMA_HEAVY_ASSERT(VmaIsPow2(alignment)); + return (val + alignment - 1) & ~(alignment - 1); +} + +// Aligns given value down to nearest multiply of align value. For example: VmaAlignDown(11, 8) = 8. +// Use types like uint32_t, uint64_t as T. +template +static inline T VmaAlignDown(T val, T alignment) +{ + VMA_HEAVY_ASSERT(VmaIsPow2(alignment)); + return val & ~(alignment - 1); +} + +// Division with mathematical rounding to nearest number. +template +static inline T VmaRoundDiv(T x, T y) +{ + return (x + (y / (T)2)) / y; +} + +// Divide by 'y' and round up to nearest integer. +template +static inline T VmaDivideRoundingUp(T x, T y) +{ + return (x + y - (T)1) / y; +} + +// Returns smallest power of 2 greater or equal to v. +static inline uint32_t VmaNextPow2(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static inline uint64_t VmaNextPow2(uint64_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +// Returns largest power of 2 less or equal to v. +static inline uint32_t VmaPrevPow2(uint32_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v = v ^ (v >> 1); + return v; +} + +static inline uint64_t VmaPrevPow2(uint64_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v = v ^ (v >> 1); + return v; +} + +static inline bool VmaStrIsEmpty(const char* pStr) +{ + return pStr == VMA_NULL || *pStr == '\0'; +} + +/* +Returns true if two memory blocks occupy overlapping pages. +ResourceA must be in less memory offset than ResourceB. + +Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)" +chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity". +*/ +static inline bool VmaBlocksOnSamePage( + VkDeviceSize resourceAOffset, + VkDeviceSize resourceASize, + VkDeviceSize resourceBOffset, + VkDeviceSize pageSize) +{ + VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0); + VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1; + VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1); + VkDeviceSize resourceBStart = resourceBOffset; + VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1); + return resourceAEndPage == resourceBStartPage; +} + +/* +Returns true if given suballocation types could conflict and must respect +VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer +or linear image and another one is optimal image. If type is unknown, behave +conservatively. +*/ +static inline bool VmaIsBufferImageGranularityConflict( + VmaSuballocationType suballocType1, + VmaSuballocationType suballocType2) +{ + if (suballocType1 > suballocType2) + { + std::swap(suballocType1, suballocType2); + } + + switch (suballocType1) + { + case VMA_SUBALLOCATION_TYPE_FREE: + return false; + case VMA_SUBALLOCATION_TYPE_UNKNOWN: + return true; + case VMA_SUBALLOCATION_TYPE_BUFFER: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL: + return false; + default: + VMA_ASSERT(0); + return true; + } +} + +static void VmaWriteMagicValue(void* pData, VkDeviceSize offset) +{ +#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION + uint32_t* pDst = (uint32_t*)((char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for (size_t i = 0; i < numberCount; ++i, ++pDst) + { + *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE; + } +#else + // no-op +#endif +} + +static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset) +{ +#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION + const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for (size_t i = 0; i < numberCount; ++i, ++pSrc) + { + if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE) + { + return false; + } + } +#endif + return true; +} + +/* +Fills structure with parameters of an example buffer to be used for transfers +during GPU memory defragmentation. +*/ +static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo) +{ + memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo)); + outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size. +} + + +/* +Performs binary search and returns iterator to first element that is greater or +equal to (key), according to comparison (cmp). + +Cmp should return true if first argument is less than second argument. + +Returned value is the found element, if present in the collection or place where +new element with value (key) should be inserted. +*/ +template +static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp) +{ + size_t down = 0, up = size_t(end - beg); + while (down < up) + { + const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation + if (cmp(*(beg + mid), key)) + { + down = mid + 1; + } + else + { + up = mid; + } + } + return beg + down; +} + +template +IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp) +{ + IterT it = VmaBinaryFindFirstNotLess( + beg, end, value, cmp); + if (it == end || + (!cmp(*it, value) && !cmp(value, *it))) + { + return it; + } + return end; +} + +/* +Returns true if all pointers in the array are not-null and unique. +Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT. +T must be pointer type, e.g. VmaAllocation, VmaPool. +*/ +template +static bool VmaValidatePointerArray(uint32_t count, const T* arr) +{ + for (uint32_t i = 0; i < count; ++i) + { + const T iPtr = arr[i]; + if (iPtr == VMA_NULL) + { + return false; + } + for (uint32_t j = i + 1; j < count; ++j) + { + if (iPtr == arr[j]) + { + return false; + } + } + } + return true; +} + +template +static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct) +{ + newStruct->pNext = mainStruct->pNext; + mainStruct->pNext = newStruct; +} +// Finds structure with s->sType == sType in mainStruct->pNext chain. +// Returns pointer to it. If not found, returns null. +template +static inline const FindT* VmaPnextChainFind(const MainT* mainStruct, VkStructureType sType) +{ + for(const VkBaseInStructure* s = (const VkBaseInStructure*)mainStruct->pNext; + s != VMA_NULL; s = s->pNext) + { + if(s->sType == sType) + { + return (const FindT*)s; + } + } + return VMA_NULL; +} + +// An abstraction over buffer or image `usage` flags, depending on available extensions. +struct VmaBufferImageUsage +{ +#if VMA_KHR_MAINTENANCE5 + typedef uint64_t BaseType; // VkFlags64 +#else + typedef uint32_t BaseType; // VkFlags32 +#endif + + static const VmaBufferImageUsage UNKNOWN; + + BaseType Value; + + VmaBufferImageUsage() { *this = UNKNOWN; } + explicit VmaBufferImageUsage(BaseType usage) : Value(usage) { } + VmaBufferImageUsage(const VkBufferCreateInfo &createInfo, bool useKhrMaintenance5); + explicit VmaBufferImageUsage(const VkImageCreateInfo &createInfo); + + bool operator==(const VmaBufferImageUsage& rhs) const { return Value == rhs.Value; } + bool operator!=(const VmaBufferImageUsage& rhs) const { return Value != rhs.Value; } + + bool Contains(BaseType flag) const { return (Value & flag) != 0; } + bool ContainsDeviceAccess() const + { + // This relies on values of VK_IMAGE_USAGE_TRANSFER* being the same as VK_BUFFER_IMAGE_TRANSFER*. + return (Value & ~BaseType(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) != 0; + } +}; + +const VmaBufferImageUsage VmaBufferImageUsage::UNKNOWN = VmaBufferImageUsage(0); + +static void swap(VmaBufferImageUsage& lhs, VmaBufferImageUsage& rhs) noexcept +{ + using std::swap; + swap(lhs.Value, rhs.Value); +} + +VmaBufferImageUsage::VmaBufferImageUsage(const VkBufferCreateInfo &createInfo, + bool useKhrMaintenance5) +{ +#if VMA_KHR_MAINTENANCE5 + if(useKhrMaintenance5) + { + // If VkBufferCreateInfo::pNext chain contains VkBufferUsageFlags2CreateInfoKHR, + // take usage from it and ignore VkBufferCreateInfo::usage, per specification + // of the VK_KHR_maintenance5 extension. + const VkBufferUsageFlags2CreateInfoKHR* const usageFlags2 = + VmaPnextChainFind(&createInfo, VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO_KHR); + if(usageFlags2) + { + this->Value = usageFlags2->usage; + return; + } + } +#endif + + this->Value = (BaseType)createInfo.usage; +} + +VmaBufferImageUsage::VmaBufferImageUsage(const VkImageCreateInfo &createInfo) +{ + // Maybe in the future there will be VK_KHR_maintenanceN extension with structure + // VkImageUsageFlags2CreateInfoKHR, like the one for buffers... + + this->Value = (BaseType)createInfo.usage; +} + +// This is the main algorithm that guides the selection of a memory type best for an allocation - +// converts usage to required/preferred/not preferred flags. +static bool FindMemoryPreferences( + bool isIntegratedGPU, + const VmaAllocationCreateInfo& allocCreateInfo, + VmaBufferImageUsage bufImgUsage, + VkMemoryPropertyFlags& outRequiredFlags, + VkMemoryPropertyFlags& outPreferredFlags, + VkMemoryPropertyFlags& outNotPreferredFlags) +{ + outRequiredFlags = allocCreateInfo.requiredFlags; + outPreferredFlags = allocCreateInfo.preferredFlags; + outNotPreferredFlags = 0; + + switch(allocCreateInfo.usage) + { + case VMA_MEMORY_USAGE_UNKNOWN: + break; + case VMA_MEMORY_USAGE_GPU_ONLY: + if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + case VMA_MEMORY_USAGE_CPU_ONLY: + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + case VMA_MEMORY_USAGE_CPU_TO_GPU: + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + case VMA_MEMORY_USAGE_GPU_TO_CPU: + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + break; + case VMA_MEMORY_USAGE_CPU_COPY: + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + break; + case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED: + outRequiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; + break; + case VMA_MEMORY_USAGE_AUTO: + case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE: + case VMA_MEMORY_USAGE_AUTO_PREFER_HOST: + { + if(bufImgUsage == VmaBufferImageUsage::UNKNOWN) + { + VMA_ASSERT(0 && "VMA_MEMORY_USAGE_AUTO* values can only be used with functions like vmaCreateBuffer, vmaCreateImage so that the details of the created resource are known." + " Maybe you use VkBufferUsageFlags2CreateInfoKHR but forgot to use VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT?" ); + return false; + } + + const bool deviceAccess = bufImgUsage.ContainsDeviceAccess(); + const bool hostAccessSequentialWrite = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) != 0; + const bool hostAccessRandom = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) != 0; + const bool hostAccessAllowTransferInstead = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) != 0; + const bool preferDevice = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + const bool preferHost = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + + // CPU random access - e.g. a buffer written to or transferred from GPU to read back on CPU. + if(hostAccessRandom) + { + // Prefer cached. Cannot require it, because some platforms don't have it (e.g. Raspberry Pi - see #362)! + outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + + if (!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost) + { + // Nice if it will end up in HOST_VISIBLE, but more importantly prefer DEVICE_LOCAL. + // Omitting HOST_VISIBLE here is intentional. + // In case there is DEVICE_LOCAL | HOST_VISIBLE | HOST_CACHED, it will pick that one. + // Otherwise, this will give same weight to DEVICE_LOCAL as HOST_VISIBLE | HOST_CACHED and select the former if occurs first on the list. + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + else + { + // Always CPU memory. + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + } + // CPU sequential write - may be CPU or host-visible GPU memory, uncached and write-combined. + else if(hostAccessSequentialWrite) + { + // Want uncached and write-combined. + outNotPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + + if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost) + { + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + else + { + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + // Direct GPU access, CPU sequential write (e.g. a dynamic uniform buffer updated every frame) + if(deviceAccess) + { + // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose GPU memory. + if(preferHost) + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + // GPU no direct access, CPU sequential write (e.g. an upload buffer to be transferred to the GPU) + else + { + // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose CPU memory. + if(preferDevice) + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + } + } + // No CPU access + else + { + // if(deviceAccess) + // + // GPU access, no CPU access (e.g. a color attachment image) - prefer GPU memory, + // unless there is a clear preference from the user not to do so. + // + // else: + // + // No direct GPU access, no CPU access, just transfers. + // It may be staging copy intended for e.g. preserving image for next frame (then better GPU memory) or + // a "swap file" copy to free some GPU memory (then better CPU memory). + // Up to the user to decide. If no preferece, assume the former and choose GPU memory. + + if(preferHost) + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + } + default: + VMA_ASSERT(0); + } + + // Avoid DEVICE_COHERENT unless explicitly requested. + if(((allocCreateInfo.requiredFlags | allocCreateInfo.preferredFlags) & + (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0) + { + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Memory allocation + +static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment) +{ + void* result = VMA_NULL; + if ((pAllocationCallbacks != VMA_NULL) && + (pAllocationCallbacks->pfnAllocation != VMA_NULL)) + { + result = (*pAllocationCallbacks->pfnAllocation)( + pAllocationCallbacks->pUserData, + size, + alignment, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + } + else + { + result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment); + } + VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed."); + return result; +} + +static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr) +{ + if ((pAllocationCallbacks != VMA_NULL) && + (pAllocationCallbacks->pfnFree != VMA_NULL)) + { + (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr); + } + else + { + VMA_SYSTEM_ALIGNED_FREE(ptr); + } +} + +template +static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks) +{ + return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T)); +} + +template +static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count) +{ + return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T)); +} + +#define vma_new(allocator, type) new(VmaAllocate(allocator))(type) + +#define vma_new_array(allocator, type, count) new(VmaAllocateArray((allocator), (count)))(type) + +template +static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr) +{ + ptr->~T(); + VmaFree(pAllocationCallbacks, ptr); +} + +template +static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count) +{ + if (ptr != VMA_NULL) + { + for (size_t i = count; i--; ) + { + ptr[i].~T(); + } + VmaFree(pAllocationCallbacks, ptr); + } +} + +static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr) +{ + if (srcStr != VMA_NULL) + { + const size_t len = strlen(srcStr); + char* const result = vma_new_array(allocs, char, len + 1); + memcpy(result, srcStr, len + 1); + return result; + } + return VMA_NULL; +} + +#if VMA_STATS_STRING_ENABLED +static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen) +{ + if (srcStr != VMA_NULL) + { + char* const result = vma_new_array(allocs, char, strLen + 1); + memcpy(result, srcStr, strLen); + result[strLen] = '\0'; + return result; + } + return VMA_NULL; +} +#endif // VMA_STATS_STRING_ENABLED + +static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str) +{ + if (str != VMA_NULL) + { + const size_t len = strlen(str); + vma_delete_array(allocs, str, len + 1); + } +} + +template +size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value) +{ + const size_t indexToInsert = VmaBinaryFindFirstNotLess( + vector.data(), + vector.data() + vector.size(), + value, + CmpLess()) - vector.data(); + VmaVectorInsert(vector, indexToInsert, value); + return indexToInsert; +} + +template +bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value) +{ + CmpLess comparator; + typename VectorT::iterator it = VmaBinaryFindFirstNotLess( + vector.begin(), + vector.end(), + value, + comparator); + if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it)) + { + size_t indexToRemove = it - vector.begin(); + VmaVectorRemove(vector, indexToRemove); + return true; + } + return false; +} +#endif // _VMA_FUNCTIONS + +#ifndef _VMA_STATISTICS_FUNCTIONS + +static void VmaClearStatistics(VmaStatistics& outStats) +{ + outStats.blockCount = 0; + outStats.allocationCount = 0; + outStats.blockBytes = 0; + outStats.allocationBytes = 0; +} + +static void VmaAddStatistics(VmaStatistics& inoutStats, const VmaStatistics& src) +{ + inoutStats.blockCount += src.blockCount; + inoutStats.allocationCount += src.allocationCount; + inoutStats.blockBytes += src.blockBytes; + inoutStats.allocationBytes += src.allocationBytes; +} + +static void VmaClearDetailedStatistics(VmaDetailedStatistics& outStats) +{ + VmaClearStatistics(outStats.statistics); + outStats.unusedRangeCount = 0; + outStats.allocationSizeMin = VK_WHOLE_SIZE; + outStats.allocationSizeMax = 0; + outStats.unusedRangeSizeMin = VK_WHOLE_SIZE; + outStats.unusedRangeSizeMax = 0; +} + +static void VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics& inoutStats, VkDeviceSize size) +{ + inoutStats.statistics.allocationCount++; + inoutStats.statistics.allocationBytes += size; + inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, size); + inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, size); +} + +static void VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics& inoutStats, VkDeviceSize size) +{ + inoutStats.unusedRangeCount++; + inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, size); + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, size); +} + +static void VmaAddDetailedStatistics(VmaDetailedStatistics& inoutStats, const VmaDetailedStatistics& src) +{ + VmaAddStatistics(inoutStats.statistics, src.statistics); + inoutStats.unusedRangeCount += src.unusedRangeCount; + inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, src.allocationSizeMin); + inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, src.allocationSizeMax); + inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, src.unusedRangeSizeMin); + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, src.unusedRangeSizeMax); +} + +#endif // _VMA_STATISTICS_FUNCTIONS + +#ifndef _VMA_MUTEX_LOCK +// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope). +struct VmaMutexLock +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLock) +public: + VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->Lock(); } + } + ~VmaMutexLock() { if (m_pMutex) { m_pMutex->Unlock(); } } + +private: + VMA_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading. +struct VmaMutexLockRead +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockRead) +public: + VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->LockRead(); } + } + ~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } } + +private: + VMA_RW_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing. +struct VmaMutexLockWrite +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockWrite) +public: + VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) + : m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->LockWrite(); } + } + ~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } } + +private: + VMA_RW_MUTEX* m_pMutex; +}; + +#if VMA_DEBUG_GLOBAL_MUTEX + static VMA_MUTEX gDebugGlobalMutex; + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true); +#else + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK +#endif +#endif // _VMA_MUTEX_LOCK + +#ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT +// An object that increments given atomic but decrements it back in the destructor unless Commit() is called. +template +struct AtomicTransactionalIncrement +{ +public: + using T = decltype(AtomicT().load()); + + ~AtomicTransactionalIncrement() + { + if(m_Atomic) + --(*m_Atomic); + } + + void Commit() { m_Atomic = VMA_NULL; } + T Increment(AtomicT* atomic) + { + m_Atomic = atomic; + return m_Atomic->fetch_add(1); + } + +private: + AtomicT* m_Atomic = VMA_NULL; +}; +#endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT + +#ifndef _VMA_STL_ALLOCATOR +// STL-compatible allocator. +template +struct VmaStlAllocator +{ + const VkAllocationCallbacks* const m_pCallbacks; + typedef T value_type; + + VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {} + template + VmaStlAllocator(const VmaStlAllocator& src) : m_pCallbacks(src.m_pCallbacks) {} + VmaStlAllocator(const VmaStlAllocator&) = default; + VmaStlAllocator& operator=(const VmaStlAllocator&) = delete; + + T* allocate(size_t n) { return VmaAllocateArray(m_pCallbacks, n); } + void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); } + + template + bool operator==(const VmaStlAllocator& rhs) const + { + return m_pCallbacks == rhs.m_pCallbacks; + } + template + bool operator!=(const VmaStlAllocator& rhs) const + { + return m_pCallbacks != rhs.m_pCallbacks; + } +}; +#endif // _VMA_STL_ALLOCATOR + +#ifndef _VMA_VECTOR +/* Class with interface compatible with subset of std::vector. +T must be POD because constructors and destructors are not called and memcpy is +used for these objects. */ +template +class VmaVector +{ +public: + typedef T value_type; + typedef T* iterator; + typedef const T* const_iterator; + + VmaVector(const AllocatorT& allocator); + VmaVector(size_t count, const AllocatorT& allocator); + // This version of the constructor is here for compatibility with pre-C++14 std::vector. + // value is unused. + VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {} + VmaVector(const VmaVector& src); + VmaVector& operator=(const VmaVector& rhs); + ~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); } + + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_pArray; } + T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; } + T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; } + const T* data() const { return m_pArray; } + const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; } + const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; } + + iterator begin() { return m_pArray; } + iterator end() { return m_pArray + m_Count; } + const_iterator cbegin() const { return m_pArray; } + const_iterator cend() const { return m_pArray + m_Count; } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + + void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); } + void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); } + void push_front(const T& src) { insert(0, src); } + + void push_back(const T& src); + void reserve(size_t newCapacity, bool freeMemory = false); + void resize(size_t newCount); + void clear() { resize(0); } + void shrink_to_fit(); + void insert(size_t index, const T& src); + void remove(size_t index); + + T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; } + const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; } + +private: + AllocatorT m_Allocator; + T* m_pArray; + size_t m_Count; + size_t m_Capacity; +}; + +#ifndef _VMA_VECTOR_FUNCTIONS +template +VmaVector::VmaVector(const AllocatorT& allocator) + : m_Allocator(allocator), + m_pArray(VMA_NULL), + m_Count(0), + m_Capacity(0) {} + +template +VmaVector::VmaVector(size_t count, const AllocatorT& allocator) + : m_Allocator(allocator), + m_pArray(count ? (T*)VmaAllocateArray(allocator.m_pCallbacks, count) : VMA_NULL), + m_Count(count), + m_Capacity(count) {} + +template +VmaVector::VmaVector(const VmaVector& src) + : m_Allocator(src.m_Allocator), + m_pArray(src.m_Count ? (T*)VmaAllocateArray(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL), + m_Count(src.m_Count), + m_Capacity(src.m_Count) +{ + if (m_Count != 0) + { + memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T)); + } +} + +template +VmaVector& VmaVector::operator=(const VmaVector& rhs) +{ + if (&rhs != this) + { + resize(rhs.m_Count); + if (m_Count != 0) + { + memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T)); + } + } + return *this; +} + +template +void VmaVector::push_back(const T& src) +{ + const size_t newIndex = size(); + resize(newIndex + 1); + m_pArray[newIndex] = src; +} + +template +void VmaVector::reserve(size_t newCapacity, bool freeMemory) +{ + newCapacity = VMA_MAX(newCapacity, m_Count); + + if ((newCapacity < m_Capacity) && !freeMemory) + { + newCapacity = m_Capacity; + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator, newCapacity) : VMA_NULL; + if (m_Count != 0) + { + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } +} + +template +void VmaVector::resize(size_t newCount) +{ + size_t newCapacity = m_Capacity; + if (newCount > m_Capacity) + { + newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8)); + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL; + const size_t elementsToCopy = VMA_MIN(m_Count, newCount); + if (elementsToCopy != 0) + { + memcpy(newArray, m_pArray, elementsToCopy * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } + + m_Count = newCount; +} + +template +void VmaVector::shrink_to_fit() +{ + if (m_Capacity > m_Count) + { + T* newArray = VMA_NULL; + if (m_Count > 0) + { + newArray = VmaAllocateArray(m_Allocator.m_pCallbacks, m_Count); + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = m_Count; + m_pArray = newArray; + } +} + +template +void VmaVector::insert(size_t index, const T& src) +{ + VMA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + if (index < oldCount) + { + memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T)); + } + m_pArray[index] = src; +} + +template +void VmaVector::remove(size_t index) +{ + VMA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if (index < oldCount - 1) + { + memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); +} +#endif // _VMA_VECTOR_FUNCTIONS + +template +static void VmaVectorInsert(VmaVector& vec, size_t index, const T& item) +{ + vec.insert(index, item); +} + +template +static void VmaVectorRemove(VmaVector& vec, size_t index) +{ + vec.remove(index); +} +#endif // _VMA_VECTOR + +#ifndef _VMA_SMALL_VECTOR +/* +This is a vector (a variable-sized array), optimized for the case when the array is small. + +It contains some number of elements in-place, which allows it to avoid heap allocation +when the actual number of elements is below that threshold. This allows normal "small" +cases to be fast without losing generality for large inputs. +*/ +template +class VmaSmallVector +{ +public: + typedef T value_type; + typedef T* iterator; + + VmaSmallVector(const AllocatorT& allocator); + VmaSmallVector(size_t count, const AllocatorT& allocator); + template + VmaSmallVector(const VmaSmallVector&) = delete; + template + VmaSmallVector& operator=(const VmaSmallVector&) = delete; + ~VmaSmallVector() = default; + + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; } + T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; } + T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; } + const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; } + const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; } + const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; } + + iterator begin() { return data(); } + iterator end() { return data() + m_Count; } + + void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); } + void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); } + void push_front(const T& src) { insert(0, src); } + + void push_back(const T& src); + void resize(size_t newCount, bool freeMemory = false); + void clear(bool freeMemory = false); + void insert(size_t index, const T& src); + void remove(size_t index); + + T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; } + const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; } + +private: + size_t m_Count; + T m_StaticArray[N]; // Used when m_Size <= N + VmaVector m_DynamicArray; // Used when m_Size > N +}; + +#ifndef _VMA_SMALL_VECTOR_FUNCTIONS +template +VmaSmallVector::VmaSmallVector(const AllocatorT& allocator) + : m_Count(0), + m_DynamicArray(allocator) {} + +template +VmaSmallVector::VmaSmallVector(size_t count, const AllocatorT& allocator) + : m_Count(count), + m_DynamicArray(count > N ? count : 0, allocator) {} + +template +void VmaSmallVector::push_back(const T& src) +{ + const size_t newIndex = size(); + resize(newIndex + 1); + data()[newIndex] = src; +} + +template +void VmaSmallVector::resize(size_t newCount, bool freeMemory) +{ + if (newCount > N && m_Count > N) + { + // Any direction, staying in m_DynamicArray + m_DynamicArray.resize(newCount); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + } + else if (newCount > N && m_Count <= N) + { + // Growing, moving from m_StaticArray to m_DynamicArray + m_DynamicArray.resize(newCount); + if (m_Count > 0) + { + memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T)); + } + } + else if (newCount <= N && m_Count > N) + { + // Shrinking, moving from m_DynamicArray to m_StaticArray + if (newCount > 0) + { + memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T)); + } + m_DynamicArray.resize(0); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + } + else + { + // Any direction, staying in m_StaticArray - nothing to do here + } + m_Count = newCount; +} + +template +void VmaSmallVector::clear(bool freeMemory) +{ + m_DynamicArray.clear(); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + m_Count = 0; +} + +template +void VmaSmallVector::insert(size_t index, const T& src) +{ + VMA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + T* const dataPtr = data(); + if (index < oldCount) + { + // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray. + memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T)); + } + dataPtr[index] = src; +} + +template +void VmaSmallVector::remove(size_t index) +{ + VMA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if (index < oldCount - 1) + { + // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray. + T* const dataPtr = data(); + memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); +} +#endif // _VMA_SMALL_VECTOR_FUNCTIONS +#endif // _VMA_SMALL_VECTOR + +#ifndef _VMA_POOL_ALLOCATOR +/* +Allocator for objects of type T using a list of arrays (pools) to speed up +allocation. Number of elements that can be allocated is not bounded because +allocator can create multiple blocks. +*/ +template +class VmaPoolAllocator +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaPoolAllocator) +public: + VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity); + ~VmaPoolAllocator(); + template T* Alloc(Types&&... args); + void Free(T* ptr); + +private: + union Item + { + uint32_t NextFreeIndex; + alignas(T) char Value[sizeof(T)]; + }; + struct ItemBlock + { + Item* pItems; + uint32_t Capacity; + uint32_t FirstFreeIndex; + }; + + const VkAllocationCallbacks* m_pAllocationCallbacks; + const uint32_t m_FirstBlockCapacity; + VmaVector> m_ItemBlocks; + + ItemBlock& CreateNewBlock(); +}; + +#ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS +template +VmaPoolAllocator::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) + : m_pAllocationCallbacks(pAllocationCallbacks), + m_FirstBlockCapacity(firstBlockCapacity), + m_ItemBlocks(VmaStlAllocator(pAllocationCallbacks)) +{ + VMA_ASSERT(m_FirstBlockCapacity > 1); +} + +template +VmaPoolAllocator::~VmaPoolAllocator() +{ + for (size_t i = m_ItemBlocks.size(); i--;) + vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity); + m_ItemBlocks.clear(); +} + +template +template T* VmaPoolAllocator::Alloc(Types&&... args) +{ + for (size_t i = m_ItemBlocks.size(); i--; ) + { + ItemBlock& block = m_ItemBlocks[i]; + // This block has some free items: Use first one. + if (block.FirstFreeIndex != UINT32_MAX) + { + Item* const pItem = &block.pItems[block.FirstFreeIndex]; + block.FirstFreeIndex = pItem->NextFreeIndex; + T* result = (T*)&pItem->Value; + new(result)T(std::forward(args)...); // Explicit constructor call. + return result; + } + } + + // No block has free item: Create new one and use it. + ItemBlock& newBlock = CreateNewBlock(); + Item* const pItem = &newBlock.pItems[0]; + newBlock.FirstFreeIndex = pItem->NextFreeIndex; + T* result = (T*)&pItem->Value; + new(result) T(std::forward(args)...); // Explicit constructor call. + return result; +} + +template +void VmaPoolAllocator::Free(T* ptr) +{ + // Search all memory blocks to find ptr. + for (size_t i = m_ItemBlocks.size(); i--; ) + { + ItemBlock& block = m_ItemBlocks[i]; + + // Casting to union. + Item* pItemPtr; + memcpy(&pItemPtr, &ptr, sizeof(pItemPtr)); + + // Check if pItemPtr is in address range of this block. + if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity)) + { + ptr->~T(); // Explicit destructor call. + const uint32_t index = static_cast(pItemPtr - block.pItems); + pItemPtr->NextFreeIndex = block.FirstFreeIndex; + block.FirstFreeIndex = index; + return; + } + } + VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool."); +} + +template +typename VmaPoolAllocator::ItemBlock& VmaPoolAllocator::CreateNewBlock() +{ + const uint32_t newBlockCapacity = m_ItemBlocks.empty() ? + m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2; + + const ItemBlock newBlock = + { + vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity), + newBlockCapacity, + 0 + }; + + m_ItemBlocks.push_back(newBlock); + + // Setup singly-linked list of all free items in this block. + for (uint32_t i = 0; i < newBlockCapacity - 1; ++i) + newBlock.pItems[i].NextFreeIndex = i + 1; + newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX; + return m_ItemBlocks.back(); +} +#endif // _VMA_POOL_ALLOCATOR_FUNCTIONS +#endif // _VMA_POOL_ALLOCATOR + +#ifndef _VMA_RAW_LIST +template +struct VmaListItem +{ + VmaListItem* pPrev; + VmaListItem* pNext; + T Value; +}; + +// Doubly linked list. +template +class VmaRawList +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaRawList) +public: + typedef VmaListItem ItemType; + + VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks); + // Intentionally not calling Clear, because that would be unnecessary + // computations to return all items to m_ItemAllocator as free. + ~VmaRawList() = default; + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + + ItemType* Front() { return m_pFront; } + ItemType* Back() { return m_pBack; } + const ItemType* Front() const { return m_pFront; } + const ItemType* Back() const { return m_pBack; } + + ItemType* PushFront(); + ItemType* PushBack(); + ItemType* PushFront(const T& value); + ItemType* PushBack(const T& value); + void PopFront(); + void PopBack(); + + // Item can be null - it means PushBack. + ItemType* InsertBefore(ItemType* pItem); + // Item can be null - it means PushFront. + ItemType* InsertAfter(ItemType* pItem); + ItemType* InsertBefore(ItemType* pItem, const T& value); + ItemType* InsertAfter(ItemType* pItem, const T& value); + + void Clear(); + void Remove(ItemType* pItem); + +private: + const VkAllocationCallbacks* const m_pAllocationCallbacks; + VmaPoolAllocator m_ItemAllocator; + ItemType* m_pFront; + ItemType* m_pBack; + size_t m_Count; +}; + +#ifndef _VMA_RAW_LIST_FUNCTIONS +template +VmaRawList::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) + : m_pAllocationCallbacks(pAllocationCallbacks), + m_ItemAllocator(pAllocationCallbacks, 128), + m_pFront(VMA_NULL), + m_pBack(VMA_NULL), + m_Count(0) {} + +template +VmaListItem* VmaRawList::PushFront() +{ + ItemType* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pPrev = VMA_NULL; + if (IsEmpty()) + { + pNewItem->pNext = VMA_NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pNext = m_pFront; + m_pFront->pPrev = pNewItem; + m_pFront = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushBack() +{ + ItemType* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pNext = VMA_NULL; + if(IsEmpty()) + { + pNewItem->pPrev = VMA_NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pPrev = m_pBack; + m_pBack->pNext = pNewItem; + m_pBack = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushFront(const T& value) +{ + ItemType* const pNewItem = PushFront(); + pNewItem->Value = value; + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushBack(const T& value) +{ + ItemType* const pNewItem = PushBack(); + pNewItem->Value = value; + return pNewItem; +} + +template +void VmaRawList::PopFront() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const pFrontItem = m_pFront; + ItemType* const pNextItem = pFrontItem->pNext; + if (pNextItem != VMA_NULL) + { + pNextItem->pPrev = VMA_NULL; + } + m_pFront = pNextItem; + m_ItemAllocator.Free(pFrontItem); + --m_Count; +} + +template +void VmaRawList::PopBack() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const pBackItem = m_pBack; + ItemType* const pPrevItem = pBackItem->pPrev; + if(pPrevItem != VMA_NULL) + { + pPrevItem->pNext = VMA_NULL; + } + m_pBack = pPrevItem; + m_ItemAllocator.Free(pBackItem); + --m_Count; +} + +template +void VmaRawList::Clear() +{ + if (IsEmpty() == false) + { + ItemType* pItem = m_pBack; + while (pItem != VMA_NULL) + { + ItemType* const pPrevItem = pItem->pPrev; + m_ItemAllocator.Free(pItem); + pItem = pPrevItem; + } + m_pFront = VMA_NULL; + m_pBack = VMA_NULL; + m_Count = 0; + } +} + +template +void VmaRawList::Remove(ItemType* pItem) +{ + VMA_HEAVY_ASSERT(pItem != VMA_NULL); + VMA_HEAVY_ASSERT(m_Count > 0); + + if(pItem->pPrev != VMA_NULL) + { + pItem->pPrev->pNext = pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = pItem->pNext; + } + + if(pItem->pNext != VMA_NULL) + { + pItem->pNext->pPrev = pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = pItem->pPrev; + } + + m_ItemAllocator.Free(pItem); + --m_Count; +} + +template +VmaListItem* VmaRawList::InsertBefore(ItemType* pItem) +{ + if(pItem != VMA_NULL) + { + ItemType* const prevItem = pItem->pPrev; + ItemType* const newItem = m_ItemAllocator.Alloc(); + newItem->pPrev = prevItem; + newItem->pNext = pItem; + pItem->pPrev = newItem; + if(prevItem != VMA_NULL) + { + prevItem->pNext = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = newItem; + } + ++m_Count; + return newItem; + } + else + return PushBack(); +} + +template +VmaListItem* VmaRawList::InsertAfter(ItemType* pItem) +{ + if(pItem != VMA_NULL) + { + ItemType* const nextItem = pItem->pNext; + ItemType* const newItem = m_ItemAllocator.Alloc(); + newItem->pNext = nextItem; + newItem->pPrev = pItem; + pItem->pNext = newItem; + if(nextItem != VMA_NULL) + { + nextItem->pPrev = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = newItem; + } + ++m_Count; + return newItem; + } + else + return PushFront(); +} + +template +VmaListItem* VmaRawList::InsertBefore(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertBefore(pItem); + newItem->Value = value; + return newItem; +} + +template +VmaListItem* VmaRawList::InsertAfter(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertAfter(pItem); + newItem->Value = value; + return newItem; +} +#endif // _VMA_RAW_LIST_FUNCTIONS +#endif // _VMA_RAW_LIST + +#ifndef _VMA_LIST +template +class VmaList +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaList) +public: + class reverse_iterator; + class const_iterator; + class const_reverse_iterator; + + class iterator + { + friend class const_iterator; + friend class VmaList; + public: + iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + iterator operator++(int) { iterator result = *this; ++*this; return result; } + iterator operator--(int) { iterator result = *this; --*this; return result; } + + iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; } + iterator& operator--(); + + private: + VmaRawList* m_pList; + VmaListItem* m_pItem; + + iterator(VmaRawList* pList, VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class reverse_iterator + { + friend class const_reverse_iterator; + friend class VmaList; + public: + reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; } + reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; } + + reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; } + reverse_iterator& operator--(); + + private: + VmaRawList* m_pList; + VmaListItem* m_pItem; + + reverse_iterator(VmaRawList* pList, VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class const_iterator + { + friend class VmaList; + public: + const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + iterator drop_const() { return { const_cast*>(m_pList), const_cast*>(m_pItem) }; } + + const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; } + const_iterator operator--(int) { const_iterator result = *this; --* this; return result; } + + const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; } + const_iterator& operator--(); + + private: + const VmaRawList* m_pList; + const VmaListItem* m_pItem; + + const_iterator(const VmaRawList* pList, const VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class const_reverse_iterator + { + friend class VmaList; + public: + const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + reverse_iterator drop_const() { return { const_cast*>(m_pList), const_cast*>(m_pItem) }; } + + const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; } + const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; } + + const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; } + const_reverse_iterator& operator--(); + + private: + const VmaRawList* m_pList; + const VmaListItem* m_pItem; + + const_reverse_iterator(const VmaRawList* pList, const VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + + VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {} + + bool empty() const { return m_RawList.IsEmpty(); } + size_t size() const { return m_RawList.GetCount(); } + + iterator begin() { return iterator(&m_RawList, m_RawList.Front()); } + iterator end() { return iterator(&m_RawList, VMA_NULL); } + + const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); } + const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); } + + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + + reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); } + reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); } + + const_reverse_iterator crbegin() const { return const_reverse_iterator(&m_RawList, m_RawList.Back()); } + const_reverse_iterator crend() const { return const_reverse_iterator(&m_RawList, VMA_NULL); } + + const_reverse_iterator rbegin() const { return crbegin(); } + const_reverse_iterator rend() const { return crend(); } + + void push_back(const T& value) { m_RawList.PushBack(value); } + iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); } + + void clear() { m_RawList.Clear(); } + void erase(iterator it) { m_RawList.Remove(it.m_pItem); } + +private: + VmaRawList m_RawList; +}; + +#ifndef _VMA_LIST_FUNCTIONS +template +typename VmaList::iterator& VmaList::iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} + +template +typename VmaList::reverse_iterator& VmaList::reverse_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Front(); + } + return *this; +} + +template +typename VmaList::const_iterator& VmaList::const_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} + +template +typename VmaList::const_reverse_iterator& VmaList::const_reverse_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} +#endif // _VMA_LIST_FUNCTIONS +#endif // _VMA_LIST + +#ifndef _VMA_INTRUSIVE_LINKED_LIST +/* +Expected interface of ItemTypeTraits: +struct MyItemTypeTraits +{ + typedef MyItem ItemType; + static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; } + static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; } + static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; } + static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; } +}; +*/ +template +class VmaIntrusiveLinkedList +{ +public: + typedef typename ItemTypeTraits::ItemType ItemType; + static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); } + static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); } + + // Movable, not copyable. + VmaIntrusiveLinkedList() = default; + VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src); + VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete; + VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src); + VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete; + ~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); } + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + ItemType* Front() { return m_Front; } + ItemType* Back() { return m_Back; } + const ItemType* Front() const { return m_Front; } + const ItemType* Back() const { return m_Back; } + + void PushBack(ItemType* item); + void PushFront(ItemType* item); + ItemType* PopBack(); + ItemType* PopFront(); + + // MyItem can be null - it means PushBack. + void InsertBefore(ItemType* existingItem, ItemType* newItem); + // MyItem can be null - it means PushFront. + void InsertAfter(ItemType* existingItem, ItemType* newItem); + void Remove(ItemType* item); + void RemoveAll(); + +private: + ItemType* m_Front = VMA_NULL; + ItemType* m_Back = VMA_NULL; + size_t m_Count = 0; +}; + +#ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS +template +VmaIntrusiveLinkedList::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src) + : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count) +{ + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; +} + +template +VmaIntrusiveLinkedList& VmaIntrusiveLinkedList::operator=(VmaIntrusiveLinkedList&& src) +{ + if (&src != this) + { + VMA_HEAVY_ASSERT(IsEmpty()); + m_Front = src.m_Front; + m_Back = src.m_Back; + m_Count = src.m_Count; + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; + } + return *this; +} + +template +void VmaIntrusiveLinkedList::PushBack(ItemType* item) +{ + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessPrev(item) = m_Back; + ItemTypeTraits::AccessNext(m_Back) = item; + m_Back = item; + ++m_Count; + } +} + +template +void VmaIntrusiveLinkedList::PushFront(ItemType* item) +{ + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessNext(item) = m_Front; + ItemTypeTraits::AccessPrev(m_Front) = item; + m_Front = item; + ++m_Count; + } +} + +template +typename VmaIntrusiveLinkedList::ItemType* VmaIntrusiveLinkedList::PopBack() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const backItem = m_Back; + ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem); + if (prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = VMA_NULL; + } + m_Back = prevItem; + --m_Count; + ItemTypeTraits::AccessPrev(backItem) = VMA_NULL; + ItemTypeTraits::AccessNext(backItem) = VMA_NULL; + return backItem; +} + +template +typename VmaIntrusiveLinkedList::ItemType* VmaIntrusiveLinkedList::PopFront() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const frontItem = m_Front; + ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem); + if (nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL; + } + m_Front = nextItem; + --m_Count; + ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL; + ItemTypeTraits::AccessNext(frontItem) = VMA_NULL; + return frontItem; +} + +template +void VmaIntrusiveLinkedList::InsertBefore(ItemType* existingItem, ItemType* newItem) +{ + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if (existingItem != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem); + ItemTypeTraits::AccessPrev(newItem) = prevItem; + ItemTypeTraits::AccessNext(newItem) = existingItem; + ItemTypeTraits::AccessPrev(existingItem) = newItem; + if (prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Front == existingItem); + m_Front = newItem; + } + ++m_Count; + } + else + PushBack(newItem); +} + +template +void VmaIntrusiveLinkedList::InsertAfter(ItemType* existingItem, ItemType* newItem) +{ + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if (existingItem != VMA_NULL) + { + ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem); + ItemTypeTraits::AccessNext(newItem) = nextItem; + ItemTypeTraits::AccessPrev(newItem) = existingItem; + ItemTypeTraits::AccessNext(existingItem) = newItem; + if (nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Back == existingItem); + m_Back = newItem; + } + ++m_Count; + } + else + return PushFront(newItem); +} + +template +void VmaIntrusiveLinkedList::Remove(ItemType* item) +{ + VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0); + if (ItemTypeTraits::GetPrev(item) != VMA_NULL) + { + ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item); + } + else + { + VMA_HEAVY_ASSERT(m_Front == item); + m_Front = ItemTypeTraits::GetNext(item); + } + + if (ItemTypeTraits::GetNext(item) != VMA_NULL) + { + ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item); + } + else + { + VMA_HEAVY_ASSERT(m_Back == item); + m_Back = ItemTypeTraits::GetPrev(item); + } + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + --m_Count; +} + +template +void VmaIntrusiveLinkedList::RemoveAll() +{ + if (!IsEmpty()) + { + ItemType* item = m_Back; + while (item != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::AccessPrev(item); + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + item = prevItem; + } + m_Front = VMA_NULL; + m_Back = VMA_NULL; + m_Count = 0; + } +} +#endif // _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS +#endif // _VMA_INTRUSIVE_LINKED_LIST + +#if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED +class VmaStringBuilder +{ +public: + VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator(allocationCallbacks)) {} + ~VmaStringBuilder() = default; + + size_t GetLength() const { return m_Data.size(); } + const char* GetData() const { return m_Data.data(); } + void AddNewLine() { Add('\n'); } + void Add(char ch) { m_Data.push_back(ch); } + + void Add(const char* pStr); + void AddNumber(uint32_t num); + void AddNumber(uint64_t num); + void AddPointer(const void* ptr); + +private: + VmaVector> m_Data; +}; + +#ifndef _VMA_STRING_BUILDER_FUNCTIONS +void VmaStringBuilder::Add(const char* pStr) +{ + const size_t strLen = strlen(pStr); + if (strLen > 0) + { + const size_t oldCount = m_Data.size(); + m_Data.resize(oldCount + strLen); + memcpy(m_Data.data() + oldCount, pStr, strLen); + } +} + +void VmaStringBuilder::AddNumber(uint32_t num) +{ + char buf[11]; + buf[10] = '\0'; + char* p = &buf[10]; + do + { + *--p = '0' + (char)(num % 10); + num /= 10; + } while (num); + Add(p); +} + +void VmaStringBuilder::AddNumber(uint64_t num) +{ + char buf[21]; + buf[20] = '\0'; + char* p = &buf[20]; + do + { + *--p = '0' + (char)(num % 10); + num /= 10; + } while (num); + Add(p); +} + +void VmaStringBuilder::AddPointer(const void* ptr) +{ + char buf[21]; + VmaPtrToStr(buf, sizeof(buf), ptr); + Add(buf); +} +#endif //_VMA_STRING_BUILDER_FUNCTIONS +#endif // _VMA_STRING_BUILDER + +#if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED +/* +Allows to conveniently build a correct JSON document to be written to the +VmaStringBuilder passed to the constructor. +*/ +class VmaJsonWriter +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaJsonWriter) +public: + // sb - string builder to write the document to. Must remain alive for the whole lifetime of this object. + VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb); + ~VmaJsonWriter(); + + // Begins object by writing "{". + // Inside an object, you must call pairs of WriteString and a value, e.g.: + // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject(); + // Will write: { "A": 1, "B": 2 } + void BeginObject(bool singleLine = false); + // Ends object by writing "}". + void EndObject(); + + // Begins array by writing "[". + // Inside an array, you can write a sequence of any values. + void BeginArray(bool singleLine = false); + // Ends array by writing "[". + void EndArray(); + + // Writes a string value inside "". + // pStr can contain any ANSI characters, including '"', new line etc. - they will be properly escaped. + void WriteString(const char* pStr); + + // Begins writing a string value. + // Call BeginString, ContinueString, ContinueString, ..., EndString instead of + // WriteString to conveniently build the string content incrementally, made of + // parts including numbers. + void BeginString(const char* pStr = VMA_NULL); + // Posts next part of an open string. + void ContinueString(const char* pStr); + // Posts next part of an open string. The number is converted to decimal characters. + void ContinueString(uint32_t n); + void ContinueString(uint64_t n); + // Posts next part of an open string. Pointer value is converted to characters + // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00 + void ContinueString_Pointer(const void* ptr); + // Ends writing a string value by writing '"'. + void EndString(const char* pStr = VMA_NULL); + + // Writes a number value. + void WriteNumber(uint32_t n); + void WriteNumber(uint64_t n); + // Writes a boolean value - false or true. + void WriteBool(bool b); + // Writes a null value. + void WriteNull(); + +private: + enum COLLECTION_TYPE + { + COLLECTION_TYPE_OBJECT, + COLLECTION_TYPE_ARRAY, + }; + struct StackItem + { + COLLECTION_TYPE type; + uint32_t valueCount; + bool singleLineMode; + }; + + static const char* const INDENT; + + VmaStringBuilder& m_SB; + VmaVector< StackItem, VmaStlAllocator > m_Stack; + bool m_InsideString; + + void BeginValue(bool isString); + void WriteIndent(bool oneLess = false); +}; +const char* const VmaJsonWriter::INDENT = " "; + +#ifndef _VMA_JSON_WRITER_FUNCTIONS +VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) + : m_SB(sb), + m_Stack(VmaStlAllocator(pAllocationCallbacks)), + m_InsideString(false) {} + +VmaJsonWriter::~VmaJsonWriter() +{ + VMA_ASSERT(!m_InsideString); + VMA_ASSERT(m_Stack.empty()); +} + +void VmaJsonWriter::BeginObject(bool singleLine) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add('{'); + + StackItem item; + item.type = COLLECTION_TYPE_OBJECT; + item.valueCount = 0; + item.singleLineMode = singleLine; + m_Stack.push_back(item); +} + +void VmaJsonWriter::EndObject() +{ + VMA_ASSERT(!m_InsideString); + + WriteIndent(true); + m_SB.Add('}'); + + VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT); + m_Stack.pop_back(); +} + +void VmaJsonWriter::BeginArray(bool singleLine) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add('['); + + StackItem item; + item.type = COLLECTION_TYPE_ARRAY; + item.valueCount = 0; + item.singleLineMode = singleLine; + m_Stack.push_back(item); +} + +void VmaJsonWriter::EndArray() +{ + VMA_ASSERT(!m_InsideString); + + WriteIndent(true); + m_SB.Add(']'); + + VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY); + m_Stack.pop_back(); +} + +void VmaJsonWriter::WriteString(const char* pStr) +{ + BeginString(pStr); + EndString(); +} + +void VmaJsonWriter::BeginString(const char* pStr) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(true); + m_SB.Add('"'); + m_InsideString = true; + if (pStr != VMA_NULL && pStr[0] != '\0') + { + ContinueString(pStr); + } +} + +void VmaJsonWriter::ContinueString(const char* pStr) +{ + VMA_ASSERT(m_InsideString); + + const size_t strLen = strlen(pStr); + for (size_t i = 0; i < strLen; ++i) + { + char ch = pStr[i]; + if (ch == '\\') + { + m_SB.Add("\\\\"); + } + else if (ch == '"') + { + m_SB.Add("\\\""); + } + else if ((uint8_t)ch >= 32) + { + m_SB.Add(ch); + } + else switch (ch) + { + case '\b': + m_SB.Add("\\b"); + break; + case '\f': + m_SB.Add("\\f"); + break; + case '\n': + m_SB.Add("\\n"); + break; + case '\r': + m_SB.Add("\\r"); + break; + case '\t': + m_SB.Add("\\t"); + break; + default: + VMA_ASSERT(0 && "Character not currently supported."); + } + } +} + +void VmaJsonWriter::ContinueString(uint32_t n) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::ContinueString(uint64_t n) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::ContinueString_Pointer(const void* ptr) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddPointer(ptr); +} + +void VmaJsonWriter::EndString(const char* pStr) +{ + VMA_ASSERT(m_InsideString); + if (pStr != VMA_NULL && pStr[0] != '\0') + { + ContinueString(pStr); + } + m_SB.Add('"'); + m_InsideString = false; +} + +void VmaJsonWriter::WriteNumber(uint32_t n) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::WriteNumber(uint64_t n) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::WriteBool(bool b) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.Add(b ? "true" : "false"); +} + +void VmaJsonWriter::WriteNull() +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.Add("null"); +} + +void VmaJsonWriter::BeginValue(bool isString) +{ + if (!m_Stack.empty()) + { + StackItem& currItem = m_Stack.back(); + if (currItem.type == COLLECTION_TYPE_OBJECT && + currItem.valueCount % 2 == 0) + { + VMA_ASSERT(isString); + } + + if (currItem.type == COLLECTION_TYPE_OBJECT && + currItem.valueCount % 2 != 0) + { + m_SB.Add(": "); + } + else if (currItem.valueCount > 0) + { + m_SB.Add(", "); + WriteIndent(); + } + else + { + WriteIndent(); + } + ++currItem.valueCount; + } +} + +void VmaJsonWriter::WriteIndent(bool oneLess) +{ + if (!m_Stack.empty() && !m_Stack.back().singleLineMode) + { + m_SB.AddNewLine(); + + size_t count = m_Stack.size(); + if (count > 0 && oneLess) + { + --count; + } + for (size_t i = 0; i < count; ++i) + { + m_SB.Add(INDENT); + } + } +} +#endif // _VMA_JSON_WRITER_FUNCTIONS + +static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedStatistics& stat) +{ + json.BeginObject(); + + json.WriteString("BlockCount"); + json.WriteNumber(stat.statistics.blockCount); + json.WriteString("BlockBytes"); + json.WriteNumber(stat.statistics.blockBytes); + json.WriteString("AllocationCount"); + json.WriteNumber(stat.statistics.allocationCount); + json.WriteString("AllocationBytes"); + json.WriteNumber(stat.statistics.allocationBytes); + json.WriteString("UnusedRangeCount"); + json.WriteNumber(stat.unusedRangeCount); + + if (stat.statistics.allocationCount > 1) + { + json.WriteString("AllocationSizeMin"); + json.WriteNumber(stat.allocationSizeMin); + json.WriteString("AllocationSizeMax"); + json.WriteNumber(stat.allocationSizeMax); + } + if (stat.unusedRangeCount > 1) + { + json.WriteString("UnusedRangeSizeMin"); + json.WriteNumber(stat.unusedRangeSizeMin); + json.WriteString("UnusedRangeSizeMax"); + json.WriteNumber(stat.unusedRangeSizeMax); + } + json.EndObject(); +} +#endif // _VMA_JSON_WRITER + +#ifndef _VMA_MAPPING_HYSTERESIS + +class VmaMappingHysteresis +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaMappingHysteresis) +public: + VmaMappingHysteresis() = default; + + uint32_t GetExtraMapping() const { return m_ExtraMapping; } + + // Call when Map was called. + // Returns true if switched to extra +1 mapping reference count. + bool PostMap() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 0) + { + ++m_MajorCounter; + if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING) + { + m_ExtraMapping = 1; + m_MajorCounter = 0; + m_MinorCounter = 0; + return true; + } + } + else // m_ExtraMapping == 1 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + return false; + } + + // Call when Unmap was called. + void PostUnmap() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 0) + ++m_MajorCounter; + else // m_ExtraMapping == 1 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + } + + // Call when allocation was made from the memory block. + void PostAlloc() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 1) + ++m_MajorCounter; + else // m_ExtraMapping == 0 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + } + + // Call when allocation was freed from the memory block. + // Returns true if switched to extra -1 mapping reference count. + bool PostFree() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 1) + { + ++m_MajorCounter; + if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING && + m_MajorCounter > m_MinorCounter + 1) + { + m_ExtraMapping = 0; + m_MajorCounter = 0; + m_MinorCounter = 0; + return true; + } + } + else // m_ExtraMapping == 0 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + return false; + } + +private: + static const int32_t COUNTER_MIN_EXTRA_MAPPING = 7; + + uint32_t m_MinorCounter = 0; + uint32_t m_MajorCounter = 0; + uint32_t m_ExtraMapping = 0; // 0 or 1. + + void PostMinorCounter() + { + if(m_MinorCounter < m_MajorCounter) + { + ++m_MinorCounter; + } + else if(m_MajorCounter > 0) + { + --m_MajorCounter; + --m_MinorCounter; + } + } +}; + +#endif // _VMA_MAPPING_HYSTERESIS + +#ifndef _VMA_DEVICE_MEMORY_BLOCK +/* +Represents a single block of device memory (`VkDeviceMemory`) with all the +data about its regions (aka suballocations, #VmaAllocation), assigned and free. + +Thread-safety: +- Access to m_pMetadata must be externally synchronized. +- Map, Unmap, Bind* are synchronized internally. +*/ +class VmaDeviceMemoryBlock +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaDeviceMemoryBlock) +public: + VmaBlockMetadata* m_pMetadata; + + VmaDeviceMemoryBlock(VmaAllocator hAllocator); + ~VmaDeviceMemoryBlock(); + + // Always call after construction. + void Init( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm, + VkDeviceSize bufferImageGranularity); + // Always call before destruction. + void Destroy(VmaAllocator allocator); + + VmaPool GetParentPool() const { return m_hParentPool; } + VkDeviceMemory GetDeviceMemory() const { return m_hMemory; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + uint32_t GetId() const { return m_Id; } + void* GetMappedData() const { return m_pMappedData; } + uint32_t GetMapRefCount() const { return m_MapCount; } + + // Call when allocation/free was made from m_pMetadata. + // Used for m_MappingHysteresis. + void PostAlloc(VmaAllocator hAllocator); + void PostFree(VmaAllocator hAllocator); + + // Validates all data structures inside this object. If not valid, returns false. + bool Validate() const; + VkResult CheckCorruption(VmaAllocator hAllocator); + + // ppData can be null. + VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData); + void Unmap(VmaAllocator hAllocator, uint32_t count); + + VkResult WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + VkResult ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + + VkResult BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext); + VkResult BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext); + +private: + VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. + uint32_t m_MemoryTypeIndex; + uint32_t m_Id; + VkDeviceMemory m_hMemory; + + /* + Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory. + Also protects m_MapCount, m_pMappedData. + Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex. + */ + VMA_MUTEX m_MapAndBindMutex; + VmaMappingHysteresis m_MappingHysteresis; + uint32_t m_MapCount; + void* m_pMappedData; +}; +#endif // _VMA_DEVICE_MEMORY_BLOCK + +#ifndef _VMA_ALLOCATION_T +struct VmaAllocation_T +{ + friend struct VmaDedicatedAllocationListItemTraits; + + enum FLAGS + { + FLAG_PERSISTENT_MAP = 0x01, + FLAG_MAPPING_ALLOWED = 0x02, + }; + +public: + enum ALLOCATION_TYPE + { + ALLOCATION_TYPE_NONE, + ALLOCATION_TYPE_BLOCK, + ALLOCATION_TYPE_DEDICATED, + }; + + // This struct is allocated using VmaPoolAllocator. + VmaAllocation_T(bool mappingAllowed); + ~VmaAllocation_T(); + + void InitBlockAllocation( + VmaDeviceMemoryBlock* block, + VmaAllocHandle allocHandle, + VkDeviceSize alignment, + VkDeviceSize size, + uint32_t memoryTypeIndex, + VmaSuballocationType suballocationType, + bool mapped); + // pMappedData not null means allocation is created with MAPPED flag. + void InitDedicatedAllocation( + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + void* pMappedData, + VkDeviceSize size); + + ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } + VkDeviceSize GetAlignment() const { return m_Alignment; } + VkDeviceSize GetSize() const { return m_Size; } + void* GetUserData() const { return m_pUserData; } + const char* GetName() const { return m_pName; } + VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; } + + VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + bool IsPersistentMap() const { return (m_Flags & FLAG_PERSISTENT_MAP) != 0; } + bool IsMappingAllowed() const { return (m_Flags & FLAG_MAPPING_ALLOWED) != 0; } + + void SetUserData(VmaAllocator hAllocator, void* pUserData) { m_pUserData = pUserData; } + void SetName(VmaAllocator hAllocator, const char* pName); + void FreeName(VmaAllocator hAllocator); + uint8_t SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation); + VmaAllocHandle GetAllocHandle() const; + VkDeviceSize GetOffset() const; + VmaPool GetParentPool() const; + VkDeviceMemory GetMemory() const; + void* GetMappedData() const; + + void BlockAllocMap(); + void BlockAllocUnmap(); + VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData); + void DedicatedAllocUnmap(VmaAllocator hAllocator); + +#if VMA_STATS_STRING_ENABLED + VmaBufferImageUsage GetBufferImageUsage() const { return m_BufferImageUsage; } + void InitBufferUsage(const VkBufferCreateInfo &createInfo, bool useKhrMaintenance5) + { + VMA_ASSERT(m_BufferImageUsage == VmaBufferImageUsage::UNKNOWN); + m_BufferImageUsage = VmaBufferImageUsage(createInfo, useKhrMaintenance5); + } + void InitImageUsage(const VkImageCreateInfo &createInfo) + { + VMA_ASSERT(m_BufferImageUsage == VmaBufferImageUsage::UNKNOWN); + m_BufferImageUsage = VmaBufferImageUsage(createInfo); + } + void PrintParameters(class VmaJsonWriter& json) const; +#endif + +private: + // Allocation out of VmaDeviceMemoryBlock. + struct BlockAllocation + { + VmaDeviceMemoryBlock* m_Block; + VmaAllocHandle m_AllocHandle; + }; + // Allocation for an object that has its own private VkDeviceMemory. + struct DedicatedAllocation + { + VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. + VkDeviceMemory m_hMemory; + void* m_pMappedData; // Not null means memory is mapped. + VmaAllocation_T* m_Prev; + VmaAllocation_T* m_Next; + }; + union + { + // Allocation out of VmaDeviceMemoryBlock. + BlockAllocation m_BlockAllocation; + // Allocation for an object that has its own private VkDeviceMemory. + DedicatedAllocation m_DedicatedAllocation; + }; + + VkDeviceSize m_Alignment; + VkDeviceSize m_Size; + void* m_pUserData; + char* m_pName; + uint32_t m_MemoryTypeIndex; + uint8_t m_Type; // ALLOCATION_TYPE + uint8_t m_SuballocationType; // VmaSuballocationType + // Reference counter for vmaMapMemory()/vmaUnmapMemory(). + uint8_t m_MapCount; + uint8_t m_Flags; // enum FLAGS +#if VMA_STATS_STRING_ENABLED + VmaBufferImageUsage m_BufferImageUsage; // 0 if unknown. +#endif +}; +#endif // _VMA_ALLOCATION_T + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS +struct VmaDedicatedAllocationListItemTraits +{ + typedef VmaAllocation_T ItemType; + + static ItemType* GetPrev(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType* GetNext(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } + static ItemType*& AccessPrev(ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType*& AccessNext(ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } +}; +#endif // _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST +/* +Stores linked list of VmaAllocation_T objects. +Thread-safe, synchronized internally. +*/ +class VmaDedicatedAllocationList +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaDedicatedAllocationList) +public: + VmaDedicatedAllocationList() {} + ~VmaDedicatedAllocationList(); + + void Init(bool useMutex) { m_UseMutex = useMutex; } + bool Validate(); + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats); + void AddStatistics(VmaStatistics& inoutStats); +#if VMA_STATS_STRING_ENABLED + // Writes JSON array with the list of allocations. + void BuildStatsString(VmaJsonWriter& json); +#endif + + bool IsEmpty(); + void Register(VmaAllocation alloc); + void Unregister(VmaAllocation alloc); + +private: + typedef VmaIntrusiveLinkedList DedicatedAllocationLinkedList; + + bool m_UseMutex = true; + VMA_RW_MUTEX m_Mutex; + DedicatedAllocationLinkedList m_AllocationList; +}; + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS + +VmaDedicatedAllocationList::~VmaDedicatedAllocationList() +{ + VMA_HEAVY_ASSERT(Validate()); + + if (!m_AllocationList.IsEmpty()) + { + VMA_ASSERT_LEAK(false && "Unfreed dedicated allocations found!"); + } +} + +bool VmaDedicatedAllocationList::Validate() +{ + const size_t declaredCount = m_AllocationList.GetCount(); + size_t actualCount = 0; + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + for (VmaAllocation alloc = m_AllocationList.Front(); + alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) + { + ++actualCount; + } + VMA_VALIDATE(actualCount == declaredCount); + + return true; +} + +void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) +{ + for(auto* item = m_AllocationList.Front(); item != VMA_NULL; item = DedicatedAllocationLinkedList::GetNext(item)) + { + const VkDeviceSize size = item->GetSize(); + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += size; + VmaAddDetailedStatisticsAllocation(inoutStats, item->GetSize()); + } +} + +void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats) +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + + const uint32_t allocCount = (uint32_t)m_AllocationList.GetCount(); + inoutStats.blockCount += allocCount; + inoutStats.allocationCount += allocCount; + + for(auto* item = m_AllocationList.Front(); item != VMA_NULL; item = DedicatedAllocationLinkedList::GetNext(item)) + { + const VkDeviceSize size = item->GetSize(); + inoutStats.blockBytes += size; + inoutStats.allocationBytes += size; + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json) +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + json.BeginArray(); + for (VmaAllocation alloc = m_AllocationList.Front(); + alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) + { + json.BeginObject(true); + alloc->PrintParameters(json); + json.EndObject(); + } + json.EndArray(); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaDedicatedAllocationList::IsEmpty() +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + return m_AllocationList.IsEmpty(); +} + +void VmaDedicatedAllocationList::Register(VmaAllocation alloc) +{ + VmaMutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.PushBack(alloc); +} + +void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc) +{ + VmaMutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.Remove(alloc); +} +#endif // _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS +#endif // _VMA_DEDICATED_ALLOCATION_LIST + +#ifndef _VMA_SUBALLOCATION +/* +Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as +allocated memory block or free. +*/ +struct VmaSuballocation +{ + VkDeviceSize offset; + VkDeviceSize size; + void* userData; + VmaSuballocationType type; +}; + +// Comparator for offsets. +struct VmaSuballocationOffsetLess +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset < rhs.offset; + } +}; + +struct VmaSuballocationOffsetGreater +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset > rhs.offset; + } +}; + +struct VmaSuballocationItemSizeLess +{ + bool operator()(const VmaSuballocationList::iterator lhs, + const VmaSuballocationList::iterator rhs) const + { + return lhs->size < rhs->size; + } + + bool operator()(const VmaSuballocationList::iterator lhs, + VkDeviceSize rhsSize) const + { + return lhs->size < rhsSize; + } +}; +#endif // _VMA_SUBALLOCATION + +#ifndef _VMA_ALLOCATION_REQUEST +/* +Parameters of planned allocation inside a VmaDeviceMemoryBlock. +item points to a FREE suballocation. +*/ +struct VmaAllocationRequest +{ + VmaAllocHandle allocHandle; + VkDeviceSize size; + VmaSuballocationList::iterator item; + void* customData; + uint64_t algorithmData; + VmaAllocationRequestType type; +}; +#endif // _VMA_ALLOCATION_REQUEST + +#ifndef _VMA_BLOCK_METADATA +/* +Data structure used for bookkeeping of allocations and unused ranges of memory +in a single VkDeviceMemory block. +*/ +class VmaBlockMetadata +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata) +public: + // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object. + VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata() = default; + + virtual void Init(VkDeviceSize size) { m_Size = size; } + bool IsVirtual() const { return m_IsVirtual; } + VkDeviceSize GetSize() const { return m_Size; } + + // Validates all data structures inside this object. If not valid, returns false. + virtual bool Validate() const = 0; + virtual size_t GetAllocationCount() const = 0; + virtual size_t GetFreeRegionsCount() const = 0; + virtual VkDeviceSize GetSumFreeSize() const = 0; + // Returns true if this block is empty - contains only single free suballocation. + virtual bool IsEmpty() const = 0; + virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0; + virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0; + virtual void* GetAllocationUserData(VmaAllocHandle allocHandle) const = 0; + + virtual VmaAllocHandle GetAllocationListBegin() const = 0; + virtual VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const = 0; + virtual VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const = 0; + + // Shouldn't modify blockCount. + virtual void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const = 0; + virtual void AddStatistics(VmaStatistics& inoutStats) const = 0; + +#if VMA_STATS_STRING_ENABLED + virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0; +#endif + + // Tries to find a place for suballocation with given parameters inside this block. + // If succeeded, fills pAllocationRequest and returns true. + // If failed, returns false. + virtual bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags. + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) = 0; + + virtual VkResult CheckCorruption(const void* pBlockData) = 0; + + // Makes actual allocation based on request. Request must already be checked and valid. + virtual void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) = 0; + + // Frees suballocation assigned to given memory region. + virtual void Free(VmaAllocHandle allocHandle) = 0; + + // Frees all allocations. + // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations! + virtual void Clear() = 0; + + virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0; + virtual void DebugLogAllAllocations() const = 0; + +protected: + const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; } + VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } + VkDeviceSize GetDebugMargin() const { return VkDeviceSize(IsVirtual() ? 0 : VMA_DEBUG_MARGIN); } + + void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const; +#if VMA_STATS_STRING_ENABLED + // mapRefCount == UINT32_MAX means unspecified. + void PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, + size_t allocationCount, + size_t unusedRangeCount) const; + void PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size, void* userData) const; + void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, + VkDeviceSize size) const; + void PrintDetailedMap_End(class VmaJsonWriter& json) const; +#endif + +private: + VkDeviceSize m_Size; + const VkAllocationCallbacks* m_pAllocationCallbacks; + const VkDeviceSize m_BufferImageGranularity; + const bool m_IsVirtual; +}; + +#ifndef _VMA_BLOCK_METADATA_FUNCTIONS +VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : m_Size(0), + m_pAllocationCallbacks(pAllocationCallbacks), + m_BufferImageGranularity(bufferImageGranularity), + m_IsVirtual(isVirtual) {} + +void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const +{ + if (IsVirtual()) + { + VMA_LEAK_LOG_FORMAT("UNFREED VIRTUAL ALLOCATION; Offset: %" PRIu64 "; Size: %" PRIu64 "; UserData: %p", offset, size, userData); + } + else + { + VMA_ASSERT(userData != VMA_NULL); + VmaAllocation allocation = reinterpret_cast(userData); + + userData = allocation->GetUserData(); + const char* name = allocation->GetName(); + +#if VMA_STATS_STRING_ENABLED + VMA_LEAK_LOG_FORMAT("UNFREED ALLOCATION; Offset: %" PRIu64 "; Size: %" PRIu64 "; UserData: %p; Name: %s; Type: %s; Usage: %" PRIu64, + offset, size, userData, name ? name : "vma_empty", + VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()], + (uint64_t)allocation->GetBufferImageUsage().Value); +#else + VMA_LEAK_LOG_FORMAT("UNFREED ALLOCATION; Offset: %" PRIu64 "; Size: %" PRIu64 "; UserData: %p; Name: %s; Type: %u", + offset, size, userData, name ? name : "vma_empty", + (unsigned)allocation->GetSuballocationType()); +#endif // VMA_STATS_STRING_ENABLED + } + +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const +{ + json.WriteString("TotalBytes"); + json.WriteNumber(GetSize()); + + json.WriteString("UnusedBytes"); + json.WriteNumber(unusedBytes); + + json.WriteString("Allocations"); + json.WriteNumber((uint64_t)allocationCount); + + json.WriteString("UnusedRanges"); + json.WriteNumber((uint64_t)unusedRangeCount); + + json.WriteString("Suballocations"); + json.BeginArray(); +} + +void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size, void* userData) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + if (IsVirtual()) + { + json.WriteString("Size"); + json.WriteNumber(size); + if (userData) + { + json.WriteString("CustomData"); + json.BeginString(); + json.ContinueString_Pointer(userData); + json.EndString(); + } + } + else + { + ((VmaAllocation)userData)->PrintParameters(json); + } + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]); + + json.WriteString("Size"); + json.WriteNumber(size); + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const +{ + json.EndArray(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_BLOCK_METADATA_FUNCTIONS +#endif // _VMA_BLOCK_METADATA + +#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY +// Before deleting object of this class remember to call 'Destroy()' +class VmaBlockBufferImageGranularity final +{ +public: + struct ValidationContext + { + const VkAllocationCallbacks* allocCallbacks; + uint16_t* pageAllocs; + }; + + VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity); + ~VmaBlockBufferImageGranularity(); + + bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_BUFFER_IMAGE_GRANULARITY; } + + void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size); + // Before destroying object you must call free it's memory + void Destroy(const VkAllocationCallbacks* pAllocationCallbacks); + + void RoundupAllocRequest(VmaSuballocationType allocType, + VkDeviceSize& inOutAllocSize, + VkDeviceSize& inOutAllocAlignment) const; + + bool CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset, + VkDeviceSize allocSize, + VkDeviceSize blockOffset, + VkDeviceSize blockSize, + VmaSuballocationType allocType) const; + + void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size); + void FreePages(VkDeviceSize offset, VkDeviceSize size); + void Clear(); + + ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks, + bool isVirutal) const; + bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const; + bool FinishValidation(ValidationContext& ctx) const; + +private: + static const uint16_t MAX_LOW_BUFFER_IMAGE_GRANULARITY = 256; + + struct RegionInfo + { + uint8_t allocType; + uint16_t allocCount; + }; + + VkDeviceSize m_BufferImageGranularity; + uint32_t m_RegionCount; + RegionInfo* m_RegionInfo; + + uint32_t GetStartPage(VkDeviceSize offset) const { return OffsetToPageIndex(offset & ~(m_BufferImageGranularity - 1)); } + uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return OffsetToPageIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); } + + uint32_t OffsetToPageIndex(VkDeviceSize offset) const; + void AllocPage(RegionInfo& page, uint8_t allocType); +}; + +#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS +VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity) + : m_BufferImageGranularity(bufferImageGranularity), + m_RegionCount(0), + m_RegionInfo(VMA_NULL) {} + +VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity() +{ + VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!"); +} + +void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size) +{ + if (IsEnabled()) + { + m_RegionCount = static_cast(VmaDivideRoundingUp(size, m_BufferImageGranularity)); + m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount); + memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo)); + } +} + +void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks) +{ + if (m_RegionInfo) + { + vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount); + m_RegionInfo = VMA_NULL; + } +} + +void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType, + VkDeviceSize& inOutAllocSize, + VkDeviceSize& inOutAllocAlignment) const +{ + if (m_BufferImageGranularity > 1 && + m_BufferImageGranularity <= MAX_LOW_BUFFER_IMAGE_GRANULARITY) + { + if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL) + { + inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity); + inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity); + } + } +} + +bool VmaBlockBufferImageGranularity::CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset, + VkDeviceSize allocSize, + VkDeviceSize blockOffset, + VkDeviceSize blockSize, + VmaSuballocationType allocType) const +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(inOutAllocOffset); + if (m_RegionInfo[startPage].allocCount > 0 && + VmaIsBufferImageGranularityConflict(static_cast(m_RegionInfo[startPage].allocType), allocType)) + { + inOutAllocOffset = VmaAlignUp(inOutAllocOffset, m_BufferImageGranularity); + if (blockSize < allocSize + inOutAllocOffset - blockOffset) + return true; + ++startPage; + } + uint32_t endPage = GetEndPage(inOutAllocOffset, allocSize); + if (endPage != startPage && + m_RegionInfo[endPage].allocCount > 0 && + VmaIsBufferImageGranularityConflict(static_cast(m_RegionInfo[endPage].allocType), allocType)) + { + return true; + } + } + return false; +} + +void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size) +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(offset); + AllocPage(m_RegionInfo[startPage], allocType); + + uint32_t endPage = GetEndPage(offset, size); + if (startPage != endPage) + AllocPage(m_RegionInfo[endPage], allocType); + } +} + +void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size) +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(offset); + --m_RegionInfo[startPage].allocCount; + if (m_RegionInfo[startPage].allocCount == 0) + m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE; + uint32_t endPage = GetEndPage(offset, size); + if (startPage != endPage) + { + --m_RegionInfo[endPage].allocCount; + if (m_RegionInfo[endPage].allocCount == 0) + m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE; + } + } +} + +void VmaBlockBufferImageGranularity::Clear() +{ + if (m_RegionInfo) + memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo)); +} + +VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation( + const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const +{ + ValidationContext ctx{ pAllocationCallbacks, VMA_NULL }; + if (!isVirutal && IsEnabled()) + { + ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount); + memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t)); + } + return ctx; +} + +bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx, + VkDeviceSize offset, VkDeviceSize size) const +{ + if (IsEnabled()) + { + uint32_t start = GetStartPage(offset); + ++ctx.pageAllocs[start]; + VMA_VALIDATE(m_RegionInfo[start].allocCount > 0); + + uint32_t end = GetEndPage(offset, size); + if (start != end) + { + ++ctx.pageAllocs[end]; + VMA_VALIDATE(m_RegionInfo[end].allocCount > 0); + } + } + return true; +} + +bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const +{ + // Check proper page structure + if (IsEnabled()) + { + VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!"); + + for (uint32_t page = 0; page < m_RegionCount; ++page) + { + VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount); + } + vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount); + ctx.pageAllocs = VMA_NULL; + } + return true; +} + +uint32_t VmaBlockBufferImageGranularity::OffsetToPageIndex(VkDeviceSize offset) const +{ + return static_cast(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity)); +} + +void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType) +{ + // When current alloc type is free then it can be overridden by new type + if (page.allocCount == 0 || (page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE)) + page.allocType = allocType; + + ++page.allocCount; +} +#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS +#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY + +#ifndef _VMA_BLOCK_METADATA_LINEAR +/* +Allocations and their references in internal data structure look like this: + +if(m_2ndVectorMode == SECOND_VECTOR_EMPTY): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER): + + 0 +-------+ + | Alloc | 2nd[0] + +-------+ + | Alloc | 2nd[1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[1] + +-------+ + | Alloc | 2nd[0] +GetSize() +-------+ + +*/ +class VmaBlockMetadata_Linear : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_Linear) +public: + VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Linear() = default; + + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; } + bool IsEmpty() const override { return GetAllocationCount() == 0; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; } + + void Init(VkDeviceSize size) override; + bool Validate() const override; + size_t GetAllocationCount() const override; + size_t GetFreeRegionsCount() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + void DebugLogAllAllocations() const override; + +private: + /* + There are two suballocation vectors, used in ping-pong way. + The one with index m_1stVectorIndex is called 1st. + The one with index (m_1stVectorIndex ^ 1) is called 2nd. + 2nd can be non-empty only when 1st is not empty. + When 2nd is not empty, m_2ndVectorMode indicates its mode of operation. + */ + typedef VmaVector> SuballocationVectorType; + + enum SECOND_VECTOR_MODE + { + SECOND_VECTOR_EMPTY, + /* + Suballocations in 2nd vector are created later than the ones in 1st, but they + all have smaller offset. + */ + SECOND_VECTOR_RING_BUFFER, + /* + Suballocations in 2nd vector are upper side of double stack. + They all have offsets higher than those in 1st vector. + Top of this stack means smaller offsets, but higher indices in this vector. + */ + SECOND_VECTOR_DOUBLE_STACK, + }; + + VkDeviceSize m_SumFreeSize; + SuballocationVectorType m_Suballocations0, m_Suballocations1; + uint32_t m_1stVectorIndex; + SECOND_VECTOR_MODE m_2ndVectorMode; + // Number of items in 1st vector with hAllocation = null at the beginning. + size_t m_1stNullItemsBeginCount; + // Number of other items in 1st vector with hAllocation = null somewhere in the middle. + size_t m_1stNullItemsMiddleCount; + // Number of items in 2nd vector with hAllocation = null. + size_t m_2ndNullItemsCount; + + SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + + VmaSuballocation& FindSuballocation(VkDeviceSize offset) const; + bool ShouldCompact1st() const; + void CleanupAfterFree(); + + bool CreateAllocationRequest_LowerAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); + bool CreateAllocationRequest_UpperAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); +}; + +#ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS +VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_SumFreeSize(0), + m_Suballocations0(VmaStlAllocator(pAllocationCallbacks)), + m_Suballocations1(VmaStlAllocator(pAllocationCallbacks)), + m_1stVectorIndex(0), + m_2ndVectorMode(SECOND_VECTOR_EMPTY), + m_1stNullItemsBeginCount(0), + m_1stNullItemsMiddleCount(0), + m_2ndNullItemsCount(0) {} + +void VmaBlockMetadata_Linear::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + m_SumFreeSize = size; +} + +bool VmaBlockMetadata_Linear::Validate() const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY)); + VMA_VALIDATE(!suballocations1st.empty() || + suballocations2nd.empty() || + m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER); + + if (!suballocations1st.empty()) + { + // Null item at the beginning should be accounted into m_1stNullItemsBeginCount. + VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE); + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE); + } + if (!suballocations2nd.empty()) + { + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE); + } + + VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size()); + VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size()); + + VkDeviceSize sumUsedSize = 0; + const size_t suballoc1stCount = suballocations1st.size(); + const VkDeviceSize debugMargin = GetDebugMargin(); + VkDeviceSize offset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = 0; i < suballoc2ndCount; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE && + suballoc.userData == VMA_NULL); + } + + size_t nullItem1stCount = m_1stNullItemsBeginCount; + + for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem1stCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount); + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = suballoc2ndCount; i--; ) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + VMA_VALIDATE(offset <= GetSize()); + VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize); + + return true; +} + +size_t VmaBlockMetadata_Linear::GetAllocationCount() const +{ + return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount + + AccessSuballocations2nd().size() - m_2ndNullItemsCount; +} + +size_t VmaBlockMetadata_Linear::GetFreeRegionsCount() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return SIZE_MAX; +} + +void VmaBlockMetadata_Linear::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += size; + + VkDeviceSize lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + if (lastOffset < freeSpace2ndTo1stEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + if (lastOffset < freeSpace1stTo2ndEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to size. + if (lastOffset < size) + { + const VkDeviceSize unusedRangeSize = size - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } +} + +void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const VkDeviceSize size = GetSize(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + inoutStats.blockCount++; + inoutStats.blockBytes += size; + inoutStats.allocationBytes += size - m_SumFreeSize; + + VkDeviceSize lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + // End of loop. + lastOffset = size; + } + } + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + // FIRST PASS + + size_t unusedRangeCount = 0; + VkDeviceSize usedBytes = 0; + + VkDeviceSize lastOffset = 0; + + size_t alloc2ndCount = 0; + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + size_t alloc1stCount = 0; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc1stCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = size; + } + } + } + + const VkDeviceSize unusedBytes = size - usedBytes; + PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount); + + // SECOND PASS + lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + nextAlloc1stIndex = m_1stNullItemsBeginCount; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + const VkDeviceSize unusedRangeSize = size - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Linear::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); + + if(allocSize > GetSize()) + return false; + + pAllocationRequest->size = allocSize; + return upperAddress ? + CreateAllocationRequest_UpperAddress( + allocSize, allocAlignment, allocType, strategy, pAllocationRequest) : + CreateAllocationRequest_LowerAddress( + allocSize, allocAlignment, allocType, strategy, pAllocationRequest); +} + +VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData) +{ + VMA_ASSERT(!IsVirtual()); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_Linear::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1; + const VmaSuballocation newSuballoc = { offset, request.size, userData, type }; + + switch (request.type) + { + case VmaAllocationRequestType::UpperAddress: + { + VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER && + "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer."); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + suballocations2nd.push_back(newSuballoc); + m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK; + } + break; + case VmaAllocationRequestType::EndOf1st: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + + VMA_ASSERT(suballocations1st.empty() || + offset >= suballocations1st.back().offset + suballocations1st.back().size); + // Check if it fits before the end of the block. + VMA_ASSERT(offset + request.size <= GetSize()); + + suballocations1st.push_back(newSuballoc); + } + break; + case VmaAllocationRequestType::EndOf2nd: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector. + VMA_ASSERT(!suballocations1st.empty() && + offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + switch (m_2ndVectorMode) + { + case SECOND_VECTOR_EMPTY: + // First allocation from second part ring buffer. + VMA_ASSERT(suballocations2nd.empty()); + m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER; + break; + case SECOND_VECTOR_RING_BUFFER: + // 2-part ring buffer is already started. + VMA_ASSERT(!suballocations2nd.empty()); + break; + case SECOND_VECTOR_DOUBLE_STACK: + VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack."); + break; + default: + VMA_ASSERT(0); + } + + suballocations2nd.push_back(newSuballoc); + } + break; + default: + VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR."); + } + + m_SumFreeSize -= newSuballoc.size; +} + +void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle) +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + VkDeviceSize offset = (VkDeviceSize)allocHandle - 1; + + if (!suballocations1st.empty()) + { + // First allocation: Mark it as next empty at the beginning. + VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount]; + if (firstSuballoc.offset == offset) + { + firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + firstSuballoc.userData = VMA_NULL; + m_SumFreeSize += firstSuballoc.size; + ++m_1stNullItemsBeginCount; + CleanupAfterFree(); + return; + } + } + + // Last allocation in 2-part ring buffer or top of upper stack (same logic). + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER || + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + VmaSuballocation& lastSuballoc = suballocations2nd.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations2nd.pop_back(); + CleanupAfterFree(); + return; + } + } + // Last allocation in 1st vector. + else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY) + { + VmaSuballocation& lastSuballoc = suballocations1st.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations1st.pop_back(); + CleanupAfterFree(); + return; + } + } + + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the middle of 1st vector. + { + const SuballocationVectorType::iterator it = VmaBinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc, + VmaSuballocationOffsetLess()); + if (it != suballocations1st.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->userData = VMA_NULL; + ++m_1stNullItemsMiddleCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Item from the middle of 2nd vector. + const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) : + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater()); + if (it != suballocations2nd.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->userData = VMA_NULL; + ++m_2ndNullItemsCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + VMA_ASSERT(0 && "Allocation to free not found in linear allocator!"); +} + +void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + outInfo.offset = (VkDeviceSize)allocHandle - 1; + VmaSuballocation& suballoc = FindSuballocation(outInfo.offset); + outInfo.size = suballoc.size; + outInfo.pUserData = suballoc.userData; +} + +void* VmaBlockMetadata_Linear::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + return FindSuballocation((VkDeviceSize)allocHandle - 1).userData; +} + +VmaAllocHandle VmaBlockMetadata_Linear::GetAllocationListBegin() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_Linear::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return VK_NULL_HANDLE; +} + +VkDeviceSize VmaBlockMetadata_Linear::GetNextFreeRegionSize(VmaAllocHandle alloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return 0; +} + +void VmaBlockMetadata_Linear::Clear() +{ + m_SumFreeSize = GetSize(); + m_Suballocations0.clear(); + m_Suballocations1.clear(); + // Leaving m_1stVectorIndex unchanged - it doesn't matter. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; +} + +void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle - 1); + suballoc.userData = userData; +} + +void VmaBlockMetadata_Linear::DebugLogAllAllocations() const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it) + if (it->type != VMA_SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(it->offset, it->size, it->userData); + + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it) + if (it->type != VMA_SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(it->offset, it->size, it->userData); +} + +VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the 1st vector. + { + SuballocationVectorType::const_iterator it = VmaBinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc, + VmaSuballocationOffsetLess()); + if (it != suballocations1st.end()) + { + return const_cast(*it); + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Rest of members stays uninitialized intentionally for better performance. + SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) : + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater()); + if (it != suballocations2nd.end()) + { + return const_cast(*it); + } + } + + VMA_ASSERT(0 && "Allocation not found in linear allocator!"); + return const_cast(suballocations1st.back()); // Should never occur. +} + +bool VmaBlockMetadata_Linear::ShouldCompact1st() const +{ + const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + const size_t suballocCount = AccessSuballocations1st().size(); + return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3; +} + +void VmaBlockMetadata_Linear::CleanupAfterFree() +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (IsEmpty()) + { + suballocations1st.clear(); + suballocations2nd.clear(); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + else + { + const size_t suballoc1stCount = suballocations1st.size(); + const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + VMA_ASSERT(nullItem1stCount <= suballoc1stCount); + + // Find more null items at the beginning of 1st vector. + while (m_1stNullItemsBeginCount < suballoc1stCount && + suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + + // Find more null items at the end of 1st vector. + while (m_1stNullItemsMiddleCount > 0 && + suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_1stNullItemsMiddleCount; + suballocations1st.pop_back(); + } + + // Find more null items at the end of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + suballocations2nd.pop_back(); + } + + // Find more null items at the beginning of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + VmaVectorRemove(suballocations2nd, 0); + } + + if (ShouldCompact1st()) + { + const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount; + size_t srcIndex = m_1stNullItemsBeginCount; + for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex) + { + while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++srcIndex; + } + if (dstIndex != srcIndex) + { + suballocations1st[dstIndex] = suballocations1st[srcIndex]; + } + ++srcIndex; + } + suballocations1st.resize(nonNullItemCount); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + } + + // 2nd vector became empty. + if (suballocations2nd.empty()) + { + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + + // 1st vector became empty. + if (suballocations1st.size() - m_1stNullItemsBeginCount == 0) + { + suballocations1st.clear(); + m_1stNullItemsBeginCount = 0; + + if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + // Swap 1st with 2nd. Now 2nd is empty. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsMiddleCount = m_2ndNullItemsCount; + while (m_1stNullItemsBeginCount < suballocations2nd.size() && + suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + m_2ndNullItemsCount = 0; + m_1stVectorIndex ^= 1; + } + } + } + + VMA_HEAVY_ASSERT(Validate()); +} + +bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + const VkDeviceSize blockSize = GetSize(); + const VkDeviceSize debugMargin = GetDebugMargin(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + // Try to allocate at the end of 1st vector. + + VkDeviceSize resultBaseOffset = 0; + if (!suballocations1st.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations1st.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? + suballocations2nd.back().offset : blockSize; + + // There is enough free space at the end after alignment. + if (resultOffset + allocSize + debugMargin <= freeSpaceEnd) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on previous page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1); + // pAllocationRequest->item, customData unused. + pAllocationRequest->type = VmaAllocationRequestType::EndOf1st; + return true; + } + } + + // Wrap-around to end of 2nd vector. Try to allocate there, watching for the + // beginning of 1st vector as the end of free space. + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(!suballocations1st.empty()); + + VkDeviceSize resultBaseOffset = 0; + if (!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + size_t index1st = m_1stNullItemsBeginCount; + + // There is enough free space at the end after alignment. + if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) || + (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset)) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) + { + for (size_t nextSuballocIndex = index1st; + nextSuballocIndex < suballocations1st.size(); + nextSuballocIndex++) + { + const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1); + pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd; + // pAllocationRequest->item, customData unused. + return true; + } + } + + return false; +} + +bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + const VkDeviceSize blockSize = GetSize(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer."); + return false; + } + + // Try to allocate before 2nd.back(), or end of block if 2nd.empty(). + if (allocSize > blockSize) + { + return false; + } + VkDeviceSize resultBaseOffset = blockSize - allocSize; + if (!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset - allocSize; + if (allocSize > lastSuballoc.offset) + { + return false; + } + } + + // Start from offset equal to end of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + // Apply debugMargin at the end. + if (debugMargin > 0) + { + if (resultOffset < debugMargin) + { + return false; + } + resultOffset -= debugMargin; + } + + // Apply alignment. + resultOffset = VmaAlignDown(resultOffset, allocAlignment); + + // Check next suballocations from 2nd for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity); + } + } + + // There is enough free space. + const VkDeviceSize endOf1st = !suballocations1st.empty() ? + suballocations1st.back().offset + suballocations1st.back().size : + 0; + if (endOf1st + debugMargin <= resultOffset) + { + // Check previous suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (bufferImageGranularity > 1) + { + for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1); + // pAllocationRequest->item unused. + pAllocationRequest->type = VmaAllocationRequestType::UpperAddress; + return true; + } + + return false; +} +#endif // _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_LINEAR + +#ifndef _VMA_BLOCK_METADATA_TLSF +// To not search current larger region if first allocation won't succeed and skip to smaller range +// use with VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as strategy in CreateAllocationRequest(). +// When fragmentation and reusal of previous blocks doesn't matter then use with +// VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT for fastest alloc time possible. +class VmaBlockMetadata_TLSF : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_TLSF) +public: + VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_TLSF(); + + size_t GetAllocationCount() const override { return m_AllocCount; } + size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; } + VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; } + bool IsEmpty() const override { return m_NullBlock->offset == 0; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; } + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + void DebugLogAllAllocations() const override; + +private: + // According to original paper it should be preferable 4 or 5: + // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems" + // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf + static const uint8_t SECOND_LEVEL_INDEX = 5; + static const uint16_t SMALL_BUFFER_SIZE = 256; + static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16; + static const uint8_t MEMORY_CLASS_SHIFT = 7; + static const uint8_t MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT; + + class Block + { + public: + VkDeviceSize offset; + VkDeviceSize size; + Block* prevPhysical; + Block* nextPhysical; + + void MarkFree() { prevFree = VMA_NULL; } + void MarkTaken() { prevFree = this; } + bool IsFree() const { return prevFree != this; } + void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; } + Block*& PrevFree() { return prevFree; } + Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; } + + private: + Block* prevFree; // Address of the same block here indicates that block is taken + union + { + Block* nextFree; + void* userData; + }; + }; + + size_t m_AllocCount; + // Total number of free blocks besides null block + size_t m_BlocksFreeCount; + // Total size of free blocks excluding null block + VkDeviceSize m_BlocksFreeSize; + uint32_t m_IsFreeBitmap; + uint8_t m_MemoryClasses; + uint32_t m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES]; + uint32_t m_ListsCount; + /* + * 0: 0-3 lists for small buffers + * 1+: 0-(2^SLI-1) lists for normal buffers + */ + Block** m_FreeList; + VmaPoolAllocator m_BlockAllocator; + Block* m_NullBlock; + VmaBlockBufferImageGranularity m_GranularityHandler; + + uint8_t SizeToMemoryClass(VkDeviceSize size) const; + uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const; + uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const; + uint32_t GetListIndex(VkDeviceSize size) const; + + void RemoveFreeBlock(Block* block); + void InsertFreeBlock(Block* block); + void MergeBlock(Block* block, Block* prev); + + Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const; + bool CheckBlock( + Block& block, + uint32_t listIndex, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaAllocationRequest* pAllocationRequest); +}; + +#ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS +VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_AllocCount(0), + m_BlocksFreeCount(0), + m_BlocksFreeSize(0), + m_IsFreeBitmap(0), + m_MemoryClasses(0), + m_ListsCount(0), + m_FreeList(VMA_NULL), + m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT), + m_NullBlock(VMA_NULL), + m_GranularityHandler(bufferImageGranularity) {} + +VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF() +{ + if (m_FreeList) + vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount); + m_GranularityHandler.Destroy(GetAllocationCallbacks()); +} + +void VmaBlockMetadata_TLSF::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + if (!IsVirtual()) + m_GranularityHandler.Init(GetAllocationCallbacks(), size); + + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = size; + m_NullBlock->offset = 0; + m_NullBlock->prevPhysical = VMA_NULL; + m_NullBlock->nextPhysical = VMA_NULL; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = VMA_NULL; + m_NullBlock->PrevFree() = VMA_NULL; + uint8_t memoryClass = SizeToMemoryClass(size); + uint16_t sli = SizeToSecondIndex(size, memoryClass); + m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1; + if (IsVirtual()) + m_ListsCount += 1UL << SECOND_LEVEL_INDEX; + else + m_ListsCount += 4; + + m_MemoryClasses = memoryClass + uint8_t(2); + memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(uint32_t)); + + m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount); + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); +} + +bool VmaBlockMetadata_TLSF::Validate() const +{ + VMA_VALIDATE(GetSumFreeSize() <= GetSize()); + + VkDeviceSize calculatedSize = m_NullBlock->size; + VkDeviceSize calculatedFreeSize = m_NullBlock->size; + size_t allocCount = 0; + size_t freeCount = 0; + + // Check integrity of free lists + for (uint32_t list = 0; list < m_ListsCount; ++list) + { + Block* block = m_FreeList[list]; + if (block != VMA_NULL) + { + VMA_VALIDATE(block->IsFree()); + VMA_VALIDATE(block->PrevFree() == VMA_NULL); + while (block->NextFree()) + { + VMA_VALIDATE(block->NextFree()->IsFree()); + VMA_VALIDATE(block->NextFree()->PrevFree() == block); + block = block->NextFree(); + } + } + } + + VkDeviceSize nextOffset = m_NullBlock->offset; + auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual()); + + VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL); + if (m_NullBlock->prevPhysical) + { + VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock); + } + // Check all blocks + for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical) + { + VMA_VALIDATE(prev->offset + prev->size == nextOffset); + nextOffset = prev->offset; + calculatedSize += prev->size; + + uint32_t listIndex = GetListIndex(prev->size); + if (prev->IsFree()) + { + ++freeCount; + // Check if free block belongs to free list + Block* freeBlock = m_FreeList[listIndex]; + VMA_VALIDATE(freeBlock != VMA_NULL); + + bool found = false; + do + { + if (freeBlock == prev) + found = true; + + freeBlock = freeBlock->NextFree(); + } while (!found && freeBlock != VMA_NULL); + + VMA_VALIDATE(found); + calculatedFreeSize += prev->size; + } + else + { + ++allocCount; + // Check if taken block is not on a free list + Block* freeBlock = m_FreeList[listIndex]; + while (freeBlock) + { + VMA_VALIDATE(freeBlock != prev); + freeBlock = freeBlock->NextFree(); + } + + if (!IsVirtual()) + { + VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size)); + } + } + + if (prev->prevPhysical) + { + VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev); + } + } + + if (!IsVirtual()) + { + VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx)); + } + + VMA_VALIDATE(nextOffset == 0); + VMA_VALIDATE(calculatedSize == GetSize()); + VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize()); + VMA_VALIDATE(allocCount == m_AllocCount); + VMA_VALIDATE(freeCount == m_BlocksFreeCount); + + return true; +} + +void VmaBlockMetadata_TLSF::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += GetSize(); + if (m_NullBlock->size > 0) + VmaAddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size); + + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (block->IsFree()) + VmaAddDetailedStatisticsUnusedRange(inoutStats, block->size); + else + VmaAddDetailedStatisticsAllocation(inoutStats, block->size); + } +} + +void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const +{ + inoutStats.blockCount++; + inoutStats.allocationCount += (uint32_t)m_AllocCount; + inoutStats.blockBytes += GetSize(); + inoutStats.allocationBytes += GetSize() - GetSumFreeSize(); +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const +{ + size_t blockCount = m_AllocCount + m_BlocksFreeCount; + VmaStlAllocator allocator(GetAllocationCallbacks()); + VmaVector> blockList(blockCount, allocator); + + size_t i = blockCount; + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + blockList[--i] = block; + } + VMA_ASSERT(i == 0); + + VmaDetailedStatistics stats; + VmaClearDetailedStatistics(stats); + AddDetailedStatistics(stats); + + PrintDetailedMap_Begin(json, + stats.statistics.blockBytes - stats.statistics.allocationBytes, + stats.statistics.allocationCount, + stats.unusedRangeCount); + + for (; i < blockCount; ++i) + { + Block* block = blockList[i]; + if (block->IsFree()) + PrintDetailedMap_UnusedRange(json, block->offset, block->size); + else + PrintDetailedMap_Allocation(json, block->offset, block->size, block->UserData()); + } + if (m_NullBlock->size > 0) + PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size); + + PrintDetailedMap_End(json); +} +#endif + +bool VmaBlockMetadata_TLSF::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!"); + VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm."); + + // For small granularity round up + if (!IsVirtual()) + m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment); + + allocSize += GetDebugMargin(); + // Quick check for too small pool + if (allocSize > GetSumFreeSize()) + return false; + + // If no free blocks in pool then check only null block + if (m_BlocksFreeCount == 0) + return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest); + + // Round up to the next block + VkDeviceSize sizeForNextList = allocSize; + VkDeviceSize smallSizeStep = VkDeviceSize(SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4)); + if (allocSize > SMALL_BUFFER_SIZE) + { + sizeForNextList += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX)); + } + else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep) + sizeForNextList = SMALL_BUFFER_SIZE + 1; + else + sizeForNextList += smallSizeStep; + + uint32_t nextListIndex = m_ListsCount; + uint32_t prevListIndex = m_ListsCount; + Block* nextListBlock = VMA_NULL; + Block* prevListBlock = VMA_NULL; + + // Check blocks according to strategies + if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) + { + // Quick check for larger block first + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + if (nextListBlock != VMA_NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // If not fitted then null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Null block failed, search larger bucket + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + + // Failed again, check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + } + else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT) + { + // Check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Check larger bucket + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + } + else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT ) + { + // Perform search from the start + VmaStlAllocator allocator(GetAllocationCallbacks()); + VmaVector> blockList(m_BlocksFreeCount, allocator); + + size_t i = m_BlocksFreeCount; + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (block->IsFree() && block->size >= allocSize) + blockList[--i] = block; + } + + for (; i < m_BlocksFreeCount; ++i) + { + Block& block = *blockList[i]; + if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Whole range searched, no more memory + return false; + } + else + { + // Check larger bucket + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + } + + // Worst case, full search has to be done + while (++nextListIndex < m_ListsCount) + { + nextListBlock = m_FreeList[nextListIndex]; + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + } + + // No more memory sadly + return false; +} + +VkResult VmaBlockMetadata_TLSF::CheckCorruption(const void* pBlockData) +{ + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (!block->IsFree()) + { + if (!VmaValidateMagicValue(pBlockData, block->offset + block->size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_TLSF::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF); + + // Get block and pop it from the free list + Block* currentBlock = (Block*)request.allocHandle; + VkDeviceSize offset = request.algorithmData; + VMA_ASSERT(currentBlock != VMA_NULL); + VMA_ASSERT(currentBlock->offset <= offset); + + if (currentBlock != m_NullBlock) + RemoveFreeBlock(currentBlock); + + VkDeviceSize debugMargin = GetDebugMargin(); + VkDeviceSize misssingAlignment = offset - currentBlock->offset; + + // Append missing alignment to prev block or create new one + if (misssingAlignment) + { + Block* prevBlock = currentBlock->prevPhysical; + VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!"); + + if (prevBlock->IsFree() && prevBlock->size != debugMargin) + { + uint32_t oldList = GetListIndex(prevBlock->size); + prevBlock->size += misssingAlignment; + // Check if new size crosses list bucket + if (oldList != GetListIndex(prevBlock->size)) + { + prevBlock->size -= misssingAlignment; + RemoveFreeBlock(prevBlock); + prevBlock->size += misssingAlignment; + InsertFreeBlock(prevBlock); + } + else + m_BlocksFreeSize += misssingAlignment; + } + else + { + Block* newBlock = m_BlockAllocator.Alloc(); + currentBlock->prevPhysical = newBlock; + prevBlock->nextPhysical = newBlock; + newBlock->prevPhysical = prevBlock; + newBlock->nextPhysical = currentBlock; + newBlock->size = misssingAlignment; + newBlock->offset = currentBlock->offset; + newBlock->MarkTaken(); + + InsertFreeBlock(newBlock); + } + + currentBlock->size -= misssingAlignment; + currentBlock->offset += misssingAlignment; + } + + VkDeviceSize size = request.size + debugMargin; + if (currentBlock->size == size) + { + if (currentBlock == m_NullBlock) + { + // Setup new null block + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = 0; + m_NullBlock->offset = currentBlock->offset + size; + m_NullBlock->prevPhysical = currentBlock; + m_NullBlock->nextPhysical = VMA_NULL; + m_NullBlock->MarkFree(); + m_NullBlock->PrevFree() = VMA_NULL; + m_NullBlock->NextFree() = VMA_NULL; + currentBlock->nextPhysical = m_NullBlock; + currentBlock->MarkTaken(); + } + } + else + { + VMA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!"); + + // Create new free block + Block* newBlock = m_BlockAllocator.Alloc(); + newBlock->size = currentBlock->size - size; + newBlock->offset = currentBlock->offset + size; + newBlock->prevPhysical = currentBlock; + newBlock->nextPhysical = currentBlock->nextPhysical; + currentBlock->nextPhysical = newBlock; + currentBlock->size = size; + + if (currentBlock == m_NullBlock) + { + m_NullBlock = newBlock; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = VMA_NULL; + m_NullBlock->PrevFree() = VMA_NULL; + currentBlock->MarkTaken(); + } + else + { + newBlock->nextPhysical->prevPhysical = newBlock; + newBlock->MarkTaken(); + InsertFreeBlock(newBlock); + } + } + currentBlock->UserData() = userData; + + if (debugMargin > 0) + { + currentBlock->size -= debugMargin; + Block* newBlock = m_BlockAllocator.Alloc(); + newBlock->size = debugMargin; + newBlock->offset = currentBlock->offset + currentBlock->size; + newBlock->prevPhysical = currentBlock; + newBlock->nextPhysical = currentBlock->nextPhysical; + newBlock->MarkTaken(); + currentBlock->nextPhysical->prevPhysical = newBlock; + currentBlock->nextPhysical = newBlock; + InsertFreeBlock(newBlock); + } + + if (!IsVirtual()) + m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData, + currentBlock->offset, currentBlock->size); + ++m_AllocCount; +} + +void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle) +{ + Block* block = (Block*)allocHandle; + Block* next = block->nextPhysical; + VMA_ASSERT(!block->IsFree() && "Block is already free!"); + + if (!IsVirtual()) + m_GranularityHandler.FreePages(block->offset, block->size); + --m_AllocCount; + + VkDeviceSize debugMargin = GetDebugMargin(); + if (debugMargin > 0) + { + RemoveFreeBlock(next); + MergeBlock(next, block); + block = next; + next = next->nextPhysical; + } + + // Try merging + Block* prev = block->prevPhysical; + if (prev != VMA_NULL && prev->IsFree() && prev->size != debugMargin) + { + RemoveFreeBlock(prev); + MergeBlock(block, prev); + } + + if (!next->IsFree()) + InsertFreeBlock(block); + else if (next == m_NullBlock) + MergeBlock(m_NullBlock, block); + else + { + RemoveFreeBlock(next); + MergeBlock(next, block); + InsertFreeBlock(next); + } +} + +void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!"); + outInfo.offset = block->offset; + outInfo.size = block->size; + outInfo.pUserData = block->UserData(); +} + +void* VmaBlockMetadata_TLSF::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Cannot get user data for free block!"); + return block->UserData(); +} + +VmaAllocHandle VmaBlockMetadata_TLSF::GetAllocationListBegin() const +{ + if (m_AllocCount == 0) + return VK_NULL_HANDLE; + + for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical) + { + if (!block->IsFree()) + return (VmaAllocHandle)block; + } + VMA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!"); + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_TLSF::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + Block* startBlock = (Block*)prevAlloc; + VMA_ASSERT(!startBlock->IsFree() && "Incorrect block!"); + + for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical) + { + if (!block->IsFree()) + return (VmaAllocHandle)block; + } + return VK_NULL_HANDLE; +} + +VkDeviceSize VmaBlockMetadata_TLSF::GetNextFreeRegionSize(VmaAllocHandle alloc) const +{ + Block* block = (Block*)alloc; + VMA_ASSERT(!block->IsFree() && "Incorrect block!"); + + if (block->prevPhysical) + return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0; + return 0; +} + +void VmaBlockMetadata_TLSF::Clear() +{ + m_AllocCount = 0; + m_BlocksFreeCount = 0; + m_BlocksFreeSize = 0; + m_IsFreeBitmap = 0; + m_NullBlock->offset = 0; + m_NullBlock->size = GetSize(); + Block* block = m_NullBlock->prevPhysical; + m_NullBlock->prevPhysical = VMA_NULL; + while (block) + { + Block* prev = block->prevPhysical; + m_BlockAllocator.Free(block); + block = prev; + } + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); + memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(uint32_t)); + m_GranularityHandler.Clear(); +} + +void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!"); + block->UserData() = userData; +} + +void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const +{ + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + if (!block->IsFree()) + DebugLogAllocation(block->offset, block->size, block->UserData()); +} + +uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const +{ + if (size > SMALL_BUFFER_SIZE) + return uint8_t(VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT); + return 0; +} + +uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const +{ + if (memoryClass == 0) + { + if (IsVirtual()) + return static_cast((size - 1) / 8); + else + return static_cast((size - 1) / 64); + } + return static_cast((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX)); +} + +uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const +{ + if (memoryClass == 0) + return secondIndex; + + const uint32_t index = static_cast(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex; + if (IsVirtual()) + return index + (1 << SECOND_LEVEL_INDEX); + else + return index + 4; +} + +uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const +{ + uint8_t memoryClass = SizeToMemoryClass(size); + return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass)); +} + +void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block) +{ + VMA_ASSERT(block != m_NullBlock); + VMA_ASSERT(block->IsFree()); + + if (block->NextFree() != VMA_NULL) + block->NextFree()->PrevFree() = block->PrevFree(); + if (block->PrevFree() != VMA_NULL) + block->PrevFree()->NextFree() = block->NextFree(); + else + { + uint8_t memClass = SizeToMemoryClass(block->size); + uint16_t secondIndex = SizeToSecondIndex(block->size, memClass); + uint32_t index = GetListIndex(memClass, secondIndex); + VMA_ASSERT(m_FreeList[index] == block); + m_FreeList[index] = block->NextFree(); + if (block->NextFree() == VMA_NULL) + { + m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex); + if (m_InnerIsFreeBitmap[memClass] == 0) + m_IsFreeBitmap &= ~(1UL << memClass); + } + } + block->MarkTaken(); + block->UserData() = VMA_NULL; + --m_BlocksFreeCount; + m_BlocksFreeSize -= block->size; +} + +void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block) +{ + VMA_ASSERT(block != m_NullBlock); + VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!"); + + uint8_t memClass = SizeToMemoryClass(block->size); + uint16_t secondIndex = SizeToSecondIndex(block->size, memClass); + uint32_t index = GetListIndex(memClass, secondIndex); + VMA_ASSERT(index < m_ListsCount); + block->PrevFree() = VMA_NULL; + block->NextFree() = m_FreeList[index]; + m_FreeList[index] = block; + if (block->NextFree() != VMA_NULL) + block->NextFree()->PrevFree() = block; + else + { + m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex; + m_IsFreeBitmap |= 1UL << memClass; + } + ++m_BlocksFreeCount; + m_BlocksFreeSize += block->size; +} + +void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev) +{ + VMA_ASSERT(block->prevPhysical == prev && "Cannot merge separate physical regions!"); + VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!"); + + block->offset = prev->offset; + block->size += prev->size; + block->prevPhysical = prev->prevPhysical; + if (block->prevPhysical) + block->prevPhysical->nextPhysical = block; + m_BlockAllocator.Free(prev); +} + +VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const +{ + uint8_t memoryClass = SizeToMemoryClass(size); + uint32_t innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass)); + if (!innerFreeMap) + { + // Check higher levels for available blocks + uint32_t freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1)); + if (!freeMap) + return VMA_NULL; // No more memory available + + // Find lowest free region + memoryClass = VMA_BITSCAN_LSB(freeMap); + innerFreeMap = m_InnerIsFreeBitmap[memoryClass]; + VMA_ASSERT(innerFreeMap != 0); + } + // Find lowest free subregion + listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(innerFreeMap)); + VMA_ASSERT(m_FreeList[listIndex]); + return m_FreeList[listIndex]; +} + +bool VmaBlockMetadata_TLSF::CheckBlock( + Block& block, + uint32_t listIndex, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(block.IsFree() && "Block is already taken!"); + + VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment); + if (block.size < allocSize + alignedOffset - block.offset) + return false; + + // Check for granularity conflicts + if (!IsVirtual() && + m_GranularityHandler.CheckConflictAndAlignUp(alignedOffset, allocSize, block.offset, block.size, allocType)) + return false; + + // Alloc successful + pAllocationRequest->type = VmaAllocationRequestType::TLSF; + pAllocationRequest->allocHandle = (VmaAllocHandle)█ + pAllocationRequest->size = allocSize - GetDebugMargin(); + pAllocationRequest->customData = (void*)allocType; + pAllocationRequest->algorithmData = alignedOffset; + + // Place block at the start of list if it's normal block + if (listIndex != m_ListsCount && block.PrevFree()) + { + block.PrevFree()->NextFree() = block.NextFree(); + if (block.NextFree()) + block.NextFree()->PrevFree() = block.PrevFree(); + block.PrevFree() = VMA_NULL; + block.NextFree() = m_FreeList[listIndex]; + m_FreeList[listIndex] = █ + if (block.NextFree()) + block.NextFree()->PrevFree() = █ + } + + return true; +} +#endif // _VMA_BLOCK_METADATA_TLSF_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_TLSF + +#ifndef _VMA_BLOCK_VECTOR +/* +Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific +Vulkan memory type. + +Synchronized internally with a mutex. +*/ +class VmaBlockVector +{ + friend struct VmaDefragmentationContext_T; + VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockVector) +public: + VmaBlockVector( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceSize preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + VkDeviceSize bufferImageGranularity, + bool explicitBlockSize, + uint32_t algorithm, + float priority, + VkDeviceSize minAllocationAlignment, + void* pMemoryAllocateNext); + ~VmaBlockVector(); + + VmaAllocator GetAllocator() const { return m_hAllocator; } + VmaPool GetParentPool() const { return m_hParentPool; } + bool IsCustomPool() const { return m_hParentPool != VMA_NULL; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; } + VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } + uint32_t GetAlgorithm() const { return m_Algorithm; } + bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; } + float GetPriority() const { return m_Priority; } + const void* GetAllocationNextPtr() const { return m_pMemoryAllocateNext; } + // To be used only while the m_Mutex is locked. Used during defragmentation. + size_t GetBlockCount() const { return m_Blocks.size(); } + // To be used only while the m_Mutex is locked. Used during defragmentation. + VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; } + VMA_RW_MUTEX &GetMutex() { return m_Mutex; } + + VkResult CreateMinBlocks(); + void AddStatistics(VmaStatistics& inoutStats); + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats); + bool IsEmpty(); + bool IsCorruptionDetectionEnabled() const; + + VkResult Allocate( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + void Free(const VmaAllocation hAllocation); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + VkResult CheckCorruption(); + +private: + const VmaAllocator m_hAllocator; + const VmaPool m_hParentPool; + const uint32_t m_MemoryTypeIndex; + const VkDeviceSize m_PreferredBlockSize; + const size_t m_MinBlockCount; + const size_t m_MaxBlockCount; + const VkDeviceSize m_BufferImageGranularity; + const bool m_ExplicitBlockSize; + const uint32_t m_Algorithm; + const float m_Priority; + const VkDeviceSize m_MinAllocationAlignment; + + void* const m_pMemoryAllocateNext; + VMA_RW_MUTEX m_Mutex; + // Incrementally sorted by sumFreeSize, ascending. + VmaVector> m_Blocks; + uint32_t m_NextBlockId; + bool m_IncrementalSort = true; + + void SetIncrementalSort(bool val) { m_IncrementalSort = val; } + + VkDeviceSize CalcMaxBlockSize() const; + // Finds and removes given block from vector. + void Remove(VmaDeviceMemoryBlock* pBlock); + // Performs single step in sorting m_Blocks. They may not be fully sorted + // after this call. + void IncrementallySortBlocks(); + void SortByFreeSize(); + + VkResult AllocatePage( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation); + + VkResult AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation); + + VkResult CommitAllocationRequest( + VmaAllocationRequest& allocRequest, + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation); + + VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex); + bool HasEmptyBlock(); +}; +#endif // _VMA_BLOCK_VECTOR + +#ifndef _VMA_DEFRAGMENTATION_CONTEXT +struct VmaDefragmentationContext_T +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaDefragmentationContext_T) +public: + VmaDefragmentationContext_T( + VmaAllocator hAllocator, + const VmaDefragmentationInfo& info); + ~VmaDefragmentationContext_T(); + + void GetStats(VmaDefragmentationStats& outStats) { outStats = m_GlobalStats; } + + VkResult DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo); + VkResult DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo); + +private: + // Max number of allocations to ignore due to size constraints before ending single pass + static const uint8_t MAX_ALLOCS_TO_IGNORE = 16; + enum class CounterStatus { Pass, Ignore, End }; + + struct FragmentedBlock + { + uint32_t data; + VmaDeviceMemoryBlock* block; + }; + struct StateBalanced + { + VkDeviceSize avgFreeSize = 0; + VkDeviceSize avgAllocSize = UINT64_MAX; + }; + struct StateExtensive + { + enum class Operation : uint8_t + { + FindFreeBlockBuffer, FindFreeBlockTexture, FindFreeBlockAll, + MoveBuffers, MoveTextures, MoveAll, + Cleanup, Done + }; + + Operation operation = Operation::FindFreeBlockTexture; + size_t firstFreeBlock = SIZE_MAX; + }; + struct MoveAllocationData + { + VkDeviceSize size; + VkDeviceSize alignment; + VmaSuballocationType type; + VmaAllocationCreateFlags flags; + VmaDefragmentationMove move = {}; + }; + + const VkDeviceSize m_MaxPassBytes; + const uint32_t m_MaxPassAllocations; + const PFN_vmaCheckDefragmentationBreakFunction m_BreakCallback; + void* m_BreakCallbackUserData; + + VmaStlAllocator m_MoveAllocator; + VmaVector> m_Moves; + + uint8_t m_IgnoredAllocs = 0; + uint32_t m_Algorithm; + uint32_t m_BlockVectorCount; + VmaBlockVector* m_PoolBlockVector; + VmaBlockVector** m_pBlockVectors; + size_t m_ImmovableBlockCount = 0; + VmaDefragmentationStats m_GlobalStats = { 0 }; + VmaDefragmentationStats m_PassStats = { 0 }; + void* m_AlgorithmState = VMA_NULL; + + static MoveAllocationData GetMoveData(VmaAllocHandle handle, VmaBlockMetadata* metadata); + CounterStatus CheckCounters(VkDeviceSize bytes); + bool IncrementCounters(VkDeviceSize bytes); + bool ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block); + bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector); + + bool ComputeDefragmentation(VmaBlockVector& vector, size_t index); + bool ComputeDefragmentation_Fast(VmaBlockVector& vector); + bool ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update); + bool ComputeDefragmentation_Full(VmaBlockVector& vector); + bool ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index); + + void UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state); + bool MoveDataToFreeBlocks(VmaSuballocationType currentType, + VmaBlockVector& vector, size_t firstFreeBlock, + bool& texturePresent, bool& bufferPresent, bool& otherPresent); +}; +#endif // _VMA_DEFRAGMENTATION_CONTEXT + +#ifndef _VMA_POOL_T +struct VmaPool_T +{ + friend struct VmaPoolListItemTraits; + VMA_CLASS_NO_COPY_NO_MOVE(VmaPool_T) +public: + VmaBlockVector m_BlockVector; + VmaDedicatedAllocationList m_DedicatedAllocations; + + VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize); + ~VmaPool_T(); + + uint32_t GetId() const { return m_Id; } + void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; } + + const char* GetName() const { return m_Name; } + void SetName(const char* pName); + +#if VMA_STATS_STRING_ENABLED + //void PrintDetailedMap(class VmaStringBuilder& sb); +#endif + +private: + uint32_t m_Id; + char* m_Name; + VmaPool_T* m_PrevPool = VMA_NULL; + VmaPool_T* m_NextPool = VMA_NULL; +}; + +struct VmaPoolListItemTraits +{ + typedef VmaPool_T ItemType; + + static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; } + static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; } + static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; } + static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; } +}; +#endif // _VMA_POOL_T + +#ifndef _VMA_CURRENT_BUDGET_DATA +struct VmaCurrentBudgetData +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaCurrentBudgetData) +public: + + VMA_ATOMIC_UINT32 m_BlockCount[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT32 m_AllocationCount[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS]; + +#if VMA_MEMORY_BUDGET + VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch; + VMA_RW_MUTEX m_BudgetMutex; + uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS]; + uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS]; + uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS]; +#endif // VMA_MEMORY_BUDGET + + VmaCurrentBudgetData(); + + void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize); + void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize); +}; + +#ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS +VmaCurrentBudgetData::VmaCurrentBudgetData() +{ + for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex) + { + m_BlockCount[heapIndex] = 0; + m_AllocationCount[heapIndex] = 0; + m_BlockBytes[heapIndex] = 0; + m_AllocationBytes[heapIndex] = 0; +#if VMA_MEMORY_BUDGET + m_VulkanUsage[heapIndex] = 0; + m_VulkanBudget[heapIndex] = 0; + m_BlockBytesAtBudgetFetch[heapIndex] = 0; +#endif + } + +#if VMA_MEMORY_BUDGET + m_OperationsSinceBudgetFetch = 0; +#endif +} + +void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize) +{ + m_AllocationBytes[heapIndex] += allocationSize; + ++m_AllocationCount[heapIndex]; +#if VMA_MEMORY_BUDGET + ++m_OperationsSinceBudgetFetch; +#endif +} + +void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize) +{ + VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); + m_AllocationBytes[heapIndex] -= allocationSize; + VMA_ASSERT(m_AllocationCount[heapIndex] > 0); + --m_AllocationCount[heapIndex]; +#if VMA_MEMORY_BUDGET + ++m_OperationsSinceBudgetFetch; +#endif +} +#endif // _VMA_CURRENT_BUDGET_DATA_FUNCTIONS +#endif // _VMA_CURRENT_BUDGET_DATA + +#ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR +/* +Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects. +*/ +class VmaAllocationObjectAllocator +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocationObjectAllocator) +public: + VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) + : m_Allocator(pAllocationCallbacks, 1024) {} + + template VmaAllocation Allocate(Types&&... args); + void Free(VmaAllocation hAlloc); + +private: + VMA_MUTEX m_Mutex; + VmaPoolAllocator m_Allocator; +}; + +template +VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args) +{ + VmaMutexLock mutexLock(m_Mutex); + return m_Allocator.Alloc(std::forward(args)...); +} + +void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc) +{ + VmaMutexLock mutexLock(m_Mutex); + m_Allocator.Free(hAlloc); +} +#endif // _VMA_ALLOCATION_OBJECT_ALLOCATOR + +#ifndef _VMA_VIRTUAL_BLOCK_T +struct VmaVirtualBlock_T +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaVirtualBlock_T) +public: + const bool m_AllocationCallbacksSpecified; + const VkAllocationCallbacks m_AllocationCallbacks; + + VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo); + ~VmaVirtualBlock_T(); + + VkResult Init() { return VK_SUCCESS; } + bool IsEmpty() const { return m_Metadata->IsEmpty(); } + void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); } + void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); } + void Clear() { m_Metadata->Clear(); } + + const VkAllocationCallbacks* GetAllocationCallbacks() const; + void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo); + VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation, + VkDeviceSize* outOffset); + void GetStatistics(VmaStatistics& outStats) const; + void CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const; +#if VMA_STATS_STRING_ENABLED + void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const; +#endif + +private: + VmaBlockMetadata* m_Metadata; +}; + +#ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS +VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo) + : m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL), + m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks) +{ + const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK; + switch (algorithm) + { + case 0: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true); + break; + case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true); + break; + default: + VMA_ASSERT(0); + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true); + } + + m_Metadata->Init(createInfo.size); +} + +VmaVirtualBlock_T::~VmaVirtualBlock_T() +{ + // Define macro VMA_DEBUG_LOG_FORMAT or more specialized VMA_LEAK_LOG_FORMAT + // to receive the list of the unfreed allocations. + if (!m_Metadata->IsEmpty()) + m_Metadata->DebugLogAllAllocations(); + // This is the most important assert in the entire library. + // Hitting it means you have some memory leak - unreleased virtual allocations. + VMA_ASSERT_LEAK(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!"); + + vma_delete(GetAllocationCallbacks(), m_Metadata); +} + +const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const +{ + return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL; +} + +void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo) +{ + m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo); +} + +VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation, + VkDeviceSize* outOffset) +{ + VmaAllocationRequest request = {}; + if (m_Metadata->CreateAllocationRequest( + createInfo.size, // allocSize + VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment + (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress + VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant + createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy + &request)) + { + m_Metadata->Alloc(request, + VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant + createInfo.pUserData); + outAllocation = (VmaVirtualAllocation)request.allocHandle; + if(outOffset) + *outOffset = m_Metadata->GetAllocationOffset(request.allocHandle); + return VK_SUCCESS; + } + outAllocation = (VmaVirtualAllocation)VK_NULL_HANDLE; + if (outOffset) + *outOffset = UINT64_MAX; + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +void VmaVirtualBlock_T::GetStatistics(VmaStatistics& outStats) const +{ + VmaClearStatistics(outStats); + m_Metadata->AddStatistics(outStats); +} + +void VmaVirtualBlock_T::CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const +{ + VmaClearDetailedStatistics(outStats); + m_Metadata->AddDetailedStatistics(outStats); +} + +#if VMA_STATS_STRING_ENABLED +void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const +{ + VmaJsonWriter json(GetAllocationCallbacks(), sb); + json.BeginObject(); + + VmaDetailedStatistics stats; + CalculateDetailedStatistics(stats); + + json.WriteString("Stats"); + VmaPrintDetailedStatistics(json, stats); + + if (detailedMap) + { + json.WriteString("Details"); + json.BeginObject(); + m_Metadata->PrintDetailedMap(json); + json.EndObject(); + } + + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_VIRTUAL_BLOCK_T_FUNCTIONS +#endif // _VMA_VIRTUAL_BLOCK_T + + +// Main allocator object. +struct VmaAllocator_T +{ + VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocator_T) +public: + const bool m_UseMutex; + const uint32_t m_VulkanApiVersion; + bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0). + bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0). + bool m_UseExtMemoryBudget; + bool m_UseAmdDeviceCoherentMemory; + bool m_UseKhrBufferDeviceAddress; + bool m_UseExtMemoryPriority; + bool m_UseKhrMaintenance4; + bool m_UseKhrMaintenance5; + const VkDevice m_hDevice; + const VkInstance m_hInstance; + const bool m_AllocationCallbacksSpecified; + const VkAllocationCallbacks m_AllocationCallbacks; + VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks; + VmaAllocationObjectAllocator m_AllocationObjectAllocator; + + // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size. + uint32_t m_HeapSizeLimitMask; + + VkPhysicalDeviceProperties m_PhysicalDeviceProperties; + VkPhysicalDeviceMemoryProperties m_MemProps; + + // Default pools. + VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES]; + VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES]; + + VmaCurrentBudgetData m_Budget; + VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects. + + VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); + VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo); + ~VmaAllocator_T(); + + const VkAllocationCallbacks* GetAllocationCallbacks() const + { + return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL; + } + const VmaVulkanFunctions& GetVulkanFunctions() const + { + return m_VulkanFunctions; + } + + VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; } + + VkDeviceSize GetBufferImageGranularity() const + { + return VMA_MAX( + static_cast(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY), + m_PhysicalDeviceProperties.limits.bufferImageGranularity); + } + + uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; } + uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; } + + uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const + { + VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount); + return m_MemProps.memoryTypes[memTypeIndex].heapIndex; + } + // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT. + bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const + { + return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) == + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + // Minimum alignment for all allocations in specific memory type. + VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const + { + return IsMemoryTypeNonCoherent(memTypeIndex) ? + VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) : + (VkDeviceSize)VMA_MIN_ALIGNMENT; + } + + bool IsIntegratedGpu() const + { + return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + } + + uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; } + + void GetBufferMemoryRequirements( + VkBuffer hBuffer, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + void GetImageMemoryRequirements( + VkImage hImage, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + VkResult FindMemoryTypeIndex( + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VmaBufferImageUsage bufImgUsage, + uint32_t* pMemoryTypeIndex) const; + + // Main allocation function. + VkResult AllocateMemory( + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VmaBufferImageUsage dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Main deallocation function. + void FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations); + + void CalculateStatistics(VmaTotalStatistics* pStats); + + void GetHeapBudgets( + VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo); + void GetAllocationInfo2(VmaAllocation hAllocation, VmaAllocationInfo2* pAllocationInfo); + + VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool); + void DestroyPool(VmaPool pool); + void GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats); + void CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats); + + void SetCurrentFrameIndex(uint32_t frameIndex); + uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); } + + VkResult CheckPoolCorruption(VmaPool hPool); + VkResult CheckCorruption(uint32_t memoryTypeBits); + + // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping. + VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory); + // Call to Vulkan function vkFreeMemory with accompanying bookkeeping. + void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory); + // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR. + VkResult BindVulkanBuffer( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkBuffer buffer, + const void* pNext); + // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR. + VkResult BindVulkanImage( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkImage image, + const void* pNext); + + VkResult Map(VmaAllocation hAllocation, void** ppData); + void Unmap(VmaAllocation hAllocation); + + VkResult BindBufferMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext); + VkResult BindImageMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext); + + VkResult FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op); + VkResult FlushOrInvalidateAllocations( + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, const VkDeviceSize* sizes, + VMA_CACHE_OPERATION op); + + VkResult CopyMemoryToAllocation( + const void* pSrcHostPointer, + VmaAllocation dstAllocation, + VkDeviceSize dstAllocationLocalOffset, + VkDeviceSize size); + VkResult CopyAllocationToMemory( + VmaAllocation srcAllocation, + VkDeviceSize srcAllocationLocalOffset, + void* pDstHostPointer, + VkDeviceSize size); + + void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern); + + /* + Returns bit mask of memory types that can support defragmentation on GPU as + they support creation of required buffer for copy operations. + */ + uint32_t GetGpuDefragmentationMemoryTypeBits(); + +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const + { + return m_TypeExternalMemoryHandleTypes[memTypeIndex]; + } +#endif // #if VMA_EXTERNAL_MEMORY + +private: + VkDeviceSize m_PreferredLargeHeapBlockSize; + + VkPhysicalDevice m_PhysicalDevice; + VMA_ATOMIC_UINT32 m_CurrentFrameIndex; + VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized. +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES]; +#endif // #if VMA_EXTERNAL_MEMORY + + VMA_RW_MUTEX m_PoolsMutex; + typedef VmaIntrusiveLinkedList PoolList; + // Protected by m_PoolsMutex. + PoolList m_Pools; + uint32_t m_NextPoolId; + + VmaVulkanFunctions m_VulkanFunctions; + + // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types. + uint32_t m_GlobalMemoryTypeBits; + + void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions); + +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + void ImportVulkanFunctions_Static(); +#endif + + void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions); + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + void ImportVulkanFunctions_Dynamic(); +#endif + + void ValidateVulkanFunctions(); + + VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex); + + VkResult AllocateMemoryOfType( + VmaPool pool, + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedPreferred, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VmaBufferImageUsage dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + uint32_t memTypeIndex, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + VmaBlockVector& blockVector, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Helper function only to be used inside AllocateDedicatedMemory. + VkResult AllocateDedicatedMemoryPage( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + bool isMappingAllowed, + void* pUserData, + VmaAllocation* pAllocation); + + // Allocates and registers new VkDeviceMemory specifically for dedicated allocations. + VkResult AllocateDedicatedMemory( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + uint32_t memTypeIndex, + bool map, + bool isUserDataString, + bool isMappingAllowed, + bool canAliasMemory, + void* pUserData, + float priority, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VmaBufferImageUsage dedicatedBufferImageUsage, + size_t allocationCount, + VmaAllocation* pAllocations, + const void* pNextChain = VMA_NULL); + + void FreeDedicatedMemory(const VmaAllocation allocation); + + VkResult CalcMemTypeParams( + VmaAllocationCreateInfo& outCreateInfo, + uint32_t memTypeIndex, + VkDeviceSize size, + size_t allocationCount); + VkResult CalcAllocationParams( + VmaAllocationCreateInfo& outCreateInfo, + bool dedicatedRequired, + bool dedicatedPreferred); + + /* + Calculates and returns bit mask of memory types that can support defragmentation + on GPU as they support creation of required buffer for copy operations. + */ + uint32_t CalculateGpuDefragmentationMemoryTypeBits() const; + uint32_t CalculateGlobalMemoryTypeBits() const; + + bool GetFlushOrInvalidateRange( + VmaAllocation allocation, + VkDeviceSize offset, VkDeviceSize size, + VkMappedMemoryRange& outRange) const; + +#if VMA_MEMORY_BUDGET + void UpdateVulkanBudget(); +#endif // #if VMA_MEMORY_BUDGET +}; + + +#ifndef _VMA_MEMORY_FUNCTIONS +static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment) +{ + return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment); +} + +static void VmaFree(VmaAllocator hAllocator, void* ptr) +{ + VmaFree(&hAllocator->m_AllocationCallbacks, ptr); +} + +template +static T* VmaAllocate(VmaAllocator hAllocator) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T)); +} + +template +static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T)); +} + +template +static void vma_delete(VmaAllocator hAllocator, T* ptr) +{ + if(ptr != VMA_NULL) + { + ptr->~T(); + VmaFree(hAllocator, ptr); + } +} + +template +static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count) +{ + if(ptr != VMA_NULL) + { + for(size_t i = count; i--; ) + ptr[i].~T(); + VmaFree(hAllocator, ptr); + } +} +#endif // _VMA_MEMORY_FUNCTIONS + +#ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS +VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) + : m_pMetadata(VMA_NULL), + m_MemoryTypeIndex(UINT32_MAX), + m_Id(0), + m_hMemory(VK_NULL_HANDLE), + m_MapCount(0), + m_pMappedData(VMA_NULL) {} + +VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock() +{ + VMA_ASSERT_LEAK(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped."); + VMA_ASSERT_LEAK(m_hMemory == VK_NULL_HANDLE); +} + +void VmaDeviceMemoryBlock::Init( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm, + VkDeviceSize bufferImageGranularity) +{ + VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); + + m_hParentPool = hParentPool; + m_MemoryTypeIndex = newMemoryTypeIndex; + m_Id = id; + m_hMemory = newMemory; + + switch (algorithm) + { + case 0: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + break; + case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + break; + default: + VMA_ASSERT(0); + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + } + m_pMetadata->Init(newSize); +} + +void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator) +{ + // Define macro VMA_DEBUG_LOG_FORMAT or more specialized VMA_LEAK_LOG_FORMAT + // to receive the list of the unfreed allocations. + if (!m_pMetadata->IsEmpty()) + m_pMetadata->DebugLogAllAllocations(); + // This is the most important assert in the entire library. + // Hitting it means you have some memory leak - unreleased VmaAllocation objects. + VMA_ASSERT_LEAK(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!"); + + VMA_ASSERT_LEAK(m_hMemory != VK_NULL_HANDLE); + allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory); + m_hMemory = VK_NULL_HANDLE; + + vma_delete(allocator, m_pMetadata); + m_pMetadata = VMA_NULL; +} + +void VmaDeviceMemoryBlock::PostAlloc(VmaAllocator hAllocator) +{ + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + m_MappingHysteresis.PostAlloc(); +} + +void VmaDeviceMemoryBlock::PostFree(VmaAllocator hAllocator) +{ + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + if(m_MappingHysteresis.PostFree()) + { + VMA_ASSERT(m_MappingHysteresis.GetExtraMapping() == 0); + if (m_MapCount == 0) + { + m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory); + } + } +} + +bool VmaDeviceMemoryBlock::Validate() const +{ + VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) && + (m_pMetadata->GetSize() != 0)); + + return m_pMetadata->Validate(); +} + +VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator) +{ + void* pData = VMA_NULL; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + res = m_pMetadata->CheckCorruption(pData); + + Unmap(hAllocator, 1); + + return res; +} + +VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData) +{ + if (count == 0) + { + return VK_SUCCESS; + } + + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + const uint32_t oldTotalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping(); + if (oldTotalMapCount != 0) + { + VMA_ASSERT(m_pMappedData != VMA_NULL); + m_MappingHysteresis.PostMap(); + m_MapCount += count; + if (ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + return VK_SUCCESS; + } + else + { + VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( + hAllocator->m_hDevice, + m_hMemory, + 0, // offset + VK_WHOLE_SIZE, + 0, // flags + &m_pMappedData); + if (result == VK_SUCCESS) + { + VMA_ASSERT(m_pMappedData != VMA_NULL); + m_MappingHysteresis.PostMap(); + m_MapCount = count; + if (ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + } + return result; + } +} + +void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count) +{ + if (count == 0) + { + return; + } + + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + if (m_MapCount >= count) + { + m_MapCount -= count; + const uint32_t totalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping(); + if (totalMapCount == 0) + { + m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory); + } + m_MappingHysteresis.PostUnmap(); + } + else + { + VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped."); + } +} + +VkResult VmaDeviceMemoryBlock::WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + VmaWriteMagicValue(pData, allocOffset + allocSize); + + Unmap(hAllocator, 1); + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + if (!VmaValidateMagicValue(pData, allocOffset + allocSize)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!"); + } + + Unmap(hAllocator, 1); + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() && + "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?"); + const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset; + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext); +} + +VkResult VmaDeviceMemoryBlock::BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() && + "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?"); + const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset; + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext); +} +#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS + +#ifndef _VMA_ALLOCATION_T_FUNCTIONS +VmaAllocation_T::VmaAllocation_T(bool mappingAllowed) + : m_Alignment{ 1 }, + m_Size{ 0 }, + m_pUserData{ VMA_NULL }, + m_pName{ VMA_NULL }, + m_MemoryTypeIndex{ 0 }, + m_Type{ (uint8_t)ALLOCATION_TYPE_NONE }, + m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN }, + m_MapCount{ 0 }, + m_Flags{ 0 } +{ + if(mappingAllowed) + m_Flags |= (uint8_t)FLAG_MAPPING_ALLOWED; +} + +VmaAllocation_T::~VmaAllocation_T() +{ + VMA_ASSERT_LEAK(m_MapCount == 0 && "Allocation was not unmapped before destruction."); + + // Check if owned string was freed. + VMA_ASSERT(m_pName == VMA_NULL); +} + +void VmaAllocation_T::InitBlockAllocation( + VmaDeviceMemoryBlock* block, + VmaAllocHandle allocHandle, + VkDeviceSize alignment, + VkDeviceSize size, + uint32_t memoryTypeIndex, + VmaSuballocationType suballocationType, + bool mapped) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(block != VMA_NULL); + m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; + m_Alignment = alignment; + m_Size = size; + m_MemoryTypeIndex = memoryTypeIndex; + if(mapped) + { + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP; + } + m_SuballocationType = (uint8_t)suballocationType; + m_BlockAllocation.m_Block = block; + m_BlockAllocation.m_AllocHandle = allocHandle; +} + +void VmaAllocation_T::InitDedicatedAllocation( + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + void* pMappedData, + VkDeviceSize size) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(hMemory != VK_NULL_HANDLE); + m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED; + m_Alignment = 0; + m_Size = size; + m_MemoryTypeIndex = memoryTypeIndex; + m_SuballocationType = (uint8_t)suballocationType; + if(pMappedData != VMA_NULL) + { + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP; + } + m_DedicatedAllocation.m_hParentPool = hParentPool; + m_DedicatedAllocation.m_hMemory = hMemory; + m_DedicatedAllocation.m_pMappedData = pMappedData; + m_DedicatedAllocation.m_Prev = VMA_NULL; + m_DedicatedAllocation.m_Next = VMA_NULL; +} + +void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName) +{ + VMA_ASSERT(pName == VMA_NULL || pName != m_pName); + + FreeName(hAllocator); + + if (pName != VMA_NULL) + m_pName = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), pName); +} + +uint8_t VmaAllocation_T::SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation) +{ + VMA_ASSERT(allocation != VMA_NULL); + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + VMA_ASSERT(allocation->m_Type == ALLOCATION_TYPE_BLOCK); + + if (m_MapCount != 0) + m_BlockAllocation.m_Block->Unmap(hAllocator, m_MapCount); + + m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, allocation); + std::swap(m_BlockAllocation, allocation->m_BlockAllocation); + m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, this); + +#if VMA_STATS_STRING_ENABLED + std::swap(m_BufferImageUsage, allocation->m_BufferImageUsage); +#endif + return m_MapCount; +} + +VmaAllocHandle VmaAllocation_T::GetAllocHandle() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_AllocHandle; + case ALLOCATION_TYPE_DEDICATED: + return VK_NULL_HANDLE; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +VkDeviceSize VmaAllocation_T::GetOffset() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle); + case ALLOCATION_TYPE_DEDICATED: + return 0; + default: + VMA_ASSERT(0); + return 0; + } +} + +VmaPool VmaAllocation_T::GetParentPool() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->GetParentPool(); + case ALLOCATION_TYPE_DEDICATED: + return m_DedicatedAllocation.m_hParentPool; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +VkDeviceMemory VmaAllocation_T::GetMemory() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->GetDeviceMemory(); + case ALLOCATION_TYPE_DEDICATED: + return m_DedicatedAllocation.m_hMemory; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +void* VmaAllocation_T::GetMappedData() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + if (m_MapCount != 0 || IsPersistentMap()) + { + void* pBlockData = m_BlockAllocation.m_Block->GetMappedData(); + VMA_ASSERT(pBlockData != VMA_NULL); + return (char*)pBlockData + GetOffset(); + } + else + { + return VMA_NULL; + } + break; + case ALLOCATION_TYPE_DEDICATED: + VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap())); + return m_DedicatedAllocation.m_pMappedData; + default: + VMA_ASSERT(0); + return VMA_NULL; + } +} + +void VmaAllocation_T::BlockAllocMap() +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + + if (m_MapCount < 0xFF) + { + ++m_MapCount; + } + else + { + VMA_ASSERT(0 && "Allocation mapped too many times simultaneously."); + } +} + +void VmaAllocation_T::BlockAllocUnmap() +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); + + if (m_MapCount > 0) + { + --m_MapCount; + } + else + { + VMA_ASSERT(0 && "Unmapping allocation not previously mapped."); + } +} + +VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData) +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + + if (m_MapCount != 0 || IsPersistentMap()) + { + if (m_MapCount < 0xFF) + { + VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL); + *ppData = m_DedicatedAllocation.m_pMappedData; + ++m_MapCount; + return VK_SUCCESS; + } + else + { + VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously."); + return VK_ERROR_MEMORY_MAP_FAILED; + } + } + else + { + VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( + hAllocator->m_hDevice, + m_DedicatedAllocation.m_hMemory, + 0, // offset + VK_WHOLE_SIZE, + 0, // flags + ppData); + if (result == VK_SUCCESS) + { + m_DedicatedAllocation.m_pMappedData = *ppData; + m_MapCount = 1; + } + return result; + } +} + +void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator) +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); + + if (m_MapCount > 0) + { + --m_MapCount; + if (m_MapCount == 0 && !IsPersistentMap()) + { + m_DedicatedAllocation.m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)( + hAllocator->m_hDevice, + m_DedicatedAllocation.m_hMemory); + } + } + else + { + VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped."); + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const +{ + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]); + + json.WriteString("Size"); + json.WriteNumber(m_Size); + json.WriteString("Usage"); + json.WriteNumber(m_BufferImageUsage.Value); // It may be uint32_t or uint64_t. + + if (m_pUserData != VMA_NULL) + { + json.WriteString("CustomData"); + json.BeginString(); + json.ContinueString_Pointer(m_pUserData); + json.EndString(); + } + if (m_pName != VMA_NULL) + { + json.WriteString("Name"); + json.WriteString(m_pName); + } +} +#endif // VMA_STATS_STRING_ENABLED + +void VmaAllocation_T::FreeName(VmaAllocator hAllocator) +{ + if(m_pName) + { + VmaFreeString(hAllocator->GetAllocationCallbacks(), m_pName); + m_pName = VMA_NULL; + } +} +#endif // _VMA_ALLOCATION_T_FUNCTIONS + +#ifndef _VMA_BLOCK_VECTOR_FUNCTIONS +VmaBlockVector::VmaBlockVector( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceSize preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + VkDeviceSize bufferImageGranularity, + bool explicitBlockSize, + uint32_t algorithm, + float priority, + VkDeviceSize minAllocationAlignment, + void* pMemoryAllocateNext) + : m_hAllocator(hAllocator), + m_hParentPool(hParentPool), + m_MemoryTypeIndex(memoryTypeIndex), + m_PreferredBlockSize(preferredBlockSize), + m_MinBlockCount(minBlockCount), + m_MaxBlockCount(maxBlockCount), + m_BufferImageGranularity(bufferImageGranularity), + m_ExplicitBlockSize(explicitBlockSize), + m_Algorithm(algorithm), + m_Priority(priority), + m_MinAllocationAlignment(minAllocationAlignment), + m_pMemoryAllocateNext(pMemoryAllocateNext), + m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_NextBlockId(0) {} + +VmaBlockVector::~VmaBlockVector() +{ + for (size_t i = m_Blocks.size(); i--; ) + { + m_Blocks[i]->Destroy(m_hAllocator); + vma_delete(m_hAllocator, m_Blocks[i]); + } +} + +VkResult VmaBlockVector::CreateMinBlocks() +{ + for (size_t i = 0; i < m_MinBlockCount; ++i) + { + VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL); + if (res != VK_SUCCESS) + { + return res; + } + } + return VK_SUCCESS; +} + +void VmaBlockVector::AddStatistics(VmaStatistics& inoutStats) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + const size_t blockCount = m_Blocks.size(); + for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VMA_HEAVY_ASSERT(pBlock->Validate()); + pBlock->m_pMetadata->AddStatistics(inoutStats); + } +} + +void VmaBlockVector::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + const size_t blockCount = m_Blocks.size(); + for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VMA_HEAVY_ASSERT(pBlock->Validate()); + pBlock->m_pMetadata->AddDetailedStatistics(inoutStats); + } +} + +bool VmaBlockVector::IsEmpty() +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + return m_Blocks.empty(); +} + +bool VmaBlockVector::IsCorruptionDetectionEnabled() const +{ + const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + return (VMA_DEBUG_DETECT_CORRUPTION != 0) && + (VMA_DEBUG_MARGIN > 0) && + (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) && + (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags; +} + +VkResult VmaBlockVector::Allocate( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + size_t allocIndex; + VkResult res = VK_SUCCESS; + + alignment = VMA_MAX(alignment, m_MinAllocationAlignment); + + if (IsCorruptionDetectionEnabled()) + { + size = VmaAlignUp(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE)); + alignment = VmaAlignUp(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE)); + } + + { + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocatePage( + size, + alignment, + createInfo, + suballocType, + pAllocations + allocIndex); + if (res != VK_SUCCESS) + { + break; + } + } + } + + if (res != VK_SUCCESS) + { + // Free all already created allocations. + while (allocIndex--) + Free(pAllocations[allocIndex]); + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaBlockVector::AllocatePage( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation) +{ + const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; + + VkDeviceSize freeMemory; + { + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); + VmaBudget heapBudget = {}; + m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1); + freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0; + } + + const bool canFallbackToDedicated = !HasExplicitBlockSize() && + (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0; + const bool canCreateNewBlock = + ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) && + (m_Blocks.size() < m_MaxBlockCount) && + (freeMemory >= size || !canFallbackToDedicated); + uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK; + + // Upper address can only be used with linear allocator and within single memory block. + if (isUpperAddress && + (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1)) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + // Early reject: requested allocation size is larger that maximum block size for this block vector. + if (size + VMA_DEBUG_MARGIN > m_PreferredBlockSize) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + + // 1. Search existing allocations. Try to allocate. + if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + { + // Use only last block. + if (!m_Blocks.empty()) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back(); + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Returned from last block #%" PRIu32, pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + else + { + if (strategy != VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) // MIN_MEMORY or default + { + const bool isHostVisible = + (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; + if(isHostVisible) + { + const bool isMappingAllowed = (createInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0; + /* + For non-mappable allocations, check blocks that are not mapped first. + For mappable allocations, check blocks that are already mapped first. + This way, having many blocks, we will separate mappable and non-mappable allocations, + hopefully limiting the number of blocks that are mapped, which will help tools like RenderDoc. + */ + for(size_t mappingI = 0; mappingI < 2; ++mappingI) + { + // Forward order in m_Blocks - prefer blocks with smallest amount of free space. + for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + const bool isBlockMapped = pCurrBlock->GetMappedData() != VMA_NULL; + if((mappingI == 0) == (isMappingAllowed == isBlockMapped)) + { + VkResult res = AllocateFromBlock( + pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%" PRIu32, pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + } + } + else + { + // Forward order in m_Blocks - prefer blocks with smallest amount of free space. + for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%" PRIu32, pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + } + else // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT + { + // Backward order in m_Blocks - prefer blocks with largest amount of free space. + for (size_t blockIndex = m_Blocks.size(); blockIndex--; ) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock(pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%" PRIu32, pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + } + + // 2. Try to create new block. + if (canCreateNewBlock) + { + // Calculate optimal size for new block. + VkDeviceSize newBlockSize = m_PreferredBlockSize; + uint32_t newBlockSizeShift = 0; + const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3; + + if (!m_ExplicitBlockSize) + { + // Allocate 1/8, 1/4, 1/2 as first blocks. + const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize(); + for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + } + else + { + break; + } + } + } + + size_t newBlockIndex = 0; + VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? + CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY; + // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize. + if (!m_ExplicitBlockSize) + { + while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize >= size) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? + CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + else + { + break; + } + } + } + + if (res == VK_SUCCESS) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex]; + VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size); + + res = AllocateFromBlock( + pBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Created new block #%" PRIu32 " Size=%" PRIu64, pBlock->GetId(), newBlockSize); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + else + { + // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + } + + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +void VmaBlockVector::Free(const VmaAllocation hAllocation) +{ + VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL; + + bool budgetExceeded = false; + { + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); + VmaBudget heapBudget = {}; + m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1); + budgetExceeded = heapBudget.usage >= heapBudget.budget; + } + + // Scope for lock. + { + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + + if (IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->ValidateMagicValueAfterAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize()); + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value."); + } + + if (hAllocation->IsPersistentMap()) + { + pBlock->Unmap(m_hAllocator, 1); + } + + const bool hadEmptyBlockBeforeFree = HasEmptyBlock(); + pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle()); + pBlock->PostFree(m_hAllocator); + VMA_HEAVY_ASSERT(pBlock->Validate()); + + VMA_DEBUG_LOG_FORMAT(" Freed from MemoryTypeIndex=%" PRIu32, m_MemoryTypeIndex); + + const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount; + // pBlock became empty after this deallocation. + if (pBlock->m_pMetadata->IsEmpty()) + { + // Already had empty block. We don't want to have two, so delete this one. + if ((hadEmptyBlockBeforeFree || budgetExceeded) && canDeleteBlock) + { + pBlockToDelete = pBlock; + Remove(pBlock); + } + // else: We now have one empty block - leave it. A hysteresis to avoid allocating whole block back and forth. + } + // pBlock didn't become empty, but we have another empty block - find and free that one. + // (This is optional, heuristics.) + else if (hadEmptyBlockBeforeFree && canDeleteBlock) + { + VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back(); + if (pLastBlock->m_pMetadata->IsEmpty()) + { + pBlockToDelete = pLastBlock; + m_Blocks.pop_back(); + } + } + + IncrementallySortBlocks(); + } + + // Destruction of a free block. Deferred until this point, outside of mutex + // lock, for performance reason. + if (pBlockToDelete != VMA_NULL) + { + VMA_DEBUG_LOG_FORMAT(" Deleted empty block #%" PRIu32, pBlockToDelete->GetId()); + pBlockToDelete->Destroy(m_hAllocator); + vma_delete(m_hAllocator, pBlockToDelete); + } + + m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize()); + m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation); +} + +VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const +{ + VkDeviceSize result = 0; + for (size_t i = m_Blocks.size(); i--; ) + { + result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize()); + if (result >= m_PreferredBlockSize) + { + break; + } + } + return result; +} + +void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock) +{ + for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + if (m_Blocks[blockIndex] == pBlock) + { + VmaVectorRemove(m_Blocks, blockIndex); + return; + } + } + VMA_ASSERT(0); +} + +void VmaBlockVector::IncrementallySortBlocks() +{ + if (!m_IncrementalSort) + return; + if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + { + // Bubble sort only until first swap. + for (size_t i = 1; i < m_Blocks.size(); ++i) + { + if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize()) + { + std::swap(m_Blocks[i - 1], m_Blocks[i]); + return; + } + } + } +} + +void VmaBlockVector::SortByFreeSize() +{ + VMA_SORT(m_Blocks.begin(), m_Blocks.end(), + [](VmaDeviceMemoryBlock* b1, VmaDeviceMemoryBlock* b2) -> bool + { + return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize(); + }); +} + +VkResult VmaBlockVector::AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation) +{ + const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; + + VmaAllocationRequest currRequest = {}; + if (pBlock->m_pMetadata->CreateAllocationRequest( + size, + alignment, + isUpperAddress, + suballocType, + strategy, + &currRequest)) + { + return CommitAllocationRequest(currRequest, pBlock, alignment, allocFlags, pUserData, suballocType, pAllocation); + } + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +VkResult VmaBlockVector::CommitAllocationRequest( + VmaAllocationRequest& allocRequest, + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation) +{ + const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; + const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; + const bool isMappingAllowed = (allocFlags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0; + + pBlock->PostAlloc(m_hAllocator); + // Allocate from pCurrBlock. + if (mapped) + { + VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL); + if (res != VK_SUCCESS) + { + return res; + } + } + + *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isMappingAllowed); + pBlock->m_pMetadata->Alloc(allocRequest, suballocType, *pAllocation); + (*pAllocation)->InitBlockAllocation( + pBlock, + allocRequest.allocHandle, + alignment, + allocRequest.size, // Not size, as actual allocation size may be larger than requested! + m_MemoryTypeIndex, + suballocType, + mapped); + VMA_HEAVY_ASSERT(pBlock->Validate()); + if (isUserDataString) + (*pAllocation)->SetName(m_hAllocator, (const char*)pUserData); + else + (*pAllocation)->SetUserData(m_hAllocator, pUserData); + m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), allocRequest.size); + if (VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + if (IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->WriteMagicValueAfterAllocation(m_hAllocator, (*pAllocation)->GetOffset(), allocRequest.size); + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value."); + } + return VK_SUCCESS; +} + +VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex) +{ + VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocInfo.pNext = m_pMemoryAllocateNext; + allocInfo.memoryTypeIndex = m_MemoryTypeIndex; + allocInfo.allocationSize = blockSize; + +#if VMA_BUFFER_DEVICE_ADDRESS + // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature. + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR }; + if (m_hAllocator->m_UseKhrBufferDeviceAddress) + { + allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo); + } +#endif // VMA_BUFFER_DEVICE_ADDRESS + +#if VMA_MEMORY_PRIORITY + VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; + if (m_hAllocator->m_UseExtMemoryPriority) + { + VMA_ASSERT(m_Priority >= 0.f && m_Priority <= 1.f); + priorityInfo.priority = m_Priority; + VmaPnextChainPushFront(&allocInfo, &priorityInfo); + } +#endif // VMA_MEMORY_PRIORITY + +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex); + if (exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // VMA_EXTERNAL_MEMORY + + VkDeviceMemory mem = VK_NULL_HANDLE; + VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem); + if (res < 0) + { + return res; + } + + // New VkDeviceMemory successfully created. + + // Create new Allocation for it. + VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator); + pBlock->Init( + m_hAllocator, + m_hParentPool, + m_MemoryTypeIndex, + mem, + allocInfo.allocationSize, + m_NextBlockId++, + m_Algorithm, + m_BufferImageGranularity); + + m_Blocks.push_back(pBlock); + if (pNewBlockIndex != VMA_NULL) + { + *pNewBlockIndex = m_Blocks.size() - 1; + } + + return VK_SUCCESS; +} + +bool VmaBlockVector::HasEmptyBlock() +{ + for (size_t index = 0, count = m_Blocks.size(); index < count; ++index) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[index]; + if (pBlock->m_pMetadata->IsEmpty()) + { + return true; + } + } + return false; +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + + json.BeginObject(); + for (size_t i = 0; i < m_Blocks.size(); ++i) + { + json.BeginString(); + json.ContinueString(m_Blocks[i]->GetId()); + json.EndString(); + + json.BeginObject(); + json.WriteString("MapRefCount"); + json.WriteNumber(m_Blocks[i]->GetMapRefCount()); + + m_Blocks[i]->m_pMetadata->PrintDetailedMap(json); + json.EndObject(); + } + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED + +VkResult VmaBlockVector::CheckCorruption() +{ + if (!IsCorruptionDetectionEnabled()) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VkResult res = pBlock->CheckCorruption(m_hAllocator); + if (res != VK_SUCCESS) + { + return res; + } + } + return VK_SUCCESS; +} + +#endif // _VMA_BLOCK_VECTOR_FUNCTIONS + +#ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS +VmaDefragmentationContext_T::VmaDefragmentationContext_T( + VmaAllocator hAllocator, + const VmaDefragmentationInfo& info) + : m_MaxPassBytes(info.maxBytesPerPass == 0 ? VK_WHOLE_SIZE : info.maxBytesPerPass), + m_MaxPassAllocations(info.maxAllocationsPerPass == 0 ? UINT32_MAX : info.maxAllocationsPerPass), + m_BreakCallback(info.pfnBreakCallback), + m_BreakCallbackUserData(info.pBreakCallbackUserData), + m_MoveAllocator(hAllocator->GetAllocationCallbacks()), + m_Moves(m_MoveAllocator) +{ + m_Algorithm = info.flags & VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK; + + if (info.pool != VMA_NULL) + { + m_BlockVectorCount = 1; + m_PoolBlockVector = &info.pool->m_BlockVector; + m_pBlockVectors = &m_PoolBlockVector; + m_PoolBlockVector->SetIncrementalSort(false); + m_PoolBlockVector->SortByFreeSize(); + } + else + { + m_BlockVectorCount = hAllocator->GetMemoryTypeCount(); + m_PoolBlockVector = VMA_NULL; + m_pBlockVectors = hAllocator->m_pBlockVectors; + for (uint32_t i = 0; i < m_BlockVectorCount; ++i) + { + VmaBlockVector* vector = m_pBlockVectors[i]; + if (vector != VMA_NULL) + { + vector->SetIncrementalSort(false); + vector->SortByFreeSize(); + } + } + } + + switch (m_Algorithm) + { + case 0: // Default algorithm + m_Algorithm = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT; + m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount); + break; + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT: + m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount); + break; + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + if (hAllocator->GetBufferImageGranularity() > 1) + { + m_AlgorithmState = vma_new_array(hAllocator, StateExtensive, m_BlockVectorCount); + } + break; + } +} + +VmaDefragmentationContext_T::~VmaDefragmentationContext_T() +{ + if (m_PoolBlockVector != VMA_NULL) + { + m_PoolBlockVector->SetIncrementalSort(true); + } + else + { + for (uint32_t i = 0; i < m_BlockVectorCount; ++i) + { + VmaBlockVector* vector = m_pBlockVectors[i]; + if (vector != VMA_NULL) + vector->SetIncrementalSort(true); + } + } + + if (m_AlgorithmState) + { + switch (m_Algorithm) + { + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT: + vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast(m_AlgorithmState), m_BlockVectorCount); + break; + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast(m_AlgorithmState), m_BlockVectorCount); + break; + default: + VMA_ASSERT(0); + } + } +} + +VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo) +{ + if (m_PoolBlockVector != VMA_NULL) + { + VmaMutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->GetAllocator()->m_UseMutex); + + if (m_PoolBlockVector->GetBlockCount() > 1) + ComputeDefragmentation(*m_PoolBlockVector, 0); + else if (m_PoolBlockVector->GetBlockCount() == 1) + ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0)); + } + else + { + for (uint32_t i = 0; i < m_BlockVectorCount; ++i) + { + if (m_pBlockVectors[i] != VMA_NULL) + { + VmaMutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->GetAllocator()->m_UseMutex); + + if (m_pBlockVectors[i]->GetBlockCount() > 1) + { + if (ComputeDefragmentation(*m_pBlockVectors[i], i)) + break; + } + else if (m_pBlockVectors[i]->GetBlockCount() == 1) + { + if (ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0))) + break; + } + } + } + } + + moveInfo.moveCount = static_cast(m_Moves.size()); + if (moveInfo.moveCount > 0) + { + moveInfo.pMoves = m_Moves.data(); + return VK_INCOMPLETE; + } + + moveInfo.pMoves = VMA_NULL; + return VK_SUCCESS; +} + +VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo) +{ + VMA_ASSERT(moveInfo.moveCount > 0 ? moveInfo.pMoves != VMA_NULL : true); + + VkResult result = VK_SUCCESS; + VmaStlAllocator blockAllocator(m_MoveAllocator.m_pCallbacks); + VmaVector> immovableBlocks(blockAllocator); + VmaVector> mappedBlocks(blockAllocator); + + VmaAllocator allocator = VMA_NULL; + for (uint32_t i = 0; i < moveInfo.moveCount; ++i) + { + VmaDefragmentationMove& move = moveInfo.pMoves[i]; + size_t prevCount = 0, currentCount = 0; + VkDeviceSize freedBlockSize = 0; + + uint32_t vectorIndex; + VmaBlockVector* vector; + if (m_PoolBlockVector != VMA_NULL) + { + vectorIndex = 0; + vector = m_PoolBlockVector; + } + else + { + vectorIndex = move.srcAllocation->GetMemoryTypeIndex(); + vector = m_pBlockVectors[vectorIndex]; + VMA_ASSERT(vector != VMA_NULL); + } + + switch (move.operation) + { + case VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY: + { + uint8_t mapCount = move.srcAllocation->SwapBlockAllocation(vector->m_hAllocator, move.dstTmpAllocation); + if (mapCount > 0) + { + allocator = vector->m_hAllocator; + VmaDeviceMemoryBlock* newMapBlock = move.srcAllocation->GetBlock(); + bool notPresent = true; + for (FragmentedBlock& block : mappedBlocks) + { + if (block.block == newMapBlock) + { + notPresent = false; + block.data += mapCount; + break; + } + } + if (notPresent) + mappedBlocks.push_back({ mapCount, newMapBlock }); + } + + // Scope for locks, Free have it's own lock + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + prevCount = vector->GetBlockCount(); + freedBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize(); + } + vector->Free(move.dstTmpAllocation); + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + currentCount = vector->GetBlockCount(); + } + + result = VK_INCOMPLETE; + break; + } + case VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE: + { + m_PassStats.bytesMoved -= move.srcAllocation->GetSize(); + --m_PassStats.allocationsMoved; + vector->Free(move.dstTmpAllocation); + + VmaDeviceMemoryBlock* newBlock = move.srcAllocation->GetBlock(); + bool notPresent = true; + for (const FragmentedBlock& block : immovableBlocks) + { + if (block.block == newBlock) + { + notPresent = false; + break; + } + } + if (notPresent) + immovableBlocks.push_back({ vectorIndex, newBlock }); + break; + } + case VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY: + { + m_PassStats.bytesMoved -= move.srcAllocation->GetSize(); + --m_PassStats.allocationsMoved; + // Scope for locks, Free have it's own lock + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + prevCount = vector->GetBlockCount(); + freedBlockSize = move.srcAllocation->GetBlock()->m_pMetadata->GetSize(); + } + vector->Free(move.srcAllocation); + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + currentCount = vector->GetBlockCount(); + } + freedBlockSize *= prevCount - currentCount; + + VkDeviceSize dstBlockSize; + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + dstBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize(); + } + vector->Free(move.dstTmpAllocation); + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount()); + currentCount = vector->GetBlockCount(); + } + + result = VK_INCOMPLETE; + break; + } + default: + VMA_ASSERT(0); + } + + if (prevCount > currentCount) + { + size_t freedBlocks = prevCount - currentCount; + m_PassStats.deviceMemoryBlocksFreed += static_cast(freedBlocks); + m_PassStats.bytesFreed += freedBlockSize; + } + + if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT && + m_AlgorithmState != VMA_NULL) + { + // Avoid unnecessary tries to allocate when new free block is available + StateExtensive& state = reinterpret_cast(m_AlgorithmState)[vectorIndex]; + if (state.firstFreeBlock != SIZE_MAX) + { + const size_t diff = prevCount - currentCount; + if (state.firstFreeBlock >= diff) + { + state.firstFreeBlock -= diff; + if (state.firstFreeBlock != 0) + state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty(); + } + else + state.firstFreeBlock = 0; + } + } + } + moveInfo.moveCount = 0; + moveInfo.pMoves = VMA_NULL; + m_Moves.clear(); + + // Update stats + m_GlobalStats.allocationsMoved += m_PassStats.allocationsMoved; + m_GlobalStats.bytesFreed += m_PassStats.bytesFreed; + m_GlobalStats.bytesMoved += m_PassStats.bytesMoved; + m_GlobalStats.deviceMemoryBlocksFreed += m_PassStats.deviceMemoryBlocksFreed; + m_PassStats = { 0 }; + + // Move blocks with immovable allocations according to algorithm + if (immovableBlocks.size() > 0) + { + do + { + if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT) + { + if (m_AlgorithmState != VMA_NULL) + { + bool swapped = false; + // Move to the start of free blocks range + for (const FragmentedBlock& block : immovableBlocks) + { + StateExtensive& state = reinterpret_cast(m_AlgorithmState)[block.data]; + if (state.operation != StateExtensive::Operation::Cleanup) + { + VmaBlockVector* vector = m_pBlockVectors[block.data]; + VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + + for (size_t i = 0, count = vector->GetBlockCount() - m_ImmovableBlockCount; i < count; ++i) + { + if (vector->GetBlock(i) == block.block) + { + std::swap(vector->m_Blocks[i], vector->m_Blocks[vector->GetBlockCount() - ++m_ImmovableBlockCount]); + if (state.firstFreeBlock != SIZE_MAX) + { + if (i + 1 < state.firstFreeBlock) + { + if (state.firstFreeBlock > 1) + std::swap(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]); + else + --state.firstFreeBlock; + } + } + swapped = true; + break; + } + } + } + } + if (swapped) + result = VK_INCOMPLETE; + break; + } + } + + // Move to the beginning + for (const FragmentedBlock& block : immovableBlocks) + { + VmaBlockVector* vector = m_pBlockVectors[block.data]; + VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + + for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i) + { + if (vector->GetBlock(i) == block.block) + { + std::swap(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]); + break; + } + } + } + } while (false); + } + + // Bulk-map destination blocks + for (const FragmentedBlock& block : mappedBlocks) + { + VkResult res = block.block->Map(allocator, block.data, VMA_NULL); + VMA_ASSERT(res == VK_SUCCESS); + } + return result; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation(VmaBlockVector& vector, size_t index) +{ + switch (m_Algorithm) + { + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT: + return ComputeDefragmentation_Fast(vector); + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT: + return ComputeDefragmentation_Balanced(vector, index, true); + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT: + return ComputeDefragmentation_Full(vector); + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + return ComputeDefragmentation_Extensive(vector, index); + default: + VMA_ASSERT(0); + return ComputeDefragmentation_Balanced(vector, index, true); + } +} + +VmaDefragmentationContext_T::MoveAllocationData VmaDefragmentationContext_T::GetMoveData( + VmaAllocHandle handle, VmaBlockMetadata* metadata) +{ + MoveAllocationData moveData; + moveData.move.srcAllocation = (VmaAllocation)metadata->GetAllocationUserData(handle); + moveData.size = moveData.move.srcAllocation->GetSize(); + moveData.alignment = moveData.move.srcAllocation->GetAlignment(); + moveData.type = moveData.move.srcAllocation->GetSuballocationType(); + moveData.flags = 0; + + if (moveData.move.srcAllocation->IsPersistentMap()) + moveData.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; + if (moveData.move.srcAllocation->IsMappingAllowed()) + moveData.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + + return moveData; +} + +VmaDefragmentationContext_T::CounterStatus VmaDefragmentationContext_T::CheckCounters(VkDeviceSize bytes) +{ + // Check custom criteria if exists + if (m_BreakCallback && m_BreakCallback(m_BreakCallbackUserData)) + return CounterStatus::End; + + // Ignore allocation if will exceed max size for copy + if (m_PassStats.bytesMoved + bytes > m_MaxPassBytes) + { + if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE) + return CounterStatus::Ignore; + else + return CounterStatus::End; + } + else + m_IgnoredAllocs = 0; + return CounterStatus::Pass; +} + +bool VmaDefragmentationContext_T::IncrementCounters(VkDeviceSize bytes) +{ + m_PassStats.bytesMoved += bytes; + // Early return when max found + if (++m_PassStats.allocationsMoved >= m_MaxPassAllocations || m_PassStats.bytesMoved >= m_MaxPassBytes) + { + VMA_ASSERT((m_PassStats.allocationsMoved == m_MaxPassAllocations || + m_PassStats.bytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!"); + return true; + } + return false; +} + +bool VmaDefragmentationContext_T::ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block) +{ + VmaBlockMetadata* metadata = block->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + VkDeviceSize offset = moveData.move.srcAllocation->GetOffset(); + if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + VmaAllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + moveData.type, + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (vector.CommitAllocationRequest( + request, + block, + moveData.alignment, + moveData.flags, + this, + moveData.type, + &moveData.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + return false; +} + +bool VmaDefragmentationContext_T::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector) +{ + for (; start < end; ++start) + { + VmaDeviceMemoryBlock* dstBlock = vector.GetBlock(start); + if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size) + { + if (vector.AllocateFromBlock(dstBlock, + data.size, + data.alignment, + data.flags, + this, + data.type, + 0, + &data.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(data.move); + if (IncrementCounters(data.size)) + return true; + break; + } + } + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Fast(VmaBlockVector& vector) +{ + // Move only between blocks + + // Go through allocations in last blocks and try to fit them inside first ones + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Check all previous blocks for free space + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + } + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update) +{ + // Go over every allocation and try to fit it in previous blocks at lowest offsets, + // if not possible: realloc within single block to minimize offset (exclude offset == 0), + // but only if there are noticeable gaps between them (some heuristic, ex. average size of allocation in block) + VMA_ASSERT(m_AlgorithmState != VMA_NULL); + + StateBalanced& vectorState = reinterpret_cast(m_AlgorithmState)[index]; + if (update && vectorState.avgAllocSize == UINT64_MAX) + UpdateVectorStatistics(vector, vectorState); + + const size_t startMoveCount = m_Moves.size(); + VkDeviceSize minimalFreeRegion = vectorState.avgFreeSize / 2; + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + VmaDeviceMemoryBlock* block = vector.GetBlock(i); + VmaBlockMetadata* metadata = block->m_pMetadata; + VkDeviceSize prevFreeRegionSize = 0; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Check all previous blocks for free space + const size_t prevMoveCount = m_Moves.size(); + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + + VkDeviceSize nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle); + // If no room found then realloc within block for lower offset + VkDeviceSize offset = moveData.move.srcAllocation->GetOffset(); + if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + // Check if realloc will make sense + if (prevFreeRegionSize >= minimalFreeRegion || + nextFreeRegionSize >= minimalFreeRegion || + moveData.size <= vectorState.avgFreeSize || + moveData.size <= vectorState.avgAllocSize) + { + VmaAllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + moveData.type, + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (vector.CommitAllocationRequest( + request, + block, + moveData.alignment, + moveData.flags, + this, + moveData.type, + &moveData.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + prevFreeRegionSize = nextFreeRegionSize; + } + } + + // No moves performed, update statistics to current vector state + if (startMoveCount == m_Moves.size() && !update) + { + vectorState.avgAllocSize = UINT64_MAX; + return ComputeDefragmentation_Balanced(vector, index, false); + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Full(VmaBlockVector& vector) +{ + // Go over every allocation and try to fit it in previous blocks at lowest offsets, + // if not possible: realloc within single block to minimize offset (exclude offset == 0) + + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + VmaDeviceMemoryBlock* block = vector.GetBlock(i); + VmaBlockMetadata* metadata = block->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Check all previous blocks for free space + const size_t prevMoveCount = m_Moves.size(); + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + + // If no room found then realloc within block for lower offset + VkDeviceSize offset = moveData.move.srcAllocation->GetOffset(); + if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + VmaAllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + moveData.type, + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (vector.CommitAllocationRequest( + request, + block, + moveData.alignment, + moveData.flags, + this, + moveData.type, + &moveData.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index) +{ + // First free single block, then populate it to the brim, then free another block, and so on + + // Fallback to previous algorithm since without granularity conflicts it can achieve max packing + if (vector.m_BufferImageGranularity == 1) + return ComputeDefragmentation_Full(vector); + + VMA_ASSERT(m_AlgorithmState != VMA_NULL); + + StateExtensive& vectorState = reinterpret_cast(m_AlgorithmState)[index]; + + bool texturePresent = false, bufferPresent = false, otherPresent = false; + switch (vectorState.operation) + { + case StateExtensive::Operation::Done: // Vector defragmented + return false; + case StateExtensive::Operation::FindFreeBlockBuffer: + case StateExtensive::Operation::FindFreeBlockTexture: + case StateExtensive::Operation::FindFreeBlockAll: + { + // No more blocks to free, just perform fast realloc and move to cleanup + if (vectorState.firstFreeBlock == 0) + { + vectorState.operation = StateExtensive::Operation::Cleanup; + return ComputeDefragmentation_Fast(vector); + } + + // No free blocks, have to clear last one + size_t last = (vectorState.firstFreeBlock == SIZE_MAX ? vector.GetBlockCount() : vectorState.firstFreeBlock) - 1; + VmaBlockMetadata* freeMetadata = vector.GetBlock(last)->m_pMetadata; + + const size_t prevMoveCount = m_Moves.size(); + for (VmaAllocHandle handle = freeMetadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = freeMetadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, freeMetadata); + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Check all previous blocks for free space + if (AllocInOtherBlock(0, last, moveData, vector)) + { + // Full clear performed already + if (prevMoveCount != m_Moves.size() && freeMetadata->GetNextAllocation(handle) == VK_NULL_HANDLE) + vectorState.firstFreeBlock = last; + return true; + } + } + + if (prevMoveCount == m_Moves.size()) + { + // Cannot perform full clear, have to move data in other blocks around + if (last != 0) + { + for (size_t i = last - 1; i; --i) + { + if (ReallocWithinBlock(vector, vector.GetBlock(i))) + return true; + } + } + + if (prevMoveCount == m_Moves.size()) + { + // No possible reallocs within blocks, try to move them around fast + return ComputeDefragmentation_Fast(vector); + } + } + else + { + switch (vectorState.operation) + { + case StateExtensive::Operation::FindFreeBlockBuffer: + vectorState.operation = StateExtensive::Operation::MoveBuffers; + break; + case StateExtensive::Operation::FindFreeBlockTexture: + vectorState.operation = StateExtensive::Operation::MoveTextures; + break; + case StateExtensive::Operation::FindFreeBlockAll: + vectorState.operation = StateExtensive::Operation::MoveAll; + break; + default: + VMA_ASSERT(0); + vectorState.operation = StateExtensive::Operation::MoveTextures; + } + vectorState.firstFreeBlock = last; + // Nothing done, block found without reallocations, can perform another reallocs in same pass + return ComputeDefragmentation_Extensive(vector, index); + } + break; + } + case StateExtensive::Operation::MoveTextures: + { + if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL, vector, + vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent)) + { + if (texturePresent) + { + vectorState.operation = StateExtensive::Operation::FindFreeBlockTexture; + return ComputeDefragmentation_Extensive(vector, index); + } + + if (!bufferPresent && !otherPresent) + { + vectorState.operation = StateExtensive::Operation::Cleanup; + break; + } + + // No more textures to move, check buffers + vectorState.operation = StateExtensive::Operation::MoveBuffers; + bufferPresent = false; + otherPresent = false; + } + else + break; + VMA_FALLTHROUGH; // Fallthrough + } + case StateExtensive::Operation::MoveBuffers: + { + if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_BUFFER, vector, + vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent)) + { + if (bufferPresent) + { + vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer; + return ComputeDefragmentation_Extensive(vector, index); + } + + if (!otherPresent) + { + vectorState.operation = StateExtensive::Operation::Cleanup; + break; + } + + // No more buffers to move, check all others + vectorState.operation = StateExtensive::Operation::MoveAll; + otherPresent = false; + } + else + break; + VMA_FALLTHROUGH; // Fallthrough + } + case StateExtensive::Operation::MoveAll: + { + if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_FREE, vector, + vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent)) + { + if (otherPresent) + { + vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer; + return ComputeDefragmentation_Extensive(vector, index); + } + // Everything moved + vectorState.operation = StateExtensive::Operation::Cleanup; + } + break; + } + case StateExtensive::Operation::Cleanup: + // Cleanup is handled below so that other operations may reuse the cleanup code. This case is here to prevent the unhandled enum value warning (C4062). + break; + } + + if (vectorState.operation == StateExtensive::Operation::Cleanup) + { + // All other work done, pack data in blocks even tighter if possible + const size_t prevMoveCount = m_Moves.size(); + for (size_t i = 0; i < vector.GetBlockCount(); ++i) + { + if (ReallocWithinBlock(vector, vector.GetBlock(i))) + return true; + } + + if (prevMoveCount == m_Moves.size()) + vectorState.operation = StateExtensive::Operation::Done; + } + return false; +} + +void VmaDefragmentationContext_T::UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state) +{ + size_t allocCount = 0; + size_t freeCount = 0; + state.avgFreeSize = 0; + state.avgAllocSize = 0; + + for (size_t i = 0; i < vector.GetBlockCount(); ++i) + { + VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata; + + allocCount += metadata->GetAllocationCount(); + freeCount += metadata->GetFreeRegionsCount(); + state.avgFreeSize += metadata->GetSumFreeSize(); + state.avgAllocSize += metadata->GetSize(); + } + + state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount; + state.avgFreeSize /= freeCount; +} + +bool VmaDefragmentationContext_T::MoveDataToFreeBlocks(VmaSuballocationType currentType, + VmaBlockVector& vector, size_t firstFreeBlock, + bool& texturePresent, bool& bufferPresent, bool& otherPresent) +{ + const size_t prevMoveCount = m_Moves.size(); + for (size_t i = firstFreeBlock ; i;) + { + VmaDeviceMemoryBlock* block = vector.GetBlock(--i); + VmaBlockMetadata* metadata = block->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Move only single type of resources at once + if (!VmaIsBufferImageGranularityConflict(moveData.type, currentType)) + { + // Try to fit allocation into free blocks + if (AllocInOtherBlock(firstFreeBlock, vector.GetBlockCount(), moveData, vector)) + return false; + } + + if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)) + texturePresent = true; + else if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_BUFFER)) + bufferPresent = true; + else + otherPresent = true; + } + } + return prevMoveCount == m_Moves.size(); +} +#endif // _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS + +#ifndef _VMA_POOL_T_FUNCTIONS +VmaPool_T::VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize) + : m_BlockVector( + hAllocator, + this, // hParentPool + createInfo.memoryTypeIndex, + createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize, + createInfo.minBlockCount, + createInfo.maxBlockCount, + (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(), + createInfo.blockSize != 0, // explicitBlockSize + createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm + createInfo.priority, + VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment), + createInfo.pMemoryAllocateNext), + m_Id(0), + m_Name(VMA_NULL) {} + +VmaPool_T::~VmaPool_T() +{ + VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL); + + const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks(); + VmaFreeString(allocs, m_Name); +} + +void VmaPool_T::SetName(const char* pName) +{ + const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks(); + VmaFreeString(allocs, m_Name); + + if (pName != VMA_NULL) + { + m_Name = VmaCreateStringCopy(allocs, pName); + } + else + { + m_Name = VMA_NULL; + } +} +#endif // _VMA_POOL_T_FUNCTIONS + +#ifndef _VMA_ALLOCATOR_T_FUNCTIONS +VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : + m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0), + m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0), + m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0), + m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0), + m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0), + m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0), + m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0), + m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0), + m_UseKhrMaintenance4((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0), + m_UseKhrMaintenance5((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT) != 0), + m_hDevice(pCreateInfo->device), + m_hInstance(pCreateInfo->instance), + m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL), + m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ? + *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks), + m_AllocationObjectAllocator(&m_AllocationCallbacks), + m_HeapSizeLimitMask(0), + m_DeviceMemoryCount(0), + m_PreferredLargeHeapBlockSize(0), + m_PhysicalDevice(pCreateInfo->physicalDevice), + m_GpuDefragmentationMemoryTypeBits(UINT32_MAX), + m_NextPoolId(0), + m_GlobalMemoryTypeBits(UINT32_MAX) +{ + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + m_UseKhrDedicatedAllocation = false; + m_UseKhrBindMemory2 = false; + } + + if(VMA_DEBUG_DETECT_CORRUPTION) + { + // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it. + VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0); + } + + VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance); + + if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0)) + { +#if !(VMA_DEDICATED_ALLOCATION) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros."); + } +#endif +#if !(VMA_BIND_MEMORY2) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros."); + } +#endif + } +#if !(VMA_MEMORY_BUDGET) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros."); + } +#endif +#if !(VMA_BUFFER_DEVICE_ADDRESS) + if(m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif +#if VMA_VULKAN_VERSION < 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_3 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if VMA_VULKAN_VERSION < 1002000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if VMA_VULKAN_VERSION < 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if !(VMA_MEMORY_PRIORITY) + if(m_UseExtMemoryPriority) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif +#if !(VMA_KHR_MAINTENANCE4) + if(m_UseKhrMaintenance4) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif +#if !(VMA_KHR_MAINTENANCE5) + if(m_UseKhrMaintenance5) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif + + memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks)); + memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties)); + memset(&m_MemProps, 0, sizeof(m_MemProps)); + + memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors)); + memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions)); + +#if VMA_EXTERNAL_MEMORY + memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes)); +#endif // #if VMA_EXTERNAL_MEMORY + + if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL) + { + m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData; + m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate; + m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree; + } + + ImportVulkanFunctions(pCreateInfo->pVulkanFunctions); + + (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties); + (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps); + + VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT)); + VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize)); + + m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ? + pCreateInfo->preferredLargeHeapBlockSize : static_cast(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE); + + m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits(); + +#if VMA_EXTERNAL_MEMORY + if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL) + { + memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes, + sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount()); + } +#endif // #if VMA_EXTERNAL_MEMORY + + if(pCreateInfo->pHeapSizeLimit != VMA_NULL) + { + for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex) + { + const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex]; + if(limit != VK_WHOLE_SIZE) + { + m_HeapSizeLimitMask |= 1u << heapIndex; + if(limit < m_MemProps.memoryHeaps[heapIndex].size) + { + m_MemProps.memoryHeaps[heapIndex].size = limit; + } + } + } + } + + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + // Create only supported types + if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0) + { + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex); + m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)( + this, + VK_NULL_HANDLE, // hParentPool + memTypeIndex, + preferredBlockSize, + 0, + SIZE_MAX, + GetBufferImageGranularity(), + false, // explicitBlockSize + 0, // algorithm + 0.5f, // priority (0.5 is the default per Vulkan spec) + GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment + VMA_NULL); // // pMemoryAllocateNext + // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here, + // because minBlockCount is 0. + } + } +} + +VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) +{ + VkResult res = VK_SUCCESS; + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + UpdateVulkanBudget(); + } +#endif // #if VMA_MEMORY_BUDGET + + return res; +} + +VmaAllocator_T::~VmaAllocator_T() +{ + VMA_ASSERT(m_Pools.IsEmpty()); + + for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; ) + { + vma_delete(this, m_pBlockVectors[memTypeIndex]); + } +} + +void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions) +{ +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + ImportVulkanFunctions_Static(); +#endif + + if(pVulkanFunctions != VMA_NULL) + { + ImportVulkanFunctions_Custom(pVulkanFunctions); + } + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + ImportVulkanFunctions_Dynamic(); +#endif + + ValidateVulkanFunctions(); +} + +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Static() +{ + // Vulkan 1.0 + m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr; + m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr; + m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties; + m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties; + m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory; + m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory; + m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory; + m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory; + m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges; + m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges; + m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory; + m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory; + m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements; + m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements; + m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer; + m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer; + m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage; + m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage; + m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer; + + // Vulkan 1.1 +#if VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2; + m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2; + m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2; + m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2; + } +#endif + +#if VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2; + } +#endif + +#if VMA_VULKAN_VERSION >= 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)vkGetDeviceBufferMemoryRequirements; + m_VulkanFunctions.vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)vkGetDeviceImageMemoryRequirements; + } +#endif +} + +#endif // VMA_STATIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions) +{ + VMA_ASSERT(pVulkanFunctions != VMA_NULL); + +#define VMA_COPY_IF_NOT_NULL(funcName) \ + if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName; + + VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr); + VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr); + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties); + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties); + VMA_COPY_IF_NOT_NULL(vkAllocateMemory); + VMA_COPY_IF_NOT_NULL(vkFreeMemory); + VMA_COPY_IF_NOT_NULL(vkMapMemory); + VMA_COPY_IF_NOT_NULL(vkUnmapMemory); + VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkBindBufferMemory); + VMA_COPY_IF_NOT_NULL(vkBindImageMemory); + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkCreateBuffer); + VMA_COPY_IF_NOT_NULL(vkDestroyBuffer); + VMA_COPY_IF_NOT_NULL(vkCreateImage); + VMA_COPY_IF_NOT_NULL(vkDestroyImage); + VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer); + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR); +#endif + +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR); + VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR); +#endif + +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR); +#endif + +#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000 + VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements); +#endif + +#undef VMA_COPY_IF_NOT_NULL +} + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Dynamic() +{ + VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr && + "To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass " + "VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. " + "Other members can be null."); + +#define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \ + if(m_VulkanFunctions.memberName == VMA_NULL) \ + m_VulkanFunctions.memberName = \ + (functionPointerType)m_VulkanFunctions.vkGetInstanceProcAddr(m_hInstance, functionNameString); +#define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \ + if(m_VulkanFunctions.memberName == VMA_NULL) \ + m_VulkanFunctions.memberName = \ + (functionPointerType)m_VulkanFunctions.vkGetDeviceProcAddr(m_hDevice, functionNameString); + + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties"); + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties"); + VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory"); + VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory"); + VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory"); + VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory"); + VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges"); + VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges"); + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory"); + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer"); + VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer"); + VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage"); + VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage"); + VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer"); + +#if VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2"); + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2"); + } +#endif + +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2"); + } + else if(m_UseExtMemoryBudget) + { + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR"); + } +#endif + +#if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR"); + } +#endif + +#if VMA_BIND_MEMORY2 + if(m_UseKhrBindMemory2) + { + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR"); + } +#endif // #if VMA_BIND_MEMORY2 + +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2"); + } + else if(m_UseExtMemoryBudget) + { + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR"); + } +#endif // #if VMA_MEMORY_BUDGET + +#if VMA_VULKAN_VERSION >= 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements"); + } +#endif +#if VMA_KHR_MAINTENANCE4 + if(m_UseKhrMaintenance4) + { + VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirementsKHR, "vkGetDeviceBufferMemoryRequirementsKHR"); + VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR"); + } +#endif + +#undef VMA_FETCH_DEVICE_FUNC +#undef VMA_FETCH_INSTANCE_FUNC +} + +#endif // VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ValidateVulkanFunctions() +{ + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL); + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation) + { + VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL); + } +#endif + +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2) + { + VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL); + } +#endif + +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL); + } +#endif + + // Not validating these due to suspected driver bugs with these function + // pointers being null despite correct extension or Vulkan version is enabled. + // See issue #397. Their usage in VMA is optional anyway. + // + // VMA_ASSERT(m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements != VMA_NULL); + // VMA_ASSERT(m_VulkanFunctions.vkGetDeviceImageMemoryRequirements != VMA_NULL); +} + +VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex) +{ + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; + const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE; + return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32); +} + +VkResult VmaAllocator_T::AllocateMemoryOfType( + VmaPool pool, + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedPreferred, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VmaBufferImageUsage dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + uint32_t memTypeIndex, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + VmaBlockVector& blockVector, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + VMA_ASSERT(pAllocations != VMA_NULL); + VMA_DEBUG_LOG_FORMAT(" AllocateMemory: MemoryTypeIndex=%" PRIu32 ", AllocationCount=%zu, Size=%" PRIu64, memTypeIndex, allocationCount, size); + + VmaAllocationCreateInfo finalCreateInfo = createInfo; + VkResult res = CalcMemTypeParams( + finalCreateInfo, + memTypeIndex, + size, + allocationCount); + if(res != VK_SUCCESS) + return res; + + if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) + { + return AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + } + else + { + const bool canAllocateDedicated = + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 && + (pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize()); + + if(canAllocateDedicated) + { + // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size. + if(size > blockVector.GetPreferredBlockSize() / 2) + { + dedicatedPreferred = true; + } + // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget, + // which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above + // 3/4 of the maximum allocation count. + if(m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount < UINT32_MAX / 4 && + m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4) + { + dedicatedPreferred = false; + } + + if(dedicatedPreferred) + { + res = AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + if(res == VK_SUCCESS) + { + // Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here. + VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); + return VK_SUCCESS; + } + } + } + + res = blockVector.Allocate( + size, + alignment, + finalCreateInfo, + suballocType, + allocationCount, + pAllocations); + if(res == VK_SUCCESS) + return VK_SUCCESS; + + // Try dedicated memory. + if(canAllocateDedicated && !dedicatedPreferred) + { + res = AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + if(res == VK_SUCCESS) + { + // Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here. + VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); + return VK_SUCCESS; + } + } + // Everything failed: Return error code. + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; + } +} + +VkResult VmaAllocator_T::AllocateDedicatedMemory( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + uint32_t memTypeIndex, + bool map, + bool isUserDataString, + bool isMappingAllowed, + bool canAliasMemory, + void* pUserData, + float priority, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VmaBufferImageUsage dedicatedBufferImageUsage, + size_t allocationCount, + VmaAllocation* pAllocations, + const void* pNextChain) +{ + VMA_ASSERT(allocationCount > 0 && pAllocations); + + VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocInfo.memoryTypeIndex = memTypeIndex; + allocInfo.allocationSize = size; + allocInfo.pNext = pNextChain; + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR }; + if(!canAliasMemory) + { + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + if(dedicatedBuffer != VK_NULL_HANDLE) + { + VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE); + dedicatedAllocInfo.buffer = dedicatedBuffer; + VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo); + } + else if(dedicatedImage != VK_NULL_HANDLE) + { + dedicatedAllocInfo.image = dedicatedImage; + VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo); + } + } + } +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + +#if VMA_BUFFER_DEVICE_ADDRESS + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR }; + if(m_UseKhrBufferDeviceAddress) + { + bool canContainBufferWithDeviceAddress = true; + if(dedicatedBuffer != VK_NULL_HANDLE) + { + canContainBufferWithDeviceAddress = dedicatedBufferImageUsage == VmaBufferImageUsage::UNKNOWN || + dedicatedBufferImageUsage.Contains(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT); + } + else if(dedicatedImage != VK_NULL_HANDLE) + { + canContainBufferWithDeviceAddress = false; + } + if(canContainBufferWithDeviceAddress) + { + allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo); + } + } +#endif // #if VMA_BUFFER_DEVICE_ADDRESS + +#if VMA_MEMORY_PRIORITY + VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; + if(m_UseExtMemoryPriority) + { + VMA_ASSERT(priority >= 0.f && priority <= 1.f); + priorityInfo.priority = priority; + VmaPnextChainPushFront(&allocInfo, &priorityInfo); + } +#endif // #if VMA_MEMORY_PRIORITY + +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex); + if(exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // #if VMA_EXTERNAL_MEMORY + + size_t allocIndex; + VkResult res = VK_SUCCESS; + for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocateDedicatedMemoryPage( + pool, + size, + suballocType, + memTypeIndex, + allocInfo, + map, + isUserDataString, + isMappingAllowed, + pUserData, + pAllocations + allocIndex); + if(res != VK_SUCCESS) + { + break; + } + } + + if(res == VK_SUCCESS) + { + for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + dedicatedAllocations.Register(pAllocations[allocIndex]); + } + VMA_DEBUG_LOG_FORMAT(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%" PRIu32, allocationCount, memTypeIndex); + } + else + { + // Free all already created allocations. + while(allocIndex--) + { + VmaAllocation currAlloc = pAllocations[allocIndex]; + VkDeviceMemory hMemory = currAlloc->GetMemory(); + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + + if(currAlloc->GetMappedData() != VMA_NULL) + { + (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); + } + */ + + FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory); + m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize()); + m_AllocationObjectAllocator.Free(currAlloc); + } + + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaAllocator_T::AllocateDedicatedMemoryPage( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + bool isMappingAllowed, + void* pUserData, + VmaAllocation* pAllocation) +{ + VkDeviceMemory hMemory = VK_NULL_HANDLE; + VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory); + if(res < 0) + { + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; + } + + void* pMappedData = VMA_NULL; + if(map) + { + res = (*m_VulkanFunctions.vkMapMemory)( + m_hDevice, + hMemory, + 0, + VK_WHOLE_SIZE, + 0, + &pMappedData); + if(res < 0) + { + VMA_DEBUG_LOG(" vkMapMemory FAILED"); + FreeVulkanMemory(memTypeIndex, size, hMemory); + return res; + } + } + + *pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed); + (*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size); + if (isUserDataString) + (*pAllocation)->SetName(this, (const char*)pUserData); + else + (*pAllocation)->SetUserData(this, pUserData); + m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size); + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + + return VK_SUCCESS; +} + +void VmaAllocator_T::GetBufferMemoryRequirements( + VkBuffer hBuffer, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const +{ +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR }; + memReqInfo.buffer = hBuffer; + + VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; + + VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; + VmaPnextChainPushFront(&memReq2, &memDedicatedReq); + + (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); + + memReq = memReq2.memoryRequirements; + requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); + prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); + } + else +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + { + (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq); + requiresDedicatedAllocation = false; + prefersDedicatedAllocation = false; + } +} + +void VmaAllocator_T::GetImageMemoryRequirements( + VkImage hImage, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const +{ +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR }; + memReqInfo.image = hImage; + + VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; + + VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; + VmaPnextChainPushFront(&memReq2, &memDedicatedReq); + + (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); + + memReq = memReq2.memoryRequirements; + requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); + prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); + } + else +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + { + (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq); + requiresDedicatedAllocation = false; + prefersDedicatedAllocation = false; + } +} + +VkResult VmaAllocator_T::FindMemoryTypeIndex( + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VmaBufferImageUsage bufImgUsage, + uint32_t* pMemoryTypeIndex) const +{ + memoryTypeBits &= GetGlobalMemoryTypeBits(); + + if(pAllocationCreateInfo->memoryTypeBits != 0) + { + memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits; + } + + VkMemoryPropertyFlags requiredFlags = 0, preferredFlags = 0, notPreferredFlags = 0; + if(!FindMemoryPreferences( + IsIntegratedGpu(), + *pAllocationCreateInfo, + bufImgUsage, + requiredFlags, preferredFlags, notPreferredFlags)) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + *pMemoryTypeIndex = UINT32_MAX; + uint32_t minCost = UINT32_MAX; + for(uint32_t memTypeIndex = 0, memTypeBit = 1; + memTypeIndex < GetMemoryTypeCount(); + ++memTypeIndex, memTypeBit <<= 1) + { + // This memory type is acceptable according to memoryTypeBits bitmask. + if((memTypeBit & memoryTypeBits) != 0) + { + const VkMemoryPropertyFlags currFlags = + m_MemProps.memoryTypes[memTypeIndex].propertyFlags; + // This memory type contains requiredFlags. + if((requiredFlags & ~currFlags) == 0) + { + // Calculate cost as number of bits from preferredFlags not present in this memory type. + uint32_t currCost = VMA_COUNT_BITS_SET(preferredFlags & ~currFlags) + + VMA_COUNT_BITS_SET(currFlags & notPreferredFlags); + // Remember memory type with lowest cost. + if(currCost < minCost) + { + *pMemoryTypeIndex = memTypeIndex; + if(currCost == 0) + { + return VK_SUCCESS; + } + minCost = currCost; + } + } + } + } + return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT; +} + +VkResult VmaAllocator_T::CalcMemTypeParams( + VmaAllocationCreateInfo& inoutCreateInfo, + uint32_t memTypeIndex, + VkDeviceSize size, + size_t allocationCount) +{ + // If memory type is not HOST_VISIBLE, disable MAPPED. + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && + (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0) + { + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + VmaBudget heapBudget = {}; + GetHeapBudgets(&heapBudget, heapIndex, 1); + if(heapBudget.usage + size * allocationCount > heapBudget.budget) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + return VK_SUCCESS; +} + +VkResult VmaAllocator_T::CalcAllocationParams( + VmaAllocationCreateInfo& inoutCreateInfo, + bool dedicatedRequired, + bool dedicatedPreferred) +{ + VMA_ASSERT((inoutCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) && + "Specifying both flags VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT and VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT is incorrect."); + VMA_ASSERT((((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) == 0 || + (inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0)) && + "Specifying VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT requires also VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT."); + if(inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST) + { + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0) + { + VMA_ASSERT((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0 && + "When using VMA_ALLOCATION_CREATE_MAPPED_BIT and usage = VMA_MEMORY_USAGE_AUTO*, you must also specify VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT."); + } + } + + // If memory is lazily allocated, it should be always dedicated. + if(dedicatedRequired || + inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + if(inoutCreateInfo.pool != VK_NULL_HANDLE) + { + if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations."); + return VK_ERROR_FEATURE_NOT_PRESENT; + } + inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority(); + } + + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense."); + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + // Non-auto USAGE values imply HOST_ACCESS flags. + // And so does VMA_MEMORY_USAGE_UNKNOWN because it is used with custom pools. + // Which specific flag is used doesn't matter. They change things only when used with VMA_MEMORY_USAGE_AUTO*. + // Otherwise they just protect from assert on mapping. + if(inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO && + inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE && + inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_HOST) + { + if((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) == 0) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + } + } + + return VK_SUCCESS; +} + +VkResult VmaAllocator_T::AllocateMemory( + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VmaBufferImageUsage dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + + VMA_ASSERT(VmaIsPow2(vkMemReq.alignment)); + + if(vkMemReq.size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VmaAllocationCreateInfo createInfoFinal = createInfo; + VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation); + if(res != VK_SUCCESS) + return res; + + if(createInfoFinal.pool != VK_NULL_HANDLE) + { + VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector; + return AllocateMemoryOfType( + createInfoFinal.pool, + vkMemReq.size, + vkMemReq.alignment, + prefersDedicatedAllocation, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + createInfoFinal, + blockVector.GetMemoryTypeIndex(), + suballocType, + createInfoFinal.pool->m_DedicatedAllocations, + blockVector, + allocationCount, + pAllocations); + } + else + { + // Bit mask of memory Vulkan types acceptable for this allocation. + uint32_t memoryTypeBits = vkMemReq.memoryTypeBits; + uint32_t memTypeIndex = UINT32_MAX; + res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex); + // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT. + if(res != VK_SUCCESS) + return res; + do + { + VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(blockVector && "Trying to use unsupported memory type!"); + res = AllocateMemoryOfType( + VK_NULL_HANDLE, + vkMemReq.size, + vkMemReq.alignment, + requiresDedicatedAllocation || prefersDedicatedAllocation, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + createInfoFinal, + memTypeIndex, + suballocType, + m_DedicatedAllocations[memTypeIndex], + *blockVector, + allocationCount, + pAllocations); + // Allocation succeeded + if(res == VK_SUCCESS) + return VK_SUCCESS; + + // Remove old memTypeIndex from list of possibilities. + memoryTypeBits &= ~(1u << memTypeIndex); + // Find alternative memTypeIndex. + res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex); + } while(res == VK_SUCCESS); + + // No other matching memory type index could be found. + // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } +} + +void VmaAllocator_T::FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations) +{ + VMA_ASSERT(pAllocations); + + for(size_t allocIndex = allocationCount; allocIndex--; ) + { + VmaAllocation allocation = pAllocations[allocIndex]; + + if(allocation != VK_NULL_HANDLE) + { + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED); + } + + allocation->FreeName(this); + + switch(allocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaBlockVector* pBlockVector = VMA_NULL; + VmaPool hPool = allocation->GetParentPool(); + if(hPool != VK_NULL_HANDLE) + { + pBlockVector = &hPool->m_BlockVector; + } + else + { + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + pBlockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!"); + } + pBlockVector->Free(allocation); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + FreeDedicatedMemory(allocation); + break; + default: + VMA_ASSERT(0); + } + } + } +} + +void VmaAllocator_T::CalculateStatistics(VmaTotalStatistics* pStats) +{ + // Initialize. + VmaClearDetailedStatistics(pStats->total); + for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) + VmaClearDetailedStatistics(pStats->memoryType[i]); + for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i) + VmaClearDetailedStatistics(pStats->memoryHeap[i]); + + // Process default pools. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; + if (pBlockVector != VMA_NULL) + pBlockVector->AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + } + + // Process custom pools. + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + VmaBlockVector& blockVector = pool->m_BlockVector; + const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex(); + blockVector.AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + pool->m_DedicatedAllocations.AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + } + } + + // Process dedicated allocations. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + m_DedicatedAllocations[memTypeIndex].AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + } + + // Sum from memory types to memory heaps. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + const uint32_t memHeapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex; + VmaAddDetailedStatistics(pStats->memoryHeap[memHeapIndex], pStats->memoryType[memTypeIndex]); + } + + // Sum from memory heaps to total. + for(uint32_t memHeapIndex = 0; memHeapIndex < GetMemoryHeapCount(); ++memHeapIndex) + VmaAddDetailedStatistics(pStats->total, pStats->memoryHeap[memHeapIndex]); + + VMA_ASSERT(pStats->total.statistics.allocationCount == 0 || + pStats->total.allocationSizeMax >= pStats->total.allocationSizeMin); + VMA_ASSERT(pStats->total.unusedRangeCount == 0 || + pStats->total.unusedRangeSizeMax >= pStats->total.unusedRangeSizeMin); +} + +void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount) +{ +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + if(m_Budget.m_OperationsSinceBudgetFetch < 30) + { + VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex); + for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets) + { + const uint32_t heapIndex = firstHeap + i; + + outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex]; + outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex]; + outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex]; + outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex]; + + if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->statistics.blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]) + { + outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] + + outBudgets->statistics.blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]; + } + else + { + outBudgets->usage = 0; + } + + // Have to take MIN with heap size because explicit HeapSizeLimit is included in it. + outBudgets->budget = VMA_MIN( + m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size); + } + } + else + { + UpdateVulkanBudget(); // Outside of mutex lock + GetHeapBudgets(outBudgets, firstHeap, heapCount); // Recursion + } + } + else +#endif + { + for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets) + { + const uint32_t heapIndex = firstHeap + i; + + outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex]; + outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex]; + outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex]; + outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex]; + + outBudgets->usage = outBudgets->statistics.blockBytes; + outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics. + } + } +} + +void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo) +{ + pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); + pAllocationInfo->deviceMemory = hAllocation->GetMemory(); + pAllocationInfo->offset = hAllocation->GetOffset(); + pAllocationInfo->size = hAllocation->GetSize(); + pAllocationInfo->pMappedData = hAllocation->GetMappedData(); + pAllocationInfo->pUserData = hAllocation->GetUserData(); + pAllocationInfo->pName = hAllocation->GetName(); +} + +void VmaAllocator_T::GetAllocationInfo2(VmaAllocation hAllocation, VmaAllocationInfo2* pAllocationInfo) +{ + GetAllocationInfo(hAllocation, &pAllocationInfo->allocationInfo); + + switch (hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + pAllocationInfo->blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize(); + pAllocationInfo->dedicatedMemory = VK_FALSE; + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + pAllocationInfo->blockSize = pAllocationInfo->allocationInfo.size; + pAllocationInfo->dedicatedMemory = VK_TRUE; + break; + default: + VMA_ASSERT(0); + } +} + +VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool) +{ + VMA_DEBUG_LOG_FORMAT(" CreatePool: MemoryTypeIndex=%" PRIu32 ", flags=%" PRIu32, pCreateInfo->memoryTypeIndex, pCreateInfo->flags); + + VmaPoolCreateInfo newCreateInfo = *pCreateInfo; + + // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash. + if(pCreateInfo->pMemoryAllocateNext) + { + VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0); + } + + if(newCreateInfo.maxBlockCount == 0) + { + newCreateInfo.maxBlockCount = SIZE_MAX; + } + if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + // Memory type index out of range or forbidden. + if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() || + ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + if(newCreateInfo.minAllocationAlignment > 0) + { + VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment)); + } + + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex); + + *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize); + + VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks(); + if(res != VK_SUCCESS) + { + vma_delete(this, *pPool); + *pPool = VMA_NULL; + return res; + } + + // Add to m_Pools. + { + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + (*pPool)->SetId(m_NextPoolId++); + m_Pools.PushBack(*pPool); + } + + return VK_SUCCESS; +} + +void VmaAllocator_T::DestroyPool(VmaPool pool) +{ + // Remove from m_Pools. + { + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + m_Pools.Remove(pool); + } + + vma_delete(this, pool); +} + +void VmaAllocator_T::GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats) +{ + VmaClearStatistics(*pPoolStats); + pool->m_BlockVector.AddStatistics(*pPoolStats); + pool->m_DedicatedAllocations.AddStatistics(*pPoolStats); +} + +void VmaAllocator_T::CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats) +{ + VmaClearDetailedStatistics(*pPoolStats); + pool->m_BlockVector.AddDetailedStatistics(*pPoolStats); + pool->m_DedicatedAllocations.AddDetailedStatistics(*pPoolStats); +} + +void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex) +{ + m_CurrentFrameIndex.store(frameIndex); + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + UpdateVulkanBudget(); + } +#endif // #if VMA_MEMORY_BUDGET +} + +VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool) +{ + return hPool->m_BlockVector.CheckCorruption(); +} + +VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits) +{ + VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT; + + // Process default pools. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; + if(pBlockVector != VMA_NULL) + { + VkResult localRes = pBlockVector->CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + + // Process custom pools. + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0) + { + VkResult localRes = pool->m_BlockVector.CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + } + + return finalRes; +} + +VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory) +{ + AtomicTransactionalIncrement deviceMemoryCountIncrement; + const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount); +#if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount) + { + return VK_ERROR_TOO_MANY_OBJECTS; + } +#endif + + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex); + + // HeapSizeLimit is in effect for this heap. + if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0) + { + const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; + VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex]; + for(;;) + { + const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize; + if(blockBytesAfterAllocation > heapSize) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation)) + { + break; + } + } + } + else + { + m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize; + } + ++m_Budget.m_BlockCount[heapIndex]; + + // VULKAN CALL vkAllocateMemory. + VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); + + if(res == VK_SUCCESS) + { +#if VMA_MEMORY_BUDGET + ++m_Budget.m_OperationsSinceBudgetFetch; +#endif + + // Informative callback. + if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData); + } + + deviceMemoryCountIncrement.Commit(); + } + else + { + --m_Budget.m_BlockCount[heapIndex]; + m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize; + } + + return res; +} + +void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory) +{ + // Informative callback. + if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData); + } + + // VULKAN CALL vkFreeMemory. + (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks()); + + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType); + --m_Budget.m_BlockCount[heapIndex]; + m_Budget.m_BlockBytes[heapIndex] -= size; + + --m_DeviceMemoryCount; +} + +VkResult VmaAllocator_T::BindVulkanBuffer( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkBuffer buffer, + const void* pNext) +{ + if(pNext != VMA_NULL) + { +#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) && + m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL) + { + VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR }; + bindBufferMemoryInfo.pNext = pNext; + bindBufferMemoryInfo.buffer = buffer; + bindBufferMemoryInfo.memory = memory; + bindBufferMemoryInfo.memoryOffset = memoryOffset; + return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo); + } + else +#endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + else + { + return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset); + } +} + +VkResult VmaAllocator_T::BindVulkanImage( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkImage image, + const void* pNext) +{ + if(pNext != VMA_NULL) + { +#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) && + m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL) + { + VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR }; + bindBufferMemoryInfo.pNext = pNext; + bindBufferMemoryInfo.image = image; + bindBufferMemoryInfo.memory = memory; + bindBufferMemoryInfo.memoryOffset = memoryOffset; + return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo); + } + else +#endif // #if VMA_BIND_MEMORY2 + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + else + { + return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset); + } +} + +VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData) +{ + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + char *pBytes = VMA_NULL; + VkResult res = pBlock->Map(this, 1, (void**)&pBytes); + if(res == VK_SUCCESS) + { + *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset(); + hAllocation->BlockAllocMap(); + } + return res; + } + VMA_FALLTHROUGH; // Fallthrough + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + return hAllocation->DedicatedAllocMap(this, ppData); + default: + VMA_ASSERT(0); + return VK_ERROR_MEMORY_MAP_FAILED; + } +} + +void VmaAllocator_T::Unmap(VmaAllocation hAllocation) +{ + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + hAllocation->BlockAllocUnmap(); + pBlock->Unmap(this, 1); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + hAllocation->DedicatedAllocUnmap(this); + break; + default: + VMA_ASSERT(0); + } +} + +VkResult VmaAllocator_T::BindBufferMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext) +{ + VkResult res = VK_ERROR_UNKNOWN_COPY; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext); + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block."); + res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +VkResult VmaAllocator_T::BindImageMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext) +{ + VkResult res = VK_ERROR_UNKNOWN_COPY; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext); + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block."); + res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +VkResult VmaAllocator_T::FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op) +{ + VkResult res = VK_SUCCESS; + + VkMappedMemoryRange memRange = {}; + if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange)) + { + switch(op) + { + case VMA_CACHE_FLUSH: + res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + case VMA_CACHE_INVALIDATE: + res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + default: + VMA_ASSERT(0); + } + } + // else: Just ignore this call. + return res; +} + +VkResult VmaAllocator_T::FlushOrInvalidateAllocations( + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, const VkDeviceSize* sizes, + VMA_CACHE_OPERATION op) +{ + typedef VmaStlAllocator RangeAllocator; + typedef VmaSmallVector RangeVector; + RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks())); + + for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + const VmaAllocation alloc = allocations[allocIndex]; + const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0; + const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE; + VkMappedMemoryRange newRange; + if(GetFlushOrInvalidateRange(alloc, offset, size, newRange)) + { + ranges.push_back(newRange); + } + } + + VkResult res = VK_SUCCESS; + if(!ranges.empty()) + { + switch(op) + { + case VMA_CACHE_FLUSH: + res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data()); + break; + case VMA_CACHE_INVALIDATE: + res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data()); + break; + default: + VMA_ASSERT(0); + } + } + // else: Just ignore this call. + return res; +} + +VkResult VmaAllocator_T::CopyMemoryToAllocation( + const void* pSrcHostPointer, + VmaAllocation dstAllocation, + VkDeviceSize dstAllocationLocalOffset, + VkDeviceSize size) +{ + void* dstMappedData = VMA_NULL; + VkResult res = Map(dstAllocation, &dstMappedData); + if(res == VK_SUCCESS) + { + memcpy((char*)dstMappedData + dstAllocationLocalOffset, pSrcHostPointer, (size_t)size); + Unmap(dstAllocation); + res = FlushOrInvalidateAllocation(dstAllocation, dstAllocationLocalOffset, size, VMA_CACHE_FLUSH); + } + return res; +} + +VkResult VmaAllocator_T::CopyAllocationToMemory( + VmaAllocation srcAllocation, + VkDeviceSize srcAllocationLocalOffset, + void* pDstHostPointer, + VkDeviceSize size) +{ + void* srcMappedData = VMA_NULL; + VkResult res = Map(srcAllocation, &srcMappedData); + if(res == VK_SUCCESS) + { + res = FlushOrInvalidateAllocation(srcAllocation, srcAllocationLocalOffset, size, VMA_CACHE_INVALIDATE); + if(res == VK_SUCCESS) + { + memcpy(pDstHostPointer, (const char*)srcMappedData + srcAllocationLocalOffset, (size_t)size); + Unmap(srcAllocation); + } + } + return res; +} + +void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation) +{ + VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + VmaPool parentPool = allocation->GetParentPool(); + if(parentPool == VK_NULL_HANDLE) + { + // Default pool + m_DedicatedAllocations[memTypeIndex].Unregister(allocation); + } + else + { + // Custom pool + parentPool->m_DedicatedAllocations.Unregister(allocation); + } + + VkDeviceMemory hMemory = allocation->GetMemory(); + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + + if(allocation->GetMappedData() != VMA_NULL) + { + (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); + } + */ + + FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory); + + m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize()); + m_AllocationObjectAllocator.Free(allocation); + + VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%" PRIu32, memTypeIndex); +} + +uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const +{ + VkBufferCreateInfo dummyBufCreateInfo; + VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo); + + uint32_t memoryTypeBits = 0; + + // Create buffer. + VkBuffer buf = VK_NULL_HANDLE; + VkResult res = (*GetVulkanFunctions().vkCreateBuffer)( + m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf); + if(res == VK_SUCCESS) + { + // Query for supported memory types. + VkMemoryRequirements memReq; + (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq); + memoryTypeBits = memReq.memoryTypeBits; + + // Destroy buffer. + (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks()); + } + + return memoryTypeBits; +} + +uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const +{ + // Make sure memory information is already fetched. + VMA_ASSERT(GetMemoryTypeCount() > 0); + + uint32_t memoryTypeBits = UINT32_MAX; + + if(!m_UseAmdDeviceCoherentMemory) + { + // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0) + { + memoryTypeBits &= ~(1u << memTypeIndex); + } + } + } + + return memoryTypeBits; +} + +bool VmaAllocator_T::GetFlushOrInvalidateRange( + VmaAllocation allocation, + VkDeviceSize offset, VkDeviceSize size, + VkMappedMemoryRange& outRange) const +{ + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex)) + { + const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; + const VkDeviceSize allocationSize = allocation->GetSize(); + VMA_ASSERT(offset <= allocationSize); + + outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + outRange.pNext = VMA_NULL; + outRange.memory = allocation->GetMemory(); + + switch(allocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + outRange.size = allocationSize - outRange.offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + outRange.size = VMA_MIN( + VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize), + allocationSize - outRange.offset); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + // 1. Still within this allocation. + outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + size = allocationSize - offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + } + outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize); + + // 2. Adjust to whole block. + const VkDeviceSize allocationOffset = allocation->GetOffset(); + VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0); + const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize(); + outRange.offset += allocationOffset; + outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset); + + break; + } + default: + VMA_ASSERT(0); + } + return true; + } + return false; +} + +#if VMA_MEMORY_BUDGET +void VmaAllocator_T::UpdateVulkanBudget() +{ + VMA_ASSERT(m_UseExtMemoryBudget); + + VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR }; + + VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT }; + VmaPnextChainPushFront(&memProps, &budgetProps); + + GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps); + + { + VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex); + + for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex) + { + m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex]; + m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex]; + m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load(); + + // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size. + if(m_Budget.m_VulkanBudget[heapIndex] == 0) + { + m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics. + } + else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size) + { + m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size; + } + if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0) + { + m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]; + } + } + m_Budget.m_OperationsSinceBudgetFetch = 0; + } +} +#endif // VMA_MEMORY_BUDGET + +void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern) +{ + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS && + hAllocation->IsMappingAllowed() && + (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + void* pData = VMA_NULL; + VkResult res = Map(hAllocation, &pData); + if(res == VK_SUCCESS) + { + memset(pData, (int)pattern, (size_t)hAllocation->GetSize()); + FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH); + Unmap(hAllocation); + } + else + { + VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation."); + } + } +} + +uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits() +{ + uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load(); + if(memoryTypeBits == UINT32_MAX) + { + memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits(); + m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits); + } + return memoryTypeBits; +} + +#if VMA_STATS_STRING_ENABLED +void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) +{ + json.WriteString("DefaultPools"); + json.BeginObject(); + { + for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex]; + VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex]; + if (pBlockVector != VMA_NULL) + { + json.BeginString("Type "); + json.ContinueString(memTypeIndex); + json.EndString(); + json.BeginObject(); + { + json.WriteString("PreferredBlockSize"); + json.WriteNumber(pBlockVector->GetPreferredBlockSize()); + + json.WriteString("Blocks"); + pBlockVector->PrintDetailedMap(json); + + json.WriteString("DedicatedAllocations"); + dedicatedAllocList.BuildStatsString(json); + } + json.EndObject(); + } + } + } + json.EndObject(); + + json.WriteString("CustomPools"); + json.BeginObject(); + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + if (!m_Pools.IsEmpty()) + { + for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + bool displayType = true; + size_t index = 0; + for (VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + VmaBlockVector& blockVector = pool->m_BlockVector; + if (blockVector.GetMemoryTypeIndex() == memTypeIndex) + { + if (displayType) + { + json.BeginString("Type "); + json.ContinueString(memTypeIndex); + json.EndString(); + json.BeginArray(); + displayType = false; + } + + json.BeginObject(); + { + json.WriteString("Name"); + json.BeginString(); + json.ContinueString((uint64_t)index++); + if (pool->GetName()) + { + json.ContinueString(" - "); + json.ContinueString(pool->GetName()); + } + json.EndString(); + + json.WriteString("PreferredBlockSize"); + json.WriteNumber(blockVector.GetPreferredBlockSize()); + + json.WriteString("Blocks"); + blockVector.PrintDetailedMap(json); + + json.WriteString("DedicatedAllocations"); + pool->m_DedicatedAllocations.BuildStatsString(json); + } + json.EndObject(); + } + } + + if (!displayType) + json.EndArray(); + } + } + } + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_ALLOCATOR_T_FUNCTIONS + + +#ifndef _VMA_PUBLIC_INTERFACE +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator( + const VmaAllocatorCreateInfo* pCreateInfo, + VmaAllocator* pAllocator) +{ + VMA_ASSERT(pCreateInfo && pAllocator); + VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 || + (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3)); + VMA_DEBUG_LOG("vmaCreateAllocator"); + *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo); + VkResult result = (*pAllocator)->Init(pCreateInfo); + if(result < 0) + { + vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator); + *pAllocator = VK_NULL_HANDLE; + } + return result; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator( + VmaAllocator allocator) +{ + if(allocator != VK_NULL_HANDLE) + { + VMA_DEBUG_LOG("vmaDestroyAllocator"); + VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying. + vma_delete(&allocationCallbacks, allocator); + } +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo) +{ + VMA_ASSERT(allocator && pAllocatorInfo); + pAllocatorInfo->instance = allocator->m_hInstance; + pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice(); + pAllocatorInfo->device = allocator->m_hDevice; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties( + VmaAllocator allocator, + const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties) +{ + VMA_ASSERT(allocator && ppPhysicalDeviceProperties); + *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties( + VmaAllocator allocator, + const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties) +{ + VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties); + *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties( + VmaAllocator allocator, + uint32_t memoryTypeIndex, + VkMemoryPropertyFlags* pFlags) +{ + VMA_ASSERT(allocator && pFlags); + VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount()); + *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex( + VmaAllocator allocator, + uint32_t frameIndex) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->SetCurrentFrameIndex(frameIndex); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics( + VmaAllocator allocator, + VmaTotalStatistics* pStats) +{ + VMA_ASSERT(allocator && pStats); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + allocator->CalculateStatistics(pStats); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets( + VmaAllocator allocator, + VmaBudget* pBudgets) +{ + VMA_ASSERT(allocator && pBudgets); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount()); +} + +#if VMA_STATS_STRING_ENABLED + +VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString( + VmaAllocator allocator, + char** ppStatsString, + VkBool32 detailedMap) +{ + VMA_ASSERT(allocator && ppStatsString); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VmaStringBuilder sb(allocator->GetAllocationCallbacks()); + { + VmaBudget budgets[VK_MAX_MEMORY_HEAPS]; + allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount()); + + VmaTotalStatistics stats; + allocator->CalculateStatistics(&stats); + + VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb); + json.BeginObject(); + { + json.WriteString("General"); + json.BeginObject(); + { + const VkPhysicalDeviceProperties& deviceProperties = allocator->m_PhysicalDeviceProperties; + const VkPhysicalDeviceMemoryProperties& memoryProperties = allocator->m_MemProps; + + json.WriteString("API"); + json.WriteString("Vulkan"); + + json.WriteString("apiVersion"); + json.BeginString(); + json.ContinueString(VK_VERSION_MAJOR(deviceProperties.apiVersion)); + json.ContinueString("."); + json.ContinueString(VK_VERSION_MINOR(deviceProperties.apiVersion)); + json.ContinueString("."); + json.ContinueString(VK_VERSION_PATCH(deviceProperties.apiVersion)); + json.EndString(); + + json.WriteString("GPU"); + json.WriteString(deviceProperties.deviceName); + json.WriteString("deviceType"); + json.WriteNumber(static_cast(deviceProperties.deviceType)); + + json.WriteString("maxMemoryAllocationCount"); + json.WriteNumber(deviceProperties.limits.maxMemoryAllocationCount); + json.WriteString("bufferImageGranularity"); + json.WriteNumber(deviceProperties.limits.bufferImageGranularity); + json.WriteString("nonCoherentAtomSize"); + json.WriteNumber(deviceProperties.limits.nonCoherentAtomSize); + + json.WriteString("memoryHeapCount"); + json.WriteNumber(memoryProperties.memoryHeapCount); + json.WriteString("memoryTypeCount"); + json.WriteNumber(memoryProperties.memoryTypeCount); + } + json.EndObject(); + } + { + json.WriteString("Total"); + VmaPrintDetailedStatistics(json, stats.total); + } + { + json.WriteString("MemoryInfo"); + json.BeginObject(); + { + for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex) + { + json.BeginString("Heap "); + json.ContinueString(heapIndex); + json.EndString(); + json.BeginObject(); + { + const VkMemoryHeap& heapInfo = allocator->m_MemProps.memoryHeaps[heapIndex]; + json.WriteString("Flags"); + json.BeginArray(true); + { + if (heapInfo.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) + json.WriteString("DEVICE_LOCAL"); + #if VMA_VULKAN_VERSION >= 1001000 + if (heapInfo.flags & VK_MEMORY_HEAP_MULTI_INSTANCE_BIT) + json.WriteString("MULTI_INSTANCE"); + #endif + + VkMemoryHeapFlags flags = heapInfo.flags & + ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT + #if VMA_VULKAN_VERSION >= 1001000 + | VK_MEMORY_HEAP_MULTI_INSTANCE_BIT + #endif + ); + if (flags != 0) + json.WriteNumber(flags); + } + json.EndArray(); + + json.WriteString("Size"); + json.WriteNumber(heapInfo.size); + + json.WriteString("Budget"); + json.BeginObject(); + { + json.WriteString("BudgetBytes"); + json.WriteNumber(budgets[heapIndex].budget); + json.WriteString("UsageBytes"); + json.WriteNumber(budgets[heapIndex].usage); + } + json.EndObject(); + + json.WriteString("Stats"); + VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]); + + json.WriteString("MemoryPools"); + json.BeginObject(); + { + for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex) + { + if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex) + { + json.BeginString("Type "); + json.ContinueString(typeIndex); + json.EndString(); + json.BeginObject(); + { + json.WriteString("Flags"); + json.BeginArray(true); + { + VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags; + if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + json.WriteString("DEVICE_LOCAL"); + if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + json.WriteString("HOST_VISIBLE"); + if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) + json.WriteString("HOST_COHERENT"); + if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) + json.WriteString("HOST_CACHED"); + if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) + json.WriteString("LAZILY_ALLOCATED"); + #if VMA_VULKAN_VERSION >= 1001000 + if (flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) + json.WriteString("PROTECTED"); + #endif + #if VK_AMD_device_coherent_memory + if (flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) + json.WriteString("DEVICE_COHERENT_AMD"); + if (flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) + json.WriteString("DEVICE_UNCACHED_AMD"); + #endif + + flags &= ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + #if VMA_VULKAN_VERSION >= 1001000 + | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT + #endif + #if VK_AMD_device_coherent_memory + | VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY + | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY + #endif + | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT + | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + | VK_MEMORY_PROPERTY_HOST_CACHED_BIT); + if (flags != 0) + json.WriteNumber(flags); + } + json.EndArray(); + + json.WriteString("Stats"); + VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]); + } + json.EndObject(); + } + } + + } + json.EndObject(); + } + json.EndObject(); + } + } + json.EndObject(); + } + + if (detailedMap == VK_TRUE) + allocator->PrintDetailedMap(json); + + json.EndObject(); + } + + *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength()); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString( + VmaAllocator allocator, + char* pStatsString) +{ + if(pStatsString != VMA_NULL) + { + VMA_ASSERT(allocator); + VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString); + } +} + +#endif // VMA_STATS_STRING_ENABLED + +/* +This function is not protected by any mutex because it just reads immutable data. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex( + VmaAllocator allocator, + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + return allocator->FindMemoryTypeIndex(memoryTypeBits, pAllocationCreateInfo, VmaBufferImageUsage::UNKNOWN, pMemoryTypeIndex); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pBufferCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions(); + VkResult res; + +#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000 + if(funcs->vkGetDeviceBufferMemoryRequirements) + { + // Can query straight from VkBufferCreateInfo :) + VkDeviceBufferMemoryRequirementsKHR devBufMemReq = {VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS_KHR}; + devBufMemReq.pCreateInfo = pBufferCreateInfo; + + VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2}; + (*funcs->vkGetDeviceBufferMemoryRequirements)(hDev, &devBufMemReq, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, + VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), pMemoryTypeIndex); + } + else +#endif // VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000 + { + // Must create a dummy buffer to query :( + VkBuffer hBuffer = VK_NULL_HANDLE; + res = funcs->vkCreateBuffer( + hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + funcs->vkGetBufferMemoryRequirements(hDev, hBuffer, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryTypeBits, pAllocationCreateInfo, + VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), pMemoryTypeIndex); + + funcs->vkDestroyBuffer( + hDev, hBuffer, allocator->GetAllocationCallbacks()); + } + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pImageCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions(); + VkResult res; + +#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000 + if(funcs->vkGetDeviceImageMemoryRequirements) + { + // Can query straight from VkImageCreateInfo :) + VkDeviceImageMemoryRequirementsKHR devImgMemReq = {VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS_KHR}; + devImgMemReq.pCreateInfo = pImageCreateInfo; + VMA_ASSERT(pImageCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY && (pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 && + "Cannot use this VkImageCreateInfo with vmaFindMemoryTypeIndexForImageInfo as I don't know what to pass as VkDeviceImageMemoryRequirements::planeAspect."); + + VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2}; + (*funcs->vkGetDeviceImageMemoryRequirements)(hDev, &devImgMemReq, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, + VmaBufferImageUsage(*pImageCreateInfo), pMemoryTypeIndex); + } + else +#endif // VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000 + { + // Must create a dummy image to query :( + VkImage hImage = VK_NULL_HANDLE; + res = funcs->vkCreateImage( + hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + funcs->vkGetImageMemoryRequirements(hDev, hImage, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryTypeBits, pAllocationCreateInfo, + VmaBufferImageUsage(*pImageCreateInfo), pMemoryTypeIndex); + + funcs->vkDestroyImage( + hDev, hImage, allocator->GetAllocationCallbacks()); + } + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool( + VmaAllocator allocator, + const VmaPoolCreateInfo* pCreateInfo, + VmaPool* pPool) +{ + VMA_ASSERT(allocator && pCreateInfo && pPool); + + VMA_DEBUG_LOG("vmaCreatePool"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CreatePool(pCreateInfo, pPool); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool( + VmaAllocator allocator, + VmaPool pool) +{ + VMA_ASSERT(allocator); + + if(pool == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyPool"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->DestroyPool(pool); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics( + VmaAllocator allocator, + VmaPool pool, + VmaStatistics* pPoolStats) +{ + VMA_ASSERT(allocator && pool && pPoolStats); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->GetPoolStatistics(pool, pPoolStats); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics( + VmaAllocator allocator, + VmaPool pool, + VmaDetailedStatistics* pPoolStats) +{ + VMA_ASSERT(allocator && pool && pPoolStats); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->CalculatePoolStatistics(pool, pPoolStats); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool) +{ + VMA_ASSERT(allocator && pool); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VMA_DEBUG_LOG("vmaCheckPoolCorruption"); + + return allocator->CheckPoolCorruption(pool); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName( + VmaAllocator allocator, + VmaPool pool, + const char** ppName) +{ + VMA_ASSERT(allocator && pool && ppName); + + VMA_DEBUG_LOG("vmaGetPoolName"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *ppName = pool->GetName(); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName( + VmaAllocator allocator, + VmaPool pool, + const char* pName) +{ + VMA_ASSERT(allocator && pool); + + VMA_DEBUG_LOG("vmaSetPoolName"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + pool->SetName(pName); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult result = allocator->AllocateMemory( + *pVkMemoryRequirements, + false, // requiresDedicatedAllocation + false, // prefersDedicatedAllocation + VK_NULL_HANDLE, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_UNKNOWN, + 1, // allocationCount + pAllocation); + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + size_t allocationCount, + VmaAllocation* pAllocations, + VmaAllocationInfo* pAllocationInfo) +{ + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations); + + VMA_DEBUG_LOG("vmaAllocateMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult result = allocator->AllocateMemory( + *pVkMemoryRequirements, + false, // requiresDedicatedAllocation + false, // prefersDedicatedAllocation + VK_NULL_HANDLE, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_UNKNOWN, + allocationCount, + pAllocations); + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) + { + for(size_t i = 0; i < allocationCount; ++i) + { + allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i); + } + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer( + VmaAllocator allocator, + VkBuffer buffer, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(buffer, vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + buffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(pAllocationInfo && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage( + VmaAllocator allocator, + VkImage image, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemoryForImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(image, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + image, // dedicatedImage + VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, + 1, // allocationCount + pAllocation); + + if(pAllocationInfo && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory( + VmaAllocator allocator, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator); + + if(allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaFreeMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->FreeMemory( + 1, // allocationCount + &allocation); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages( + VmaAllocator allocator, + size_t allocationCount, + const VmaAllocation* pAllocations) +{ + if(allocationCount == 0) + { + return; + } + + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaFreeMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->FreeMemory(allocationCount, pAllocations); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo( + VmaAllocator allocator, + VmaAllocation allocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && allocation && pAllocationInfo); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->GetAllocationInfo(allocation, pAllocationInfo); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo2( + VmaAllocator allocator, + VmaAllocation allocation, + VmaAllocationInfo2* pAllocationInfo) +{ + VMA_ASSERT(allocator && allocation && pAllocationInfo); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->GetAllocationInfo2(allocation, pAllocationInfo); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData( + VmaAllocator allocator, + VmaAllocation allocation, + void* pUserData) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocation->SetUserData(allocator, pUserData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const char* VMA_NULLABLE pName) +{ + allocation->SetName(allocator, pName); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags) +{ + VMA_ASSERT(allocator && allocation && pFlags); + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + *pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory( + VmaAllocator allocator, + VmaAllocation allocation, + void** ppData) +{ + VMA_ASSERT(allocator && allocation && ppData); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->Map(allocation, ppData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory( + VmaAllocator allocator, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->Unmap(allocation); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize offset, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaFlushAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize offset, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaInvalidateAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations( + VmaAllocator allocator, + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, + const VkDeviceSize* sizes) +{ + VMA_ASSERT(allocator); + + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocations); + + VMA_DEBUG_LOG("vmaFlushAllocations"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations( + VmaAllocator allocator, + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, + const VkDeviceSize* sizes) +{ + VMA_ASSERT(allocator); + + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocations); + + VMA_DEBUG_LOG("vmaInvalidateAllocations"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyMemoryToAllocation( + VmaAllocator allocator, + const void* pSrcHostPointer, + VmaAllocation dstAllocation, + VkDeviceSize dstAllocationLocalOffset, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && pSrcHostPointer && dstAllocation); + + if(size == 0) + { + return VK_SUCCESS; + } + + VMA_DEBUG_LOG("vmaCopyMemoryToAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CopyMemoryToAllocation(pSrcHostPointer, dstAllocation, dstAllocationLocalOffset, size); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyAllocationToMemory( + VmaAllocator allocator, + VmaAllocation srcAllocation, + VkDeviceSize srcAllocationLocalOffset, + void* pDstHostPointer, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && srcAllocation && pDstHostPointer); + + if(size == 0) + { + return VK_SUCCESS; + } + + VMA_DEBUG_LOG("vmaCopyAllocationToMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CopyAllocationToMemory(srcAllocation, srcAllocationLocalOffset, pDstHostPointer, size); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption( + VmaAllocator allocator, + uint32_t memoryTypeBits) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaCheckCorruption"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CheckCorruption(memoryTypeBits); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation( + VmaAllocator allocator, + const VmaDefragmentationInfo* pInfo, + VmaDefragmentationContext* pContext) +{ + VMA_ASSERT(allocator && pInfo && pContext); + + VMA_DEBUG_LOG("vmaBeginDefragmentation"); + + if (pInfo->pool != VMA_NULL) + { + // Check if run on supported algorithms + if (pInfo->pool->m_BlockVector.GetAlgorithm() & VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pContext = vma_new(allocator, VmaDefragmentationContext_T)(allocator, *pInfo); + return VK_SUCCESS; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation( + VmaAllocator allocator, + VmaDefragmentationContext context, + VmaDefragmentationStats* pStats) +{ + VMA_ASSERT(allocator && context); + + VMA_DEBUG_LOG("vmaEndDefragmentation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if (pStats) + context->GetStats(*pStats); + vma_delete(allocator, context); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo) +{ + VMA_ASSERT(context && pPassInfo); + + VMA_DEBUG_LOG("vmaBeginDefragmentationPass"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return context->DefragmentPassBegin(*pPassInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo) +{ + VMA_ASSERT(context && pPassInfo); + + VMA_DEBUG_LOG("vmaEndDefragmentationPass"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return context->DefragmentPassEnd(*pPassInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkBuffer buffer) +{ + VMA_ASSERT(allocator && allocation && buffer); + + VMA_DEBUG_LOG("vmaBindBufferMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize allocationLocalOffset, + VkBuffer buffer, + const void* pNext) +{ + VMA_ASSERT(allocator && allocation && buffer); + + VMA_DEBUG_LOG("vmaBindBufferMemory2"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkImage image) +{ + VMA_ASSERT(allocator && allocation && image); + + VMA_DEBUG_LOG("vmaBindImageMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindImageMemory(allocation, 0, image, VMA_NULL); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize allocationLocalOffset, + VkImage image, + const void* pNext) +{ + VMA_ASSERT(allocator && allocation && image); + + VMA_DEBUG_LOG("vmaBindImageMemory2"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pBuffer = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if(res >= 0) + { + // 2. vkGetBufferMemoryRequirements. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + // 3. Allocate memory using allocator. + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pBuffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), // dedicatedBufferImageUsage + *pAllocationCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind buffer with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateBufferWithAlignment"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pBuffer = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if(res >= 0) + { + // 2. vkGetBufferMemoryRequirements. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + // 2a. Include minAlignment + vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment); + + // 3. Allocate memory using allocator. + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pBuffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), // dedicatedBufferImageUsage + *pAllocationCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind buffer with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer) +{ + return vmaCreateAliasingBuffer2(allocator, allocation, 0, pBufferCreateInfo, pBuffer); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pBuffer && allocation); + VMA_ASSERT(allocationLocalOffset + pBufferCreateInfo->size <= allocation->GetSize()); + + VMA_DEBUG_LOG("vmaCreateAliasingBuffer2"); + + *pBuffer = VK_NULL_HANDLE; + + if (pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if (res >= 0) + { + // 2. Bind buffer with memory. + res = allocator->BindBufferMemory(allocation, allocationLocalOffset, *pBuffer, VMA_NULL); + if (res >= 0) + { + return VK_SUCCESS; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer( + VmaAllocator allocator, + VkBuffer buffer, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator); + + if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(buffer != VK_NULL_HANDLE) + { + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks()); + } + + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); + } +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkImage* pImage, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation); + + if(pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pImage = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkImage. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)( + allocator->m_hDevice, + pImageCreateInfo, + allocator->GetAllocationCallbacks(), + pImage); + if(res >= 0) + { + VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL : + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR; + + // 2. Allocate memory using allocator. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(*pImage, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + *pImage, // dedicatedImage + VmaBufferImageUsage(*pImageCreateInfo), // dedicatedBufferImageUsage + *pAllocationCreateInfo, + suballocType, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind image with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitImageUsage(*pImageCreateInfo); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage) +{ + return vmaCreateAliasingImage2(allocator, allocation, 0, pImageCreateInfo, pImage); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage) +{ + VMA_ASSERT(allocator && pImageCreateInfo && pImage && allocation); + + *pImage = VK_NULL_HANDLE; + + VMA_DEBUG_LOG("vmaCreateImage2"); + + if (pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + // 1. Create VkImage. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)( + allocator->m_hDevice, + pImageCreateInfo, + allocator->GetAllocationCallbacks(), + pImage); + if (res >= 0) + { + // 2. Bind image with memory. + res = allocator->BindImageMemory(allocation, allocationLocalOffset, *pImage, VMA_NULL); + if (res >= 0) + { + return VK_SUCCESS; + } + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NULLABLE_NON_DISPATCHABLE image, + VmaAllocation VMA_NULLABLE allocation) +{ + VMA_ASSERT(allocator); + + if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(image != VK_NULL_HANDLE) + { + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks()); + } + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); + } +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( + const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock) +{ + VMA_ASSERT(pCreateInfo && pVirtualBlock); + VMA_ASSERT(pCreateInfo->size > 0); + VMA_DEBUG_LOG("vmaCreateVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo); + VkResult res = (*pVirtualBlock)->Init(); + if(res < 0) + { + vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock); + *pVirtualBlock = VK_NULL_HANDLE; + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock) +{ + if(virtualBlock != VK_NULL_HANDLE) + { + VMA_DEBUG_LOG("vmaDestroyVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying. + vma_delete(&allocationCallbacks, virtualBlock); + } +} + +VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL); + VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation, + VkDeviceSize* VMA_NULLABLE pOffset) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL); + VMA_DEBUG_LOG("vmaVirtualAllocate"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation) +{ + if(allocation != VK_NULL_HANDLE) + { + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaVirtualFree"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->Free(allocation); + } +} + +VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaClearVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->Clear(); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, void* VMA_NULLABLE pUserData) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->SetAllocationUserData(allocation, pUserData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaStatistics* VMA_NOT_NULL pStats) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL); + VMA_DEBUG_LOG("vmaGetVirtualBlockStatistics"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->GetStatistics(*pStats); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaDetailedStatistics* VMA_NOT_NULL pStats) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL); + VMA_DEBUG_LOG("vmaCalculateVirtualBlockStatistics"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->CalculateDetailedStatistics(*pStats); +} + +#if VMA_STATS_STRING_ENABLED + +VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks(); + VmaStringBuilder sb(allocationCallbacks); + virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb); + *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength()); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE pStatsString) +{ + if(pStatsString != VMA_NULL) + { + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString); + } +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_PUBLIC_INTERFACE +#endif // VMA_IMPLEMENTATION + +/** +\page quick_start Quick start + +\section quick_start_project_setup Project setup + +Vulkan Memory Allocator comes in form of a "stb-style" single header file. +While you can pull the entire repository e.g. as Git module, there is also Cmake script provided, +you don't need to build it as a separate library project. +You can add file "vk_mem_alloc.h" directly to your project and submit it to code repository next to your other source files. + +"Single header" doesn't mean that everything is contained in C/C++ declarations, +like it tends to be in case of inline functions or C++ templates. +It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro. +If you don't do it properly, it will result in linker errors. + +To do it properly: + +-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library. + This includes declarations of all members of the library. +-# In exactly one CPP file define following macro before this include. + It enables also internal definitions. + +\code +#define VMA_IMPLEMENTATION +#include "vk_mem_alloc.h" +\endcode + +It may be a good idea to create dedicated CPP file just for this purpose, e.g. "VmaUsage.cpp". + +This library includes header ``, which in turn +includes `` on Windows. If you need some specific macros defined +before including these headers (like `WIN32_LEAN_AND_MEAN` or +`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define +them before every `#include` of this library. +It may be a good idea to create a dedicate header file for this purpose, e.g. "VmaUsage.h", +that will be included in other source files instead of VMA header directly. + +This library is written in C++, but has C-compatible interface. +Thus, you can include and use "vk_mem_alloc.h" in C or C++ code, but full +implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C. +Some features of C++14 are used and required. Features of C++20 are used optionally when available. +Some headers of standard C and C++ library are used, but STL containers, RTTI, or C++ exceptions are not used. + + +\section quick_start_initialization Initialization + +VMA offers library interface in a style similar to Vulkan, with object handles like #VmaAllocation, +structures describing parameters of objects to be created like #VmaAllocationCreateInfo, +and errors codes returned from functions using `VkResult` type. + +The first and the main object that needs to be created is #VmaAllocator. +It represents the initialization of the entire library. +Only one such object should be created per `VkDevice`. +You should create it at program startup, after `VkDevice` was created, and before any device memory allocator needs to be made. +It must be destroyed before `VkDevice` is destroyed. + +At program startup: + +-# Initialize Vulkan to have `VkInstance`, `VkPhysicalDevice`, `VkDevice` object. +-# Fill VmaAllocatorCreateInfo structure and call vmaCreateAllocator() to create #VmaAllocator object. + +Only members `physicalDevice`, `device`, `instance` are required. +However, you should inform the library which Vulkan version do you use by setting +VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable +by setting VmaAllocatorCreateInfo::flags. +Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions. +See below for details. + +\subsection quick_start_initialization_selecting_vulkan_version Selecting Vulkan version + +VMA supports Vulkan version down to 1.0, for backward compatibility. +If you want to use higher version, you need to inform the library about it. +This is a two-step process. + +Step 1: Compile time. By default, VMA compiles with code supporting the highest +Vulkan version found in the included `` that is also supported by the library. +If this is OK, you don't need to do anything. +However, if you want to compile VMA as if only some lower Vulkan version was available, +define macro `VMA_VULKAN_VERSION` before every `#include "vk_mem_alloc.h"`. +It should have decimal numeric value in form of ABBBCCC, where A = major, BBB = minor, CCC = patch Vulkan version. +For example, to compile against Vulkan 1.2: + +\code +#define VMA_VULKAN_VERSION 1002000 // Vulkan 1.2 +#include "vk_mem_alloc.h" +\endcode + +Step 2: Runtime. Even when compiled with higher Vulkan version available, +VMA can use only features of a lower version, which is configurable during creation of the #VmaAllocator object. +By default, only Vulkan 1.0 is used. +To initialize the allocator with support for higher Vulkan version, you need to set member +VmaAllocatorCreateInfo::vulkanApiVersion to an appropriate value, e.g. using constants like `VK_API_VERSION_1_2`. +See code sample below. + +\subsection quick_start_initialization_importing_vulkan_functions Importing Vulkan functions + +You may need to configure importing Vulkan functions. There are 3 ways to do this: + +-# **If you link with Vulkan static library** (e.g. "vulkan-1.lib" on Windows): + - You don't need to do anything. + - VMA will use these, as macro `VMA_STATIC_VULKAN_FUNCTIONS` is defined to 1 by default. +-# **If you want VMA to fetch pointers to Vulkan functions dynamically** using `vkGetInstanceProcAddr`, + `vkGetDeviceProcAddr` (this is the option presented in the example below): + - Define `VMA_STATIC_VULKAN_FUNCTIONS` to 0, `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 1. + - Provide pointers to these two functions via VmaVulkanFunctions::vkGetInstanceProcAddr, + VmaVulkanFunctions::vkGetDeviceProcAddr. + - The library will fetch pointers to all other functions it needs internally. +-# **If you fetch pointers to all Vulkan functions in a custom way**, e.g. using some loader like + [Volk](https://github.com/zeux/volk): + - Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0. + - Pass these pointers via structure #VmaVulkanFunctions. + +\subsection quick_start_initialization_enabling_extensions Enabling extensions + +VMA can automatically use following Vulkan extensions. +If you found them available on the selected physical device and you enabled them +while creating `VkInstance` / `VkDevice` object, inform VMA about their availability +by setting appropriate flags in VmaAllocatorCreateInfo::flags. + +Vulkan extension | VMA flag +------------------------------|----------------------------------------------------- +VK_KHR_dedicated_allocation | #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT +VK_KHR_bind_memory2 | #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT +VK_KHR_maintenance4 | #VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT +VK_KHR_maintenance5 | #VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT +VK_EXT_memory_budget | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT +VK_KHR_buffer_device_address | #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT +VK_EXT_memory_priority | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT +VK_AMD_device_coherent_memory | #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT + +Example with fetching pointers to Vulkan functions dynamically: + +\code +#define VMA_STATIC_VULKAN_FUNCTIONS 0 +#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 +#include "vk_mem_alloc.h" + +... + +VmaVulkanFunctions vulkanFunctions = {}; +vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; +vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; + +VmaAllocatorCreateInfo allocatorCreateInfo = {}; +allocatorCreateInfo.flags = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT; +allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2; +allocatorCreateInfo.physicalDevice = physicalDevice; +allocatorCreateInfo.device = device; +allocatorCreateInfo.instance = instance; +allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; + +VmaAllocator allocator; +vmaCreateAllocator(&allocatorCreateInfo, &allocator); + +// Entire program... + +// At the end, don't forget to: +vmaDestroyAllocator(allocator); +\endcode + + +\subsection quick_start_initialization_other_config Other configuration options + +There are additional configuration options available through preprocessor macros that you can define +before including VMA header and through parameters passed in #VmaAllocatorCreateInfo. +They include a possibility to use your own callbacks for host memory allocations (`VkAllocationCallbacks`), +callbacks for device memory allocations (instead of `vkAllocateMemory`, `vkFreeMemory`), +or your custom `VMA_ASSERT` macro, among others. +For more information, see: @ref configuration. + + +\section quick_start_resource_allocation Resource allocation + +When you want to create a buffer or image: + +-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure. +-# Fill VmaAllocationCreateInfo structure. +-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory + already allocated and bound to it, plus #VmaAllocation objects that represents its underlying memory. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +Don't forget to destroy your buffer and allocation objects when no longer needed: + +\code +vmaDestroyBuffer(allocator, buffer, allocation); +\endcode + +If you need to map the buffer, you must set flag +#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT +in VmaAllocationCreateInfo::flags. +There are many additional parameters that can control the choice of memory type to be used for the allocation +and other features. +For more information, see documentation chapters: @ref choosing_memory_type, @ref memory_mapping. + + +\page choosing_memory_type Choosing memory type + +Physical devices in Vulkan support various combinations of memory heaps and +types. Help with choosing correct and optimal memory type for your specific +resource is one of the key features of this library. You can use it by filling +appropriate members of VmaAllocationCreateInfo structure, as described below. +You can also combine multiple methods. + +-# If you just want to find memory type index that meets your requirements, you + can use function: vmaFindMemoryTypeIndexForBufferInfo(), + vmaFindMemoryTypeIndexForImageInfo(), vmaFindMemoryTypeIndex(). +-# If you want to allocate a region of device memory without association with any + specific image or buffer, you can use function vmaAllocateMemory(). Usage of + this function is not recommended and usually not needed. + vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once, + which may be useful for sparse binding. +-# If you already have a buffer or an image created, you want to allocate memory + for it and then you will bind it yourself, you can use function + vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(). + For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory() + or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2(). +-# If you want to create a buffer or an image, allocate memory for it, and bind + them together, all in one call, you can use function vmaCreateBuffer(), + vmaCreateImage(). + This is the easiest and recommended way to use this library! + +When using 3. or 4., the library internally queries Vulkan for memory types +supported for that buffer or image (function `vkGetBufferMemoryRequirements()`) +and uses only one of these types. + +If no memory type can be found that meets all the requirements, these functions +return `VK_ERROR_FEATURE_NOT_PRESENT`. + +You can leave VmaAllocationCreateInfo structure completely filled with zeros. +It means no requirements are specified for memory type. +It is valid, although not very useful. + +\section choosing_memory_type_usage Usage + +The easiest way to specify memory requirements is to fill member +VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage. +It defines high level, common usage types. +Since version 3 of the library, it is recommended to use #VMA_MEMORY_USAGE_AUTO to let it select best memory type for your resource automatically. + +For example, if you want to create a uniform buffer that will be filled using +transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can +do it using following code. The buffer will most likely end up in a memory type with +`VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT` to be fast to access by the GPU device. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +If you have a preference for putting the resource in GPU (device) memory or CPU (host) memory +on systems with discrete graphics card that have the memories separate, you can use +#VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST. + +When using `VMA_MEMORY_USAGE_AUTO*` while you want to map the allocated memory, +you also need to specify one of the host access flags: +#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +This will help the library decide about preferred memory type to ensure it has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` +so you can map it. + +For example, a staging buffer that will be filled via mapped pointer and then +used as a source of transfer to the buffer described previously can be created like this. +It will likely end up in a memory type that is `HOST_VISIBLE` and `HOST_COHERENT` +but not `HOST_CACHED` (meaning uncached, write-combined) and not `DEVICE_LOCAL` (meaning system RAM). + +\code +VkBufferCreateInfo stagingBufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +stagingBufferInfo.size = 65536; +stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo stagingAllocInfo = {}; +stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO; +stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + +VkBuffer stagingBuffer; +VmaAllocation stagingAllocation; +vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr); +\endcode + +For more examples of creating different kinds of resources, see chapter \ref usage_patterns. +See also: @ref memory_mapping. + +Usage values `VMA_MEMORY_USAGE_AUTO*` are legal to use only when the library knows +about the resource being created by having `VkBufferCreateInfo` / `VkImageCreateInfo` passed, +so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc. +If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting +memory type, as described below. + +\note +Old usage values (`VMA_MEMORY_USAGE_GPU_ONLY`, `VMA_MEMORY_USAGE_CPU_ONLY`, +`VMA_MEMORY_USAGE_CPU_TO_GPU`, `VMA_MEMORY_USAGE_GPU_TO_CPU`, `VMA_MEMORY_USAGE_CPU_COPY`) +are still available and work same way as in previous versions of the library +for backward compatibility, but they are deprecated. + +\section choosing_memory_type_required_preferred_flags Required and preferred flags + +You can specify more detailed requirements by filling members +VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags +with a combination of bits from enum `VkMemoryPropertyFlags`. For example, +if you want to create a buffer that will be persistently mapped on host (so it +must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`, +use following code: + +\code +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; +allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +A memory type is chosen that has all the required flags and as many preferred +flags set as possible. + +Value passed in VmaAllocationCreateInfo::usage is internally converted to a set of required and preferred flags, +plus some extra "magic" (heuristics). + +\section choosing_memory_type_explicit_memory_types Explicit memory types + +If you inspected memory types available on the physical device and you have +a preference for memory types that you want to use, you can fill member +VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set +means that a memory type with that index is allowed to be used for the +allocation. Special value 0, just like `UINT32_MAX`, means there are no +restrictions to memory type index. + +Please note that this member is NOT just a memory type index. +Still you can use it to choose just one, specific memory type. +For example, if you already determined that your buffer should be created in +memory type 2, use following code: + +\code +uint32_t memoryTypeIndex = 2; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.memoryTypeBits = 1u << memoryTypeIndex; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +You can also use this parameter to exclude some memory types. +If you inspect memory heaps and types available on the current physical device and +you determine that for some reason you don't want to use a specific memory type for the allocation, +you can enable automatic memory type selection but exclude certain memory type or types +by setting all bits of `memoryTypeBits` to 1 except the ones you choose. + +\code +// ... +uint32_t excludedMemoryTypeIndex = 2; +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocInfo.memoryTypeBits = ~(1u << excludedMemoryTypeIndex); +// ... +\endcode + + +\section choosing_memory_type_custom_memory_pools Custom memory pools + +If you allocate from custom memory pool, all the ways of specifying memory +requirements described above are not applicable and the aforementioned members +of VmaAllocationCreateInfo structure are ignored. Memory type is selected +explicitly when creating the pool and then used to make all the allocations from +that pool. For further details, see \ref custom_memory_pools. + +\section choosing_memory_type_dedicated_allocations Dedicated allocations + +Memory for allocations is reserved out of larger block of `VkDeviceMemory` +allocated from Vulkan internally. That is the main feature of this whole library. +You can still request a separate memory block to be created for an allocation, +just like you would do in a trivial solution without using any allocator. +In that case, a buffer or image is always bound to that memory at offset 0. +This is called a "dedicated allocation". +You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +The library can also internally decide to use dedicated allocation in some cases, e.g.: + +- When the size of the allocation is large. +- When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled + and it reports that dedicated allocation is required or recommended for the resource. +- When allocation of next big memory block fails due to not enough device memory, + but allocation with the exact requested size succeeds. + + +\page memory_mapping Memory mapping + +To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`, +to be able to read from it or write to it in CPU code. +Mapping is possible only of memory allocated from a memory type that has +`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag. +Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose. +You can use them directly with memory allocated by this library, +but it is not recommended because of following issue: +Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed. +This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan. +It is also not thread-safe. +Because of this, Vulkan Memory Allocator provides following facilities: + +\note If you want to be able to map an allocation, you need to specify one of the flags +#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT +in VmaAllocationCreateInfo::flags. These flags are required for an allocation to be mappable +when using #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` enum values. +For other usage values they are ignored and every such allocation made in `HOST_VISIBLE` memory type is mappable, +but these flags can still be used for consistency. + +\section memory_mapping_copy_functions Copy functions + +The easiest way to copy data from a host pointer to an allocation is to use convenience function vmaCopyMemoryToAllocation(). +It automatically maps the Vulkan memory temporarily (if not already mapped), performs `memcpy`, +and calls `vkFlushMappedMemoryRanges` (if required - if memory type is not `HOST_COHERENT`). + +It is also the safest one, because using `memcpy` avoids a risk of accidentally introducing memory reads +(e.g. by doing `pMappedVectors[i] += v`), which may be very slow on memory types that are not `HOST_CACHED`. + +\code +struct ConstantBuffer +{ + ... +}; +ConstantBuffer constantBufferData = ... + +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + +VkBuffer buf; +VmaAllocation alloc; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr); + +vmaCopyMemoryToAllocation(allocator, &constantBufferData, alloc, 0, sizeof(ConstantBuffer)); +\endcode + +Copy in the other direction - from an allocation to a host pointer can be performed the same way using function vmaCopyAllocationToMemory(). + +\section memory_mapping_mapping_functions Mapping functions + +The library provides following functions for mapping of a specific allocation: vmaMapMemory(), vmaUnmapMemory(). +They are safer and more convenient to use than standard Vulkan functions. +You can map an allocation multiple times simultaneously - mapping is reference-counted internally. +You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block. +The way it is implemented is that the library always maps entire memory block, not just region of the allocation. +For further details, see description of vmaMapMemory() function. +Example: + +\code +// Having these objects initialized: +struct ConstantBuffer +{ + ... +}; +ConstantBuffer constantBufferData = ... + +VmaAllocator allocator = ... +VkBuffer constantBuffer = ... +VmaAllocation constantBufferAllocation = ... + +// You can map and fill your buffer using following code: + +void* mappedData; +vmaMapMemory(allocator, constantBufferAllocation, &mappedData); +memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); +vmaUnmapMemory(allocator, constantBufferAllocation); +\endcode + +When mapping, you may see a warning from Vulkan validation layer similar to this one: + +Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used. + +It happens because the library maps entire `VkDeviceMemory` block, where different +types of images and buffers may end up together, especially on GPUs with unified memory like Intel. +You can safely ignore it if you are sure you access only memory of the intended +object that you wanted to map. + + +\section memory_mapping_persistently_mapped_memory Persistently mapped memory + +Keeping your memory persistently mapped is generally OK in Vulkan. +You don't need to unmap it before using its data on the GPU. +The library provides a special feature designed for that: +Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in +VmaAllocationCreateInfo::flags stay mapped all the time, +so you can just access CPU pointer to it any time +without a need to call any "map" or "unmap" function. +Example: + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +// Buffer is already mapped. You can access its memory. +memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData)); +\endcode + +\note #VMA_ALLOCATION_CREATE_MAPPED_BIT by itself doesn't guarantee that the allocation will end up +in a mappable memory type. +For this, you need to also specify #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or +#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +#VMA_ALLOCATION_CREATE_MAPPED_BIT only guarantees that if the memory is `HOST_VISIBLE`, the allocation will be mapped on creation. +For an example of how to make use of this fact, see section \ref usage_patterns_advanced_data_uploading. + +\section memory_mapping_cache_control Cache flush and invalidate + +Memory in Vulkan doesn't need to be unmapped before using it on GPU, +but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set, +you need to manually **invalidate** cache before reading of mapped pointer +and **flush** cache after writing to mapped pointer. +Map/unmap operations don't do that automatically. +Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`, +`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient +functions that refer to given allocation object: vmaFlushAllocation(), +vmaInvalidateAllocation(), +or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations(). + +Regions of memory specified for flush/invalidate must be aligned to +`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library. +In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations +within blocks are aligned to this value, so their offsets are always multiply of +`nonCoherentAtomSize` and two different allocations never share same "line" of this size. + +Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA) +currently provide `HOST_COHERENT` flag on all memory types that are +`HOST_VISIBLE`, so on PC you may not need to bother. + + +\page staying_within_budget Staying within budget + +When developing a graphics-intensive game or program, it is important to avoid allocating +more GPU memory than it is physically available. When the memory is over-committed, +various bad things can happen, depending on the specific GPU, graphics driver, and +operating system: + +- It may just work without any problems. +- The application may slow down because some memory blocks are moved to system RAM + and the GPU has to access them through PCI Express bus. +- A new allocation may take very long time to complete, even few seconds, and possibly + freeze entire system. +- The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +- It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST` + returned somewhere later. + +\section staying_within_budget_querying_for_budget Querying for budget + +To query for current memory usage and available budget, use function vmaGetHeapBudgets(). +Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap. + +Please note that this function returns different information and works faster than +vmaCalculateStatistics(). vmaGetHeapBudgets() can be called every frame or even before every +allocation, while vmaCalculateStatistics() is intended to be used rarely, +only to obtain statistical information, e.g. for debugging purposes. + +It is recommended to use VK_EXT_memory_budget device extension to obtain information +about the budget from Vulkan device. VMA is able to use this extension automatically. +When not enabled, the allocator behaves same way, but then it estimates current usage +and available budget based on its internal information and Vulkan memory heap sizes, +which may be less precise. In order to use this extension: + +1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2 + required by it are available and enable them. Please note that the first is a device + extension and the second is instance extension! +2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object. +3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from + Vulkan inside of it to avoid overhead of querying it with every allocation. + +\section staying_within_budget_controlling_memory_usage Controlling memory usage + +There are many ways in which you can try to stay within the budget. + +First, when making new allocation requires allocating a new memory block, the library +tries not to exceed the budget automatically. If a block with default recommended size +(e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even +dedicated memory for just this resource. + +If the size of the requested resource plus current memory usage is more than the +budget, by default the library still tries to create it, leaving it to the Vulkan +implementation whether the allocation succeeds or fails. You can change this behavior +by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is +not made if it would exceed the budget or if the budget is already exceeded. +VMA then tries to make the allocation from the next eligible Vulkan memory type. +The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag +when creating resources that are not essential for the application (e.g. the texture +of a specific object) and not to pass it when creating critically important resources +(e.g. render targets). + +On AMD graphics cards there is a custom vendor extension available: VK_AMD_memory_overallocation_behavior +that allows to control the behavior of the Vulkan implementation in out-of-memory cases - +whether it should fail with an error code or still allow the allocation. +Usage of this extension involves only passing extra structure on Vulkan device creation, +so it is out of scope of this library. + +Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure +a new allocation is created only when it fits inside one of the existing memory blocks. +If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +This also ensures that the function call is very fast because it never goes to Vulkan +to obtain a new block. + +\note Creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount +set to more than 0 will currently try to allocate memory blocks without checking whether they +fit within budget. + + +\page resource_aliasing Resource aliasing (overlap) + +New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory +management, give an opportunity to alias (overlap) multiple resources in the +same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL). +It can be useful to save video memory, but it must be used with caution. + +For example, if you know the flow of your whole render frame in advance, you +are going to use some intermediate textures or buffers only during a small range of render passes, +and you know these ranges don't overlap in time, you can bind these resources to +the same place in memory, even if they have completely different parameters (width, height, format etc.). + +![Resource aliasing (overlap)](../gfx/Aliasing.png) + +Such scenario is possible using VMA, but you need to create your images manually. +Then you need to calculate parameters of an allocation to be made using formula: + +- allocation size = max(size of each image) +- allocation alignment = max(alignment of each image) +- allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image) + +Following example shows two different images bound to the same place in memory, +allocated to fit largest of them. + +\code +// A 512x512 texture to be sampled. +VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +img1CreateInfo.imageType = VK_IMAGE_TYPE_2D; +img1CreateInfo.extent.width = 512; +img1CreateInfo.extent.height = 512; +img1CreateInfo.extent.depth = 1; +img1CreateInfo.mipLevels = 10; +img1CreateInfo.arrayLayers = 1; +img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; +img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; +img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +// A full screen texture to be used as color attachment. +VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +img2CreateInfo.imageType = VK_IMAGE_TYPE_2D; +img2CreateInfo.extent.width = 1920; +img2CreateInfo.extent.height = 1080; +img2CreateInfo.extent.depth = 1; +img2CreateInfo.mipLevels = 1; +img2CreateInfo.arrayLayers = 1; +img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VkImage img1; +res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1); +VkImage img2; +res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2); + +VkMemoryRequirements img1MemReq; +vkGetImageMemoryRequirements(device, img1, &img1MemReq); +VkMemoryRequirements img2MemReq; +vkGetImageMemoryRequirements(device, img2, &img2MemReq); + +VkMemoryRequirements finalMemReq = {}; +finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size); +finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment); +finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits; +// Validate if(finalMemReq.memoryTypeBits != 0) + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + +VmaAllocation alloc; +res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr); + +res = vmaBindImageMemory(allocator, alloc, img1); +res = vmaBindImageMemory(allocator, alloc, img2); + +// You can use img1, img2 here, but not at the same time! + +vmaFreeMemory(allocator, alloc); +vkDestroyImage(allocator, img2, nullptr); +vkDestroyImage(allocator, img1, nullptr); +\endcode + +VMA also provides convenience functions that create a buffer or image and bind it to memory +represented by an existing #VmaAllocation: +vmaCreateAliasingBuffer(), vmaCreateAliasingBuffer2(), +vmaCreateAliasingImage(), vmaCreateAliasingImage2(). +Versions with "2" offer additional parameter `allocationLocalOffset`. + +Remember that using resources that alias in memory requires proper synchronization. +You need to issue a memory barrier to make sure commands that use `img1` and `img2` +don't overlap on GPU timeline. +You also need to treat a resource after aliasing as uninitialized - containing garbage data. +For example, if you use `img1` and then want to use `img2`, you need to issue +an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`. + +Additional considerations: + +- Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases. +See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag. +- You can create more complex layout where different images and buffers are bound +at different offsets inside one large allocation. For example, one can imagine +a big texture used in some render passes, aliasing with a set of many small buffers +used between in some further passes. To bind a resource at non-zero offset in an allocation, +use vmaBindBufferMemory2() / vmaBindImageMemory2(). +- Before allocating memory for the resources you want to alias, check `memoryTypeBits` +returned in memory requirements of each resource to make sure the bits overlap. +Some GPUs may expose multiple memory types suitable e.g. only for buffers or +images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your +resources may be disjoint. Aliasing them is not possible in that case. + + +\page custom_memory_pools Custom memory pools + +A memory pool contains a number of `VkDeviceMemory` blocks. +The library automatically creates and manages default pool for each memory type available on the device. +Default memory pool automatically grows in size. +Size of allocated blocks is also variable and managed automatically. +You are using default pools whenever you leave VmaAllocationCreateInfo::pool = null. + +You can create custom pool and allocate memory out of it. +It can be useful if you want to: + +- Keep certain kind of allocations separate from others. +- Enforce particular, fixed size of Vulkan memory blocks. +- Limit maximum amount of Vulkan memory allocated for that pool. +- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool. +- Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in + #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain. +- Perform defragmentation on a specific subset of your allocations. + +To use custom memory pools: + +-# Fill VmaPoolCreateInfo structure. +-# Call vmaCreatePool() to obtain #VmaPool handle. +-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle. + You don't need to specify any other parameters of this structure, like `usage`. + +Example: + +\code +// Find memoryTypeIndex for the pool. +VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +sampleBufCreateInfo.size = 0x10000; // Doesn't matter. +sampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo sampleAllocCreateInfo = {}; +sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + +uint32_t memTypeIndex; +VkResult res = vmaFindMemoryTypeIndexForBufferInfo(allocator, + &sampleBufCreateInfo, &sampleAllocCreateInfo, &memTypeIndex); +// Check res... + +// Create a pool that can have at most 2 blocks, 128 MiB each. +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = memTypeIndex; +poolCreateInfo.blockSize = 128ull * 1024 * 1024; +poolCreateInfo.maxBlockCount = 2; + +VmaPool pool; +res = vmaCreatePool(allocator, &poolCreateInfo, &pool); +// Check res... + +// Allocate a buffer out of it. +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 1024; +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.pool = pool; + +VkBuffer buf; +VmaAllocation alloc; +res = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr); +// Check res... +\endcode + +You have to free all allocations made from this pool before destroying it. + +\code +vmaDestroyBuffer(allocator, buf, alloc); +vmaDestroyPool(allocator, pool); +\endcode + +New versions of this library support creating dedicated allocations in custom pools. +It is supported only when VmaPoolCreateInfo::blockSize = 0. +To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and +VmaAllocationCreateInfo::flags to #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + + +\section custom_memory_pools_MemTypeIndex Choosing memory type index + +When creating a pool, you must explicitly specify memory type index. +To find the one suitable for your buffers or images, you can use helper functions +vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo(). +You need to provide structures with example parameters of buffers or images +that you are going to create in that pool. + +\code +VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +exampleBufCreateInfo.size = 1024; // Doesn't matter +exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + +uint32_t memTypeIndex; +vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex); + +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = memTypeIndex; +// ... +\endcode + +When creating buffers/images allocated in that pool, provide following parameters: + +- `VkBufferCreateInfo`: Prefer to pass same parameters as above. + Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior. + Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers + or the other way around. +- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member. + Other members are ignored anyway. + + +\section custom_memory_pools_when_not_use When not to use custom pools + +Custom pools are commonly overused by VMA users. +While it may feel natural to keep some logical groups of resources separate in memory, +in most cases it does more harm than good. +Using custom pool shouldn't be your first choice. +Instead, please make all allocations from default pools first and only use custom pools +if you can prove and measure that it is beneficial in some way, +e.g. it results in lower memory usage, better performance, etc. + +Using custom pools has disadvantages: + +- Each pool has its own collection of `VkDeviceMemory` blocks. + Some of them may be partially or even completely empty. + Spreading allocations across multiple pools increases the amount of wasted (allocated but unbound) memory. +- You must manually choose specific memory type to be used by a custom pool (set as VmaPoolCreateInfo::memoryTypeIndex). + When using default pools, best memory type for each of your allocations can be selected automatically + using a carefully design algorithm that works across all kinds of GPUs. +- If an allocation from a custom pool at specific memory type fails, entire allocation operation returns failure. + When using default pools, VMA tries another compatible memory type. +- If you set VmaPoolCreateInfo::blockSize != 0, each memory block has the same size, + while default pools start from small blocks and only allocate next blocks larger and larger + up to the preferred block size. + +Many of the common concerns can be addressed in a different way than using custom pools: + +- If you want to keep your allocations of certain size (small versus large) or certain lifetime (transient versus long lived) + separate, you likely don't need to. + VMA uses a high quality allocation algorithm that manages memory well in various cases. + Please measure and check if using custom pools provides a benefit. +- If you want to keep your images and buffers separate, you don't need to. + VMA respects `bufferImageGranularity` limit automatically. +- If you want to keep your mapped and not mapped allocations separate, you don't need to. + VMA respects `nonCoherentAtomSize` limit automatically. + It also maps only those `VkDeviceMemory` blocks that need to map any allocation. + It even tries to keep mappable and non-mappable allocations in separate blocks to minimize the amount of mapped memory. +- If you want to choose a custom size for the default memory block, you can set it globally instead + using VmaAllocatorCreateInfo::preferredLargeHeapBlockSize. +- If you want to select specific memory type for your allocation, + you can set VmaAllocationCreateInfo::memoryTypeBits to `(1u << myMemoryTypeIndex)` instead. +- If you need to create a buffer with certain minimum alignment, you can still do it + using default pools with dedicated function vmaCreateBufferWithAlignment(). + + +\section linear_algorithm Linear allocation algorithm + +Each Vulkan memory block managed by this library has accompanying metadata that +keeps track of used and unused regions. By default, the metadata structure and +algorithm tries to find best place for new allocations among free regions to +optimize memory usage. This way you can allocate and free objects in any order. + +![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png) + +Sometimes there is a need to use simpler, linear allocation algorithm. You can +create custom pool that uses such algorithm by adding flag +#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating +#VmaPool object. Then an alternative metadata management is used. It always +creates new allocations after last one and doesn't reuse free regions after +allocations freed in the middle. It results in better allocation performance and +less memory consumed by metadata. + +![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png) + +With this one flag, you can create a custom pool that can be used in many ways: +free-at-once, stack, double stack, and ring buffer. See below for details. +You don't need to specify explicitly which of these options you are going to use - it is detected automatically. + +\subsection linear_algorithm_free_at_once Free-at-once + +In a pool that uses linear algorithm, you still need to free all the allocations +individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free +them in any order. New allocations are always made after last one - free space +in the middle is not reused. However, when you release all the allocation and +the pool becomes empty, allocation starts from the beginning again. This way you +can use linear algorithm to speed up creation of allocations that you are going +to release all at once. + +![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_stack Stack + +When you free an allocation that was created last, its space can be reused. +Thanks to this, if you always release allocations in the order opposite to their +creation (LIFO - Last In First Out), you can achieve behavior of a stack. + +![Stack](../gfx/Linear_allocator_4_stack.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_double_stack Double stack + +The space reserved by a custom pool with linear algorithm may be used by two +stacks: + +- First, default one, growing up from offset 0. +- Second, "upper" one, growing down from the end towards lower offsets. + +To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT +to VmaAllocationCreateInfo::flags. + +![Double stack](../gfx/Linear_allocator_7_double_stack.png) + +Double stack is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +When the two stacks' ends meet so there is not enough space between them for a +new allocation, such allocation fails with usual +`VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + +\subsection linear_algorithm_ring_buffer Ring buffer + +When you free some allocations from the beginning and there is not enough free space +for a new one at the end of a pool, allocator's "cursor" wraps around to the +beginning and starts allocation there. Thanks to this, if you always release +allocations in the same order as you created them (FIFO - First In First Out), +you can achieve behavior of a ring buffer / queue. + +![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png) + +Ring buffer is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +\note \ref defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT. + + +\page defragmentation Defragmentation + +Interleaved allocations and deallocations of many objects of varying size can +cause fragmentation over time, which can lead to a situation where the library is unable +to find a continuous range of free memory for a new allocation despite there is +enough free space, just scattered across many small free ranges between existing +allocations. + +To mitigate this problem, you can use defragmentation feature. +It doesn't happen automatically though and needs your cooperation, +because VMA is a low level library that only allocates memory. +It cannot recreate buffers and images in a new place as it doesn't remember the contents of `VkBufferCreateInfo` / `VkImageCreateInfo` structures. +It cannot copy their contents as it doesn't record any commands to a command buffer. + +Example: + +\code +VmaDefragmentationInfo defragInfo = {}; +defragInfo.pool = myPool; +defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT; + +VmaDefragmentationContext defragCtx; +VkResult res = vmaBeginDefragmentation(allocator, &defragInfo, &defragCtx); +// Check res... + +for(;;) +{ + VmaDefragmentationPassMoveInfo pass; + res = vmaBeginDefragmentationPass(allocator, defragCtx, &pass); + if(res == VK_SUCCESS) + break; + else if(res != VK_INCOMPLETE) + // Handle error... + + for(uint32_t i = 0; i < pass.moveCount; ++i) + { + // Inspect pass.pMoves[i].srcAllocation, identify what buffer/image it represents. + VmaAllocationInfo allocInfo; + vmaGetAllocationInfo(allocator, pass.pMoves[i].srcAllocation, &allocInfo); + MyEngineResourceData* resData = (MyEngineResourceData*)allocInfo.pUserData; + + // Recreate and bind this buffer/image at: pass.pMoves[i].dstMemory, pass.pMoves[i].dstOffset. + VkImageCreateInfo imgCreateInfo = ... + VkImage newImg; + res = vkCreateImage(device, &imgCreateInfo, nullptr, &newImg); + // Check res... + res = vmaBindImageMemory(allocator, pass.pMoves[i].dstTmpAllocation, newImg); + // Check res... + + // Issue a vkCmdCopyBuffer/vkCmdCopyImage to copy its content to the new place. + vkCmdCopyImage(cmdBuf, resData->img, ..., newImg, ...); + } + + // Make sure the copy commands finished executing. + vkWaitForFences(...); + + // Destroy old buffers/images bound with pass.pMoves[i].srcAllocation. + for(uint32_t i = 0; i < pass.moveCount; ++i) + { + // ... + vkDestroyImage(device, resData->img, nullptr); + } + + // Update appropriate descriptors to point to the new places... + + res = vmaEndDefragmentationPass(allocator, defragCtx, &pass); + if(res == VK_SUCCESS) + break; + else if(res != VK_INCOMPLETE) + // Handle error... +} + +vmaEndDefragmentation(allocator, defragCtx, nullptr); +\endcode + +Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage() +create/destroy an allocation and a buffer/image at once, these are just a shortcut for +creating the resource, allocating memory, and binding them together. +Defragmentation works on memory allocations only. You must handle the rest manually. +Defragmentation is an iterative process that should repreat "passes" as long as related functions +return `VK_INCOMPLETE` not `VK_SUCCESS`. +In each pass: + +1. vmaBeginDefragmentationPass() function call: + - Calculates and returns the list of allocations to be moved in this pass. + Note this can be a time-consuming process. + - Reserves destination memory for them by creating temporary destination allocations + that you can query for their `VkDeviceMemory` + offset using vmaGetAllocationInfo(). +2. Inside the pass, **you should**: + - Inspect the returned list of allocations to be moved. + - Create new buffers/images and bind them at the returned destination temporary allocations. + - Copy data from source to destination resources if necessary. + - Destroy the source buffers/images, but NOT their allocations. +3. vmaEndDefragmentationPass() function call: + - Frees the source memory reserved for the allocations that are moved. + - Modifies source #VmaAllocation objects that are moved to point to the destination reserved memory. + - Frees `VkDeviceMemory` blocks that became empty. + +Unlike in previous iterations of the defragmentation API, there is no list of "movable" allocations passed as a parameter. +Defragmentation algorithm tries to move all suitable allocations. +You can, however, refuse to move some of them inside a defragmentation pass, by setting +`pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. +This is not recommended and may result in suboptimal packing of the allocations after defragmentation. +If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool. + +Inside a pass, for each allocation that should be moved: + +- You should copy its data from the source to the destination place by calling e.g. `vkCmdCopyBuffer()`, `vkCmdCopyImage()`. + - You need to make sure these commands finished executing before destroying the source buffers/images and before calling vmaEndDefragmentationPass(). +- If a resource doesn't contain any meaningful data, e.g. it is a transient color attachment image to be cleared, + filled, and used temporarily in each rendering frame, you can just recreate this image + without copying its data. +- If the resource is in `HOST_VISIBLE` and `HOST_CACHED` memory, you can copy its data on the CPU + using `memcpy()`. +- If you cannot move the allocation, you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. + This will cancel the move. + - vmaEndDefragmentationPass() will then free the destination memory + not the source memory of the allocation, leaving it unchanged. +- If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time), + you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY. + - vmaEndDefragmentationPass() will then free both source and destination memory, and will destroy the source #VmaAllocation object. + +You can defragment a specific custom pool by setting VmaDefragmentationInfo::pool +(like in the example above) or all the default pools by setting this member to null. + +Defragmentation is always performed in each pool separately. +Allocations are never moved between different Vulkan memory types. +The size of the destination memory reserved for a moved allocation is the same as the original one. +Alignment of an allocation as it was determined using `vkGetBufferMemoryRequirements()` etc. is also respected after defragmentation. +Buffers/images should be recreated with the same `VkBufferCreateInfo` / `VkImageCreateInfo` parameters as the original ones. + +You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved +in each pass, e.g. to call it in sync with render frames and not to experience too big hitches. +See members: VmaDefragmentationInfo::maxBytesPerPass, VmaDefragmentationInfo::maxAllocationsPerPass. + +It is also safe to perform the defragmentation asynchronously to render frames and other Vulkan and VMA +usage, possibly from multiple threads, with the exception that allocations +returned in VmaDefragmentationPassMoveInfo::pMoves shouldn't be destroyed until the defragmentation pass is ended. + +Mapping is preserved on allocations that are moved during defragmentation. +Whether through #VMA_ALLOCATION_CREATE_MAPPED_BIT or vmaMapMemory(), the allocations +are mapped at their new place. Of course, pointer to the mapped data changes, so it needs to be queried +using VmaAllocationInfo::pMappedData. + +\note Defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT. + + +\page statistics Statistics + +This library contains several functions that return information about its internal state, +especially the amount of memory allocated from Vulkan. + +\section statistics_numeric_statistics Numeric statistics + +If you need to obtain basic statistics about memory usage per heap, together with current budget, +you can call function vmaGetHeapBudgets() and inspect structure #VmaBudget. +This is useful to keep track of memory usage and stay within budget +(see also \ref staying_within_budget). +Example: + +\code +uint32_t heapIndex = ... + +VmaBudget budgets[VK_MAX_MEMORY_HEAPS]; +vmaGetHeapBudgets(allocator, budgets); + +printf("My heap currently has %u allocations taking %llu B,\n", + budgets[heapIndex].statistics.allocationCount, + budgets[heapIndex].statistics.allocationBytes); +printf("allocated out of %u Vulkan device memory blocks taking %llu B,\n", + budgets[heapIndex].statistics.blockCount, + budgets[heapIndex].statistics.blockBytes); +printf("Vulkan reports total usage %llu B with budget %llu B.\n", + budgets[heapIndex].usage, + budgets[heapIndex].budget); +\endcode + +You can query for more detailed statistics per memory heap, type, and totals, +including minimum and maximum allocation size and unused range size, +by calling function vmaCalculateStatistics() and inspecting structure #VmaTotalStatistics. +This function is slower though, as it has to traverse all the internal data structures, +so it should be used only for debugging purposes. + +You can query for statistics of a custom pool using function vmaGetPoolStatistics() +or vmaCalculatePoolStatistics(). + +You can query for information about a specific allocation using function vmaGetAllocationInfo(). +It fill structure #VmaAllocationInfo. + +\section statistics_json_dump JSON dump + +You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString(). +The result is guaranteed to be correct JSON. +It uses ANSI encoding. +Any strings provided by user (see [Allocation names](@ref allocation_names)) +are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding, +this JSON string can be treated as using this encoding. +It must be freed using function vmaFreeStatsString(). + +The format of this JSON string is not part of official documentation of the library, +but it will not change in backward-incompatible way without increasing library major version number +and appropriate mention in changelog. + +The JSON string contains all the data that can be obtained using vmaCalculateStatistics(). +It can also contain detailed map of allocated memory blocks and their regions - +free and occupied by allocations. +This allows e.g. to visualize the memory or assess fragmentation. + + +\page allocation_annotation Allocation names and user data + +\section allocation_user_data Allocation user data + +You can annotate allocations with your own information, e.g. for debugging purposes. +To do that, fill VmaAllocationCreateInfo::pUserData field when creating +an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer, +some handle, index, key, ordinal number or any other value that would associate +the allocation with your custom metadata. +It is useful to identify appropriate data structures in your engine given #VmaAllocation, +e.g. when doing \ref defragmentation. + +\code +VkBufferCreateInfo bufCreateInfo = ... + +MyBufferMetadata* pMetadata = CreateBufferMetadata(); + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.pUserData = pMetadata; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buffer, &allocation, nullptr); +\endcode + +The pointer may be later retrieved as VmaAllocationInfo::pUserData: + +\code +VmaAllocationInfo allocInfo; +vmaGetAllocationInfo(allocator, allocation, &allocInfo); +MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData; +\endcode + +It can also be changed using function vmaSetAllocationUserData(). + +Values of (non-zero) allocations' `pUserData` are printed in JSON report created by +vmaBuildStatsString() in hexadecimal form. + +\section allocation_names Allocation names + +An allocation can also carry a null-terminated string, giving a name to the allocation. +To set it, call vmaSetAllocationName(). +The library creates internal copy of the string, so the pointer you pass doesn't need +to be valid for whole lifetime of the allocation. You can free it after the call. + +\code +std::string imageName = "Texture: "; +imageName += fileName; +vmaSetAllocationName(allocator, allocation, imageName.c_str()); +\endcode + +The string can be later retrieved by inspecting VmaAllocationInfo::pName. +It is also printed in JSON report created by vmaBuildStatsString(). + +\note Setting string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it. +You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library. + + +\page virtual_allocator Virtual allocator + +As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator". +It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block". +You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan. +A common use case is sub-allocation of pieces of one large GPU buffer. + +\section virtual_allocator_creating_virtual_block Creating virtual block + +To use this functionality, there is no main "allocator" object. +You don't need to have #VmaAllocator object created. +All you need to do is to create a separate #VmaVirtualBlock object for each block of memory you want to be managed by the allocator: + +-# Fill in #VmaVirtualBlockCreateInfo structure. +-# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object. + +Example: + +\code +VmaVirtualBlockCreateInfo blockCreateInfo = {}; +blockCreateInfo.size = 1048576; // 1 MB + +VmaVirtualBlock block; +VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block); +\endcode + +\section virtual_allocator_making_virtual_allocations Making virtual allocations + +#VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions +using the same code as the main Vulkan memory allocator. +Similarly to #VmaAllocation for standard GPU allocations, there is #VmaVirtualAllocation type +that represents an opaque handle to an allocation within the virtual block. + +In order to make such allocation: + +-# Fill in #VmaVirtualAllocationCreateInfo structure. +-# Call vmaVirtualAllocate(). Get new #VmaVirtualAllocation object that represents the allocation. + You can also receive `VkDeviceSize offset` that was assigned to the allocation. + +Example: + +\code +VmaVirtualAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.size = 4096; // 4 KB + +VmaVirtualAllocation alloc; +VkDeviceSize offset; +res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, &offset); +if(res == VK_SUCCESS) +{ + // Use the 4 KB of your memory starting at offset. +} +else +{ + // Allocation failed - no space for it could be found. Handle this error! +} +\endcode + +\section virtual_allocator_deallocation Deallocation + +When no longer needed, an allocation can be freed by calling vmaVirtualFree(). +You can only pass to this function an allocation that was previously returned by vmaVirtualAllocate() +called for the same #VmaVirtualBlock. + +When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock(). +All allocations must be freed before the block is destroyed, which is checked internally by an assert. +However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once - +a feature not available in normal Vulkan memory allocator. Example: + +\code +vmaVirtualFree(block, alloc); +vmaDestroyVirtualBlock(block); +\endcode + +\section virtual_allocator_allocation_parameters Allocation parameters + +You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData(). +Its default value is null. +It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some +larger data structure containing more information. Example: + +\code +struct CustomAllocData +{ + std::string m_AllocName; +}; +CustomAllocData* allocData = new CustomAllocData(); +allocData->m_AllocName = "My allocation 1"; +vmaSetVirtualAllocationUserData(block, alloc, allocData); +\endcode + +The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function +vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo. +If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation! +Example: + +\code +VmaVirtualAllocationInfo allocInfo; +vmaGetVirtualAllocationInfo(block, alloc, &allocInfo); +delete (CustomAllocData*)allocInfo.pUserData; + +vmaVirtualFree(block, alloc); +\endcode + +\section virtual_allocator_alignment_and_units Alignment and units + +It feels natural to express sizes and offsets in bytes. +If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member +VmaVirtualAllocationCreateInfo::alignment to request it. Example: + +\code +VmaVirtualAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.size = 4096; // 4 KB +allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B + +VmaVirtualAllocation alloc; +res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, nullptr); +\endcode + +Alignments of different allocations made from one block may vary. +However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`, +you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes. +It might be more convenient, but you need to make sure to use this new unit consistently in all the places: + +- VmaVirtualBlockCreateInfo::size +- VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment +- Using offset returned by vmaVirtualAllocate() or in VmaVirtualAllocationInfo::offset + +\section virtual_allocator_statistics Statistics + +You can obtain statistics of a virtual block using vmaGetVirtualBlockStatistics() +(to get brief statistics that are fast to calculate) +or vmaCalculateVirtualBlockStatistics() (to get more detailed statistics, slower to calculate). +The functions fill structures #VmaStatistics, #VmaDetailedStatistics respectively - same as used by the normal Vulkan memory allocator. +Example: + +\code +VmaStatistics stats; +vmaGetVirtualBlockStatistics(block, &stats); +printf("My virtual block has %llu bytes used by %u virtual allocations\n", + stats.allocationBytes, stats.allocationCount); +\endcode + +You can also request a full list of allocations and free regions as a string in JSON format by calling +vmaBuildVirtualBlockStatsString(). +Returned string must be later freed using vmaFreeVirtualBlockStatsString(). +The format of this string differs from the one returned by the main Vulkan allocator, but it is similar. + +\section virtual_allocator_additional_considerations Additional considerations + +The "virtual allocator" functionality is implemented on a level of individual memory blocks. +Keeping track of a whole collection of blocks, allocating new ones when out of free space, +deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user. + +Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory. +See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT). +You can find their description in chapter \ref custom_memory_pools. +Allocation strategies are also supported. +See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT). + +Following features are supported only by the allocator of the real GPU memory and not by virtual allocations: +buffer-image granularity, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`. + + +\page debugging_memory_usage Debugging incorrect memory usage + +If you suspect a bug with memory usage, like usage of uninitialized memory or +memory being overwritten out of bounds of an allocation, +you can use debug features of this library to verify this. + +\section debugging_memory_usage_initialization Memory initialization + +If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used, +you can enable automatic memory initialization to verify this. +To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1. + +\code +#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 +#include "vk_mem_alloc.h" +\endcode + +It makes memory of new allocations initialized to bit pattern `0xDCDCDCDC`. +Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`. +Memory is automatically mapped and unmapped if necessary. + +If you find these values while debugging your program, good chances are that you incorrectly +read Vulkan memory that is allocated but not initialized, or already freed, respectively. + +Memory initialization works only with memory types that are `HOST_VISIBLE` and with allocations that can be mapped. +It works also with dedicated allocations. + +\section debugging_memory_usage_margins Margins + +By default, allocations are laid out in memory blocks next to each other if possible +(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`). + +![Allocations without margin](../gfx/Margins_1.png) + +Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified +number of bytes as a margin after every allocation. + +\code +#define VMA_DEBUG_MARGIN 16 +#include "vk_mem_alloc.h" +\endcode + +![Allocations with margin](../gfx/Margins_2.png) + +If your bug goes away after enabling margins, it means it may be caused by memory +being overwritten outside of allocation boundaries. It is not 100% certain though. +Change in application behavior may also be caused by different order and distribution +of allocations across memory blocks after margins are applied. + +Margins work with all types of memory. + +Margin is applied only to allocations made out of memory blocks and not to dedicated +allocations, which have their own memory block of specific size. +It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag +or those automatically decided to put into dedicated allocations, e.g. due to its +large size or recommended by VK_KHR_dedicated_allocation extension. + +Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space. + +Note that enabling margins increases memory usage and fragmentation. + +Margins do not apply to \ref virtual_allocator. + +\section debugging_memory_usage_corruption_detection Corruption detection + +You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation +of contents of the margins. + +\code +#define VMA_DEBUG_MARGIN 16 +#define VMA_DEBUG_DETECT_CORRUPTION 1 +#include "vk_mem_alloc.h" +\endcode + +When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN` +(it must be multiply of 4) after every allocation is filled with a magic number. +This idea is also know as "canary". +Memory is automatically mapped and unmapped if necessary. + +This number is validated automatically when the allocation is destroyed. +If it is not equal to the expected value, `VMA_ASSERT()` is executed. +It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation, +which indicates a serious bug. + +You can also explicitly request checking margins of all allocations in all memory blocks +that belong to specified memory types by using function vmaCheckCorruption(), +or in memory blocks that belong to specified custom pool, by using function +vmaCheckPoolCorruption(). + +Margin validation (corruption detection) works only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. + + +\section debugging_memory_usage_leak_detection Leak detection features + +At allocation and allocator destruction time VMA checks for unfreed and unmapped blocks using +`VMA_ASSERT_LEAK()`. This macro defaults to an assertion, triggering a typically fatal error in Debug +builds, and doing nothing in Release builds. You can provide your own definition of `VMA_ASSERT_LEAK()` +to change this behavior. + +At memory block destruction time VMA lists out all unfreed allocations using the `VMA_LEAK_LOG_FORMAT()` +macro, which defaults to `VMA_DEBUG_LOG_FORMAT`, which in turn defaults to a no-op. +If you're having trouble with leaks - for example, the aforementioned assertion triggers, but you don't +quite know \em why -, overriding this macro to print out the the leaking blocks, combined with assigning +individual names to allocations using vmaSetAllocationName(), can greatly aid in fixing them. + +\page other_api_interop Interop with other graphics APIs + +VMA provides some features that help with interoperability with other graphics APIs, e.g. OpenGL. + +\section opengl_interop_exporting_memory Exporting memory + +If you want to attach `VkExportMemoryAllocateInfoKHR` or other structure to `pNext` chain of memory allocations made by the library: + +You can create \ref custom_memory_pools for such allocations. +Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext +while creating the custom pool. +Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool, +not only while creating it, as no copy of the structure is made, +but its original pointer is used for each allocation instead. + +If you want to export all memory allocated by VMA from certain memory types, +also dedicated allocations or other allocations made from default pools, +an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes. +It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library +through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type. +Please note that new versions of the library also support dedicated allocations created in custom pools. + +You should not mix these two methods in a way that allows to apply both to the same memory type. +Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`. + + +\section opengl_interop_custom_alignment Custom alignment + +Buffers or images exported to a different API like OpenGL may require a different alignment, +higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`. +To impose such alignment: + +You can create \ref custom_memory_pools for such allocations. +Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation +to be made out of this pool. +The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image +from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically. + +If you want to create a buffer with a specific minimum alignment out of default pools, +use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`. + +Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated +allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block. +You can ensure that an allocation is created as dedicated by using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation. + +\section opengl_interop_extended_allocation_information Extended allocation information + +If you want to rely on VMA to allocate your buffers and images inside larger memory blocks, +but you need to know the size of the entire block and whether the allocation was made +with its own dedicated memory, use function vmaGetAllocationInfo2() to retrieve +extended allocation information in structure #VmaAllocationInfo2. + + + +\page usage_patterns Recommended usage patterns + +Vulkan gives great flexibility in memory allocation. +This chapter shows the most common patterns. + +See also slides from talk: +[Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New) + + +\section usage_patterns_gpu_only GPU-only resource + +When: +Any resources that you frequently write and read on GPU, +e.g. images used as color attachments (aka "render targets"), depth-stencil attachments, +images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)"). + +What to do: +Let the library select the optimal memory type, which will likely have `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + +\code +VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; +imgCreateInfo.extent.width = 3840; +imgCreateInfo.extent.height = 2160; +imgCreateInfo.extent.depth = 1; +imgCreateInfo.mipLevels = 1; +imgCreateInfo.arrayLayers = 1; +imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; +allocCreateInfo.priority = 1.0f; + +VkImage img; +VmaAllocation alloc; +vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr); +\endcode + +Also consider: +Consider creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, +especially if they are large or if you plan to destroy and recreate them with different sizes +e.g. when display resolution changes. +Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later. +When VK_EXT_memory_priority extension is enabled, it is also worth setting high priority to such allocation +to decrease chances to be evicted to system memory by the operating system. + +\section usage_patterns_staging_copy_upload Staging copy for upload + +When: +A "staging" buffer than you want to map and fill from CPU code, then use as a source of transfer +to some GPU resource. + +What to do: +Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT. +Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 65536; +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +... + +memcpy(allocInfo.pMappedData, myData, myDataSize); +\endcode + +Also consider: +You can map the allocation using vmaMapMemory() or you can create it as persistenly mapped +using #VMA_ALLOCATION_CREATE_MAPPED_BIT, as in the example above. + + +\section usage_patterns_readback Readback + +When: +Buffers for data written by or transferred from the GPU that you want to read back on the CPU, +e.g. results of some computations. + +What to do: +Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` +and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 65536; +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +... + +const float* downloadedData = (const float*)allocInfo.pMappedData; +\endcode + + +\section usage_patterns_advanced_data_uploading Advanced data uploading + +For resources that you frequently write on CPU via mapped pointer and +frequently read on GPU e.g. as a uniform buffer (also called "dynamic"), multiple options are possible: + +-# Easiest solution is to have one copy of the resource in `HOST_VISIBLE` memory, + even if it means system RAM (not `DEVICE_LOCAL`) on systems with a discrete graphics card, + and make the device reach out to that resource directly. + - Reads performed by the device will then go through PCI Express bus. + The performance of this access may be limited, but it may be fine depending on the size + of this resource (whether it is small enough to quickly end up in GPU cache) and the sparsity + of access. +-# On systems with unified memory (e.g. AMD APU or Intel integrated graphics, mobile chips), + a memory type may be available that is both `HOST_VISIBLE` (available for mapping) and `DEVICE_LOCAL` + (fast to access from the GPU). Then, it is likely the best choice for such type of resource. +-# Systems with a discrete graphics card and separate video memory may or may not expose + a memory type that is both `HOST_VISIBLE` and `DEVICE_LOCAL`, also known as Base Address Register (BAR). + If they do, it represents a piece of VRAM (or entire VRAM, if ReBAR is enabled in the motherboard BIOS) + that is available to CPU for mapping. + - Writes performed by the host to that memory go through PCI Express bus. + The performance of these writes may be limited, but it may be fine, especially on PCIe 4.0, + as long as rules of using uncached and write-combined memory are followed - only sequential writes and no reads. +-# Finally, you may need or prefer to create a separate copy of the resource in `DEVICE_LOCAL` memory, + a separate "staging" copy in `HOST_VISIBLE` memory and perform an explicit transfer command between them. + +Thankfully, VMA offers an aid to create and use such resources in the the way optimal +for the current Vulkan device. To help the library make the best choice, +use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT together with +#VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT. +It will then prefer a memory type that is both `DEVICE_LOCAL` and `HOST_VISIBLE` (integrated memory or BAR), +but if no such memory type is available or allocation from it fails +(PC graphics cards have only 256 MB of BAR by default, unless ReBAR is supported and enabled in BIOS), +it will fall back to `DEVICE_LOCAL` memory for fast GPU access. +It is then up to you to detect that the allocation ended up in a memory type that is not `HOST_VISIBLE`, +so you need to create another "staging" allocation and perform explicit transfers. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 65536; +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +VkMemoryPropertyFlags memPropFlags; +vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags); + +if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) +{ + // Allocation ended up in a mappable memory and is already mapped - write to it directly. + + // [Executed in runtime]: + memcpy(allocInfo.pMappedData, myData, myDataSize); +} +else +{ + // Allocation ended up in a non-mappable memory - need to transfer. + VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + stagingBufCreateInfo.size = 65536; + stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + VmaAllocationCreateInfo stagingAllocCreateInfo = {}; + stagingAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + stagingAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + + VkBuffer stagingBuf; + VmaAllocation stagingAlloc; + VmaAllocationInfo stagingAllocInfo; + vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo, + &stagingBuf, &stagingAlloc, stagingAllocInfo); + + // [Executed in runtime]: + memcpy(stagingAllocInfo.pMappedData, myData, myDataSize); + vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE); + //vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT + VkBufferCopy bufCopy = { + 0, // srcOffset + 0, // dstOffset, + myDataSize); // size + vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy); +} +\endcode + +\section usage_patterns_other_use_cases Other use cases + +Here are some other, less obvious use cases and their recommended settings: + +- An image that is used only as transfer source and destination, but it should stay on the device, + as it is used to temporarily store a copy of some texture, e.g. from the current to the next frame, + for temporal antialiasing or other temporal effects. + - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT` + - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO +- An image that is used only as transfer source and destination, but it should be placed + in the system RAM despite it doesn't need to be mapped, because it serves as a "swap" copy to evict + least recently used textures from VRAM. + - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT` + - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_HOST, + as VMA needs a hint here to differentiate from the previous case. +- A buffer that you want to map and write from the CPU, directly read from the GPU + (e.g. as a uniform or vertex buffer), but you have a clear preference to place it in device or + host memory due to its large size. + - Use `VkBufferCreateInfo::usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT` + - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST + - Use VmaAllocationCreateInfo::flags = #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT + + +\page configuration Configuration + +Please check "CONFIGURATION SECTION" in the code to find macros that you can define +before each include of this file or change directly in this file to provide +your own implementation of basic facilities like assert, `min()` and `max()` functions, +mutex, atomic etc. +The library uses its own implementation of containers by default, but you can switch to using +STL containers instead. + +For example, define `VMA_ASSERT(expr)` before including the library to provide +custom implementation of the assertion, compatible with your project. +By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration +and empty otherwise. + +\section config_Vulkan_functions Pointers to Vulkan functions + +There are multiple ways to import pointers to Vulkan functions in the library. +In the simplest case you don't need to do anything. +If the compilation or linking of your program or the initialization of the #VmaAllocator +doesn't work for you, you can try to reconfigure it. + +First, the allocator tries to fetch pointers to Vulkan functions linked statically, +like this: + +\code +m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory; +\endcode + +If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`. + +Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions. +You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or +by using a helper library like [volk](https://github.com/zeux/volk). + +Third, VMA tries to fetch remaining pointers that are still null by calling +`vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own. +You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr. +Other pointers will be fetched automatically. +If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`. + +Finally, all the function pointers required by the library (considering selected +Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null. + + +\section custom_memory_allocator Custom host memory allocator + +If you use custom allocator for CPU memory rather than default operator `new` +and `delete` from C++, you can make this library using your allocator as well +by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These +functions will be passed to Vulkan, as well as used by the library itself to +make any CPU-side allocations. + +\section allocation_callbacks Device memory allocation callbacks + +The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally. +You can setup callbacks to be informed about these calls, e.g. for the purpose +of gathering some statistics. To do it, fill optional member +VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. + +\section heap_memory_limit Device heap memory limit + +When device memory of certain heap runs out of free space, new allocations may +fail (returning error code) or they may succeed, silently pushing some existing_ +memory blocks from GPU VRAM to system RAM (which degrades performance). This +behavior is implementation-dependent - it depends on GPU vendor and graphics +driver. + +On AMD cards it can be controlled while creating Vulkan device object by using +VK_AMD_memory_overallocation_behavior extension, if available. + +Alternatively, if you want to test how your program behaves with limited amount of Vulkan device +memory available without switching your graphics card to one that really has +smaller VRAM, you can use a feature of this library intended for this purpose. +To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit. + + + +\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation + +VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve +performance on some GPUs. It augments Vulkan API with possibility to query +driver whether it prefers particular buffer or image to have its own, dedicated +allocation (separate `VkDeviceMemory` block) for better efficiency - to be able +to do some internal optimizations. The extension is supported by this library. +It will be used automatically when enabled. + +It has been promoted to core Vulkan 1.1, so if you use eligible Vulkan version +and inform VMA about it by setting VmaAllocatorCreateInfo::vulkanApiVersion, +you are all set. + +Otherwise, if you want to use it as an extension: + +1 . When creating Vulkan device, check if following 2 device extensions are +supported (call `vkEnumerateDeviceExtensionProperties()`). +If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`). + +- VK_KHR_get_memory_requirements2 +- VK_KHR_dedicated_allocation + +If you enabled these extensions: + +2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating +your #VmaAllocator to inform the library that you enabled required extensions +and you want the library to use them. + +\code +allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; + +vmaCreateAllocator(&allocatorInfo, &allocator); +\endcode + +That is all. The extension will be automatically used whenever you create a +buffer using vmaCreateBuffer() or image using vmaCreateImage(). + +When using the extension together with Vulkan Validation Layer, you will receive +warnings like this: + +_vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer._ + +It is OK, you should just ignore it. It happens because you use function +`vkGetBufferMemoryRequirements2KHR()` instead of standard +`vkGetBufferMemoryRequirements()`, while the validation layer seems to be +unaware of it. + +To learn more about this extension, see: + +- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation) +- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5) + + + +\page vk_ext_memory_priority VK_EXT_memory_priority + +VK_EXT_memory_priority is a device extension that allows to pass additional "priority" +value to Vulkan memory allocations that the implementation may use prefer certain +buffers and images that are critical for performance to stay in device-local memory +in cases when the memory is over-subscribed, while some others may be moved to the system memory. + +VMA offers convenient usage of this extension. +If you enable it, you can pass "priority" parameter when creating allocations or custom pools +and the library automatically passes the value to Vulkan using this extension. + +If you want to use this extension in connection with VMA, follow these steps: + +\section vk_ext_memory_priority_initialization Initialization + +1) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_EXT_memory_priority". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority` is true. + +3) While creating device with `vkCreateDevice`, enable this extension - add "VK_EXT_memory_priority" +to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to +`VkPhysicalDeviceFeatures2::pNext` chain and set its member `memoryPriority` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT +to VmaAllocatorCreateInfo::flags. + +\section vk_ext_memory_priority_usage Usage + +When using this extension, you should initialize following member: + +- VmaAllocationCreateInfo::priority when creating a dedicated allocation with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +- VmaPoolCreateInfo::priority when creating a custom pool. + +It should be a floating-point value between `0.0f` and `1.0f`, where recommended default is `0.5f`. +Memory allocated with higher value can be treated by the Vulkan implementation as higher priority +and so it can have lower chances of being pushed out to system memory, experiencing degraded performance. + +It might be a good idea to create performance-critical resources like color-attachment or depth-stencil images +as dedicated and set high priority to them. For example: + +\code +VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; +imgCreateInfo.extent.width = 3840; +imgCreateInfo.extent.height = 2160; +imgCreateInfo.extent.depth = 1; +imgCreateInfo.mipLevels = 1; +imgCreateInfo.arrayLayers = 1; +imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; +allocCreateInfo.priority = 1.0f; + +VkImage img; +VmaAllocation alloc; +vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr); +\endcode + +`priority` member is ignored in the following situations: + +- Allocations created in custom pools: They inherit the priority, along with all other allocation parameters + from the parameters passed in #VmaPoolCreateInfo when the pool was created. +- Allocations created in default pools: They inherit the priority from the parameters + VMA used when creating default pools, which means `priority == 0.5f`. + + +\page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory + +VK_AMD_device_coherent_memory is a device extension that enables access to +additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and +`VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for +allocation of buffers intended for writing "breadcrumb markers" in between passes +or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases. + +When the extension is available but has not been enabled, Vulkan physical device +still exposes those memory types, but their usage is forbidden. VMA automatically +takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt +to allocate memory of such type is made. + +If you want to use this extension in connection with VMA, follow these steps: + +\section vk_amd_device_coherent_memory_initialization Initialization + +1) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true. + +3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory" +to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to +`VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT +to VmaAllocatorCreateInfo::flags. + +\section vk_amd_device_coherent_memory_usage Usage + +After following steps described above, you can create VMA allocations and custom pools +out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible +devices. There are multiple ways to do it, for example: + +- You can request or prefer to allocate out of such memory types by adding + `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags + or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with + other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage. +- If you manually found memory type index to use for this purpose, force allocation + from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`. + +\section vk_amd_device_coherent_memory_more_information More information + +To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_AMD_device_coherent_memory.html) + +Example use of this extension can be found in the code of the sample and test suite +accompanying this library. + + +\page enabling_buffer_device_address Enabling buffer device address + +Device extension VK_KHR_buffer_device_address +allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code. +It has been promoted to core Vulkan 1.2. + +If you want to use this feature in connection with VMA, follow these steps: + +\section enabling_buffer_device_address_initialization Initialization + +1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains +"VK_KHR_buffer_device_address". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress` is true. + +3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add +"VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to +`VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT +to VmaAllocatorCreateInfo::flags. + +\section enabling_buffer_device_address_usage Usage + +After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA. +The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to +allocated memory blocks wherever it might be needed. + +Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`. +The second part of this functionality related to "capture and replay" is not supported, +as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage. + +\section enabling_buffer_device_address_more_information More information + +To learn more about this extension, see [VK_KHR_buffer_device_address in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#VK_KHR_buffer_device_address) + +Example use of this extension can be found in the code of the sample and test suite +accompanying this library. + +\page general_considerations General considerations + +\section general_considerations_thread_safety Thread safety + +- The library has no global state, so separate #VmaAllocator objects can be used + independently. + There should be no need to create multiple such objects though - one per `VkDevice` is enough. +- By default, all calls to functions that take #VmaAllocator as first parameter + are safe to call from multiple threads simultaneously because they are + synchronized internally when needed. + This includes allocation and deallocation from default memory pool, as well as custom #VmaPool. +- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT + flag, calls to functions that take such #VmaAllocator object must be + synchronized externally. +- Access to a #VmaAllocation object must be externally synchronized. For example, + you must not call vmaGetAllocationInfo() and vmaMapMemory() from different + threads at the same time if you pass the same #VmaAllocation object to these + functions. +- #VmaVirtualBlock is not safe to be used from multiple threads simultaneously. + +\section general_considerations_versioning_and_compatibility Versioning and compatibility + +The library uses [**Semantic Versioning**](https://semver.org/), +which means version numbers follow convention: Major.Minor.Patch (e.g. 2.3.0), where: + +- Incremented Patch version means a release is backward- and forward-compatible, + introducing only some internal improvements, bug fixes, optimizations etc. + or changes that are out of scope of the official API described in this documentation. +- Incremented Minor version means a release is backward-compatible, + so existing code that uses the library should continue to work, while some new + symbols could have been added: new structures, functions, new values in existing + enums and bit flags, new structure members, but not new function parameters. +- Incrementing Major version means a release could break some backward compatibility. + +All changes between official releases are documented in file "CHANGELOG.md". + +\warning Backward compatibility is considered on the level of C++ source code, not binary linkage. +Adding new members to existing structures is treated as backward compatible if initializing +the new members to binary zero results in the old behavior. +You should always fully initialize all library structures to zeros and not rely on their +exact binary size. + +\section general_considerations_validation_layer_warnings Validation layer warnings + +When using this library, you can meet following types of warnings issued by +Vulkan validation layer. They don't necessarily indicate a bug, so you may need +to just ignore them. + +- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.* + - It happens when VK_KHR_dedicated_allocation extension is enabled. + `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it. +- *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.* + - It happens when you map a buffer or image, because the library maps entire + `VkDeviceMemory` block, where different types of images and buffers may end + up together, especially on GPUs with unified memory like Intel. +- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.* + - It may happen when you use [defragmentation](@ref defragmentation). + +\section general_considerations_allocation_algorithm Allocation algorithm + +The library uses following algorithm for allocation, in order: + +-# Try to find free range of memory in existing blocks. +-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size. +-# If failed, try to create such block with size / 2, size / 4, size / 8. +-# If failed, try to allocate separate `VkDeviceMemory` for this allocation, + just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +-# If failed, choose other memory type that meets the requirements specified in + VmaAllocationCreateInfo and go to point 1. +-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + +\section general_considerations_features_not_supported Features not supported + +Features deliberately excluded from the scope of this library: + +-# **Data transfer.** Uploading (streaming) and downloading data of buffers and images + between CPU and GPU memory and related synchronization is responsibility of the user. + Defining some "texture" object that would automatically stream its data from a + staging copy in CPU memory to GPU memory would rather be a feature of another, + higher-level library implemented on top of VMA. + VMA doesn't record any commands to a `VkCommandBuffer`. It just allocates memory. +-# **Recreation of buffers and images.** Although the library has functions for + buffer and image creation: vmaCreateBuffer(), vmaCreateImage(), you need to + recreate these objects yourself after defragmentation. That is because the big + structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in + #VmaAllocation object. +-# **Handling CPU memory allocation failures.** When dynamically creating small C++ + objects in CPU memory (not Vulkan memory), allocation failures are not checked + and handled gracefully, because that would complicate code significantly and + is usually not needed in desktop PC applications anyway. + Success of an allocation is just checked with an assert. +-# **Code free of any compiler warnings.** Maintaining the library to compile and + work correctly on so many different platforms is hard enough. Being free of + any warnings, on any version of any compiler, is simply not feasible. + There are many preprocessor macros that make some variables unused, function parameters unreferenced, + or conditional expressions constant in some configurations. + The code of this library should not be bigger or more complicated just to silence these warnings. + It is recommended to disable such warnings instead. +-# This is a C++ library with C interface. **Bindings or ports to any other programming languages** are welcome as external projects but + are not going to be included into this repository. +*/ diff --git a/src/devicelibrary.cpp b/src/devicelibrary.cpp index 8fbeb86..6d28958 100644 --- a/src/devicelibrary.cpp +++ b/src/devicelibrary.cpp @@ -300,14 +300,19 @@ void DeviceControl::createLogicalDevice() { queueCreateSingularInfo.pQueuePriorities = &queuePriority; queueCreateInfos.push_back(queueCreateSingularInfo); } - + VkPhysicalDeviceVulkan12Features features12{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + .pNext = nullptr, + .bufferDeviceAddress = true, + }; VkPhysicalDeviceVulkan13Features features13{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, - .pNext = nullptr, + .pNext = &features12, .synchronization2 = true, .dynamicRendering = true, }; VkPhysicalDeviceFeatures featuresBase{ + .robustBufferAccess = true, .sampleRateShading = true, .samplerAnisotropy = true, }; diff --git a/src/entrypoint.cpp b/src/entrypoint.cpp index e511a18..b387701 100644 --- a/src/entrypoint.cpp +++ b/src/entrypoint.cpp @@ -21,6 +21,7 @@ VkInstance vulkaninstance; GLFWwindow *window; const uint32_t WIDTH = 800; const uint32_t HEIGHT = 600; + // Getters and Setters! void EntryApp::setFramebufferResized(bool setter) { framebufferResized = setter; @@ -76,12 +77,8 @@ void createInstance() { glfwExtensions + glfwExtensionCount); VkInstanceCreateInfo createInfo{}; // Define parameters of new vulkan instance - createInfo.sType = - VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; // Tell vulkan this is a info - // structure - createInfo.pApplicationInfo = - &appInfo; // We just created a new appInfo structure, so we pass the - // pointer to it. + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); @@ -101,6 +98,7 @@ void initVulkan() { DeviceControl::createLogicalDevice(); volkLoadDevice(DeviceControl::getDevice()); DeviceControl::createSwapChain(window); + Buffers::createMemoryAllocator(vulkaninstance); DeviceControl::createImageViews(); Buffers::createDescriptorSetLayout(); Graphics::createGraphicsPipeline(); @@ -110,14 +108,13 @@ void initVulkan() { Texture::createTextureImage(); Texture::createTextureImageView(); Texture::createTextureSampler(); - Model::loadModel(); - Buffers::createVertexBuffer(); - Buffers::createIndexBuffer(); + Model::loadModel(glm::vec3(0.0, 0.0, 0.0)); Buffers::createUniformBuffers(); Buffers::createDescriptorPool(); Buffers::createDescriptorSets(); Graphics::createCommandBuffer(); Render::createSyncObject(); + Gui::initImgui(vulkaninstance); } diff --git a/src/entrypoint.h b/src/entrypoint.h index 91c638e..4320968 100644 --- a/src/entrypoint.h +++ b/src/entrypoint.h @@ -1,6 +1,7 @@ #pragma once #include + class EntryApp { public: static EntryApp &getInstance(); diff --git a/src/graphics/buffers.cpp b/src/graphics/buffers.cpp index f7713b6..4494f83 100644 --- a/src/graphics/buffers.cpp +++ b/src/graphics/buffers.cpp @@ -1,21 +1,34 @@ #include "../devicelibrary.h" +#include "../types.h" #include "buffers.h" #include "texture.h" + #include #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" VkBuffer vertexBuffer; VkDeviceMemory vertexBufferMemory; VkBuffer indexBuffer; VkDeviceMemory indexBufferMemory; -std::vector vertices; +std::vector vertices; // Index buffer definition, showing which points to reuse. std::vector indices; +VmaAllocator _allocator; +Agnosia_T::GPUPushConstants pushConstants; + VkDescriptorPool descriptorPool; VkDescriptorSetLayout descriptorSetLayout; std::vector descriptorSets; @@ -60,6 +73,40 @@ 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; @@ -95,6 +142,101 @@ 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, @@ -128,68 +270,6 @@ void Buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, vkBindBufferMemory(DeviceControl::getDevice(), buffer, bufferMemory, 0); } -void Buffers::createIndexBuffer() { - VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); - - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - - createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - stagingBuffer, stagingBufferMemory); - - void *data; - vkMapMemory(DeviceControl::getDevice(), stagingBufferMemory, 0, bufferSize, 0, - &data); - memcpy(data, indices.data(), (size_t)bufferSize); - - vkUnmapMemory(DeviceControl::getDevice(), stagingBufferMemory); - - createBuffer( - bufferSize, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); - - copyBuffer(stagingBuffer, indexBuffer, bufferSize); - - vkDestroyBuffer(DeviceControl::getDevice(), stagingBuffer, nullptr); - vkFreeMemory(DeviceControl::getDevice(), stagingBufferMemory, nullptr); -} -void Buffers::createVertexBuffer() { - // Create a Vertex Buffer to hold the vertex information in memory so it - // doesn't have to be hardcoded! Size denotes the size of the buffer in bytes, - // usage in this case is the buffer behaviour, using a bitwise OR. Sharing - // mode denostes the same as the images in the swap chain! in this case, only - // the graphics queue uses this buffer, so we make it exclusive. - VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); - - VkBuffer stagingBuffer; - VkDeviceMemory stagingBufferMemory; - createBuffer(bufferSize, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, - stagingBuffer, stagingBufferMemory); - - void *data; - vkMapMemory(DeviceControl::getDevice(), stagingBufferMemory, 0, bufferSize, 0, - &data); - memcpy(data, vertices.data(), (size_t)bufferSize); - vkUnmapMemory(DeviceControl::getDevice(), stagingBufferMemory); - - createBuffer( - bufferSize, - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); - - copyBuffer(stagingBuffer, vertexBuffer, bufferSize); - - vkDestroyBuffer(DeviceControl::getDevice(), stagingBuffer, nullptr); - vkFreeMemory(DeviceControl::getDevice(), stagingBufferMemory, nullptr); -} - void Buffers::destroyBuffers() { vkDestroyBuffer(DeviceControl::getDevice(), indexBuffer, nullptr); vkFreeMemory(DeviceControl::getDevice(), indexBufferMemory, nullptr); @@ -373,8 +453,6 @@ void Buffers::createDescriptorSets() { void Buffers::destroyDescriptorPool() { vkDestroyDescriptorPool(DeviceControl::getDevice(), descriptorPool, nullptr); } -VkBuffer &Buffers::getVertexBuffer() { return vertexBuffer; } -VkBuffer &Buffers::getIndexBuffer() { return indexBuffer; } VkDescriptorPool &Buffers::getDescriptorPool() { return descriptorPool; } std::vector &Buffers::getDescriptorSets() { return descriptorSets; @@ -397,5 +475,5 @@ VkCommandPool &Buffers::getCommandPool() { return commandPool; } VkDescriptorSetLayout &Buffers::getDescriptorSetLayout() { return descriptorSetLayout; } -std::vector &Buffers::getVertices() { return vertices; } +std::vector &Buffers::getVertices() { return vertices; } std::vector &Buffers::getIndices() { return indices; } diff --git a/src/graphics/buffers.h b/src/graphics/buffers.h index 6a26a14..df86cba 100644 --- a/src/graphics/buffers.h +++ b/src/graphics/buffers.h @@ -1,68 +1,29 @@ #pragma once +#include +#include #define VK_NO_PROTOTYPES +#include "../types.h" #include "volk.h" #include -#define GLM_FORCE_DEPTH_ZERO_TO_ONE -#include - #define GLFW_INCLUDE_VULKAN #include -#include class Buffers { public: - struct Vertex { - // This defines what a vertex is! - // We control the position, color and texture coordinate here! - 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; - } - }; + 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 createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer &buffer, VkDeviceMemory &bufferMemory); static uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags); - static void createIndexBuffer(); - static void createVertexBuffer(); static void destroyBuffers(); - static VkBuffer &getVertexBuffer(); - static VkBuffer &getIndexBuffer(); static void createDescriptorSetLayout(); static void createUniformBuffers(); static void updateUniformBuffer(uint32_t currentImage); @@ -85,6 +46,6 @@ public: static std::vector &getUniformBuffers(); static std::vector &getUniformBuffersMemory(); static VkCommandPool &getCommandPool(); - static std::vector &getVertices(); + static std::vector &getVertices(); static std::vector &getIndices(); }; diff --git a/src/graphics/graphicspipeline.cpp b/src/graphics/graphicspipeline.cpp index 99d32b1..68d78b0 100644 --- a/src/graphics/graphicspipeline.cpp +++ b/src/graphics/graphicspipeline.cpp @@ -6,6 +6,7 @@ #include "render.h" #include "texture.h" #include +#include std::vector dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; @@ -83,8 +84,8 @@ void Graphics::createGraphicsPipeline() { vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - auto bindingDescription = Buffers::Vertex::getBindingDescription(); - auto attributeDescriptions = Buffers::Vertex::getAttributeDescriptions(); + auto bindingDescription = Agnosia_T::Vertex::getBindingDescription(); + auto attributeDescriptions = Agnosia_T::Vertex::getAttributeDescriptions(); vertexInputInfo.vertexBindingDescriptionCount = 1; vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; @@ -174,10 +175,18 @@ void Graphics::createGraphicsPipeline() { dynamicState.dynamicStateCount = static_cast(dynamicStates.size()); dynamicState.pDynamicStates = dynamicStates.data(); + VkPushConstantRange pushConstant{ + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .offset = 0, + .size = sizeof(Agnosia_T::GPUPushConstants), + }; + VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; pipelineLayoutInfo.pSetLayouts = &Buffers::getDescriptorSetLayout(); + pipelineLayoutInfo.pPushConstantRanges = &pushConstant; + pipelineLayoutInfo.pushConstantRangeCount = 1; if (vkCreatePipelineLayout(DeviceControl::getDevice(), &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { @@ -256,7 +265,6 @@ void Graphics::createCommandBuffer() { throw std::runtime_error("Failed to allocate command buffers"); } } - void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { VkCommandBufferBeginInfo beginInfo{}; @@ -344,10 +352,15 @@ void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer, scissor.extent = DeviceControl::getSwapChainExtent(); vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - VkBuffer vertexBuffers[] = {Buffers::getVertexBuffer()}; - VkDeviceSize offsets[] = {0}; - vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); - vkCmdBindIndexBuffer(commandBuffer, Buffers::getIndexBuffer(), 0, + Agnosia_T::GPUMeshBuffers Model = + Buffers::sendMesh(Buffers::getIndices(), Buffers::getVertices()); + + Agnosia_T::GPUPushConstants pushConsts; + pushConsts.vertexBuffer = Model.vertexBufferAddress; + + vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, + 0, sizeof(Agnosia_T::GPUPushConstants), &pushConsts); + vkCmdBindIndexBuffer(commandBuffer, Model.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdBindDescriptorSets( diff --git a/src/graphics/model.cpp b/src/graphics/model.cpp index 17ce4de..bef6691 100644 --- a/src/graphics/model.cpp +++ b/src/graphics/model.cpp @@ -9,8 +9,8 @@ #include namespace std { -template <> struct hash { - size_t operator()(Buffers::Vertex const &vertex) const { +template <> struct hash { + size_t operator()(Agnosia_T::Vertex const &vertex) const { return ((hash()(vertex.pos) ^ (hash()(vertex.color) << 1)) >> 1) ^ @@ -21,7 +21,7 @@ template <> struct hash { const std::string MODEL_PATH = "assets/models/viking_room.obj"; -void Model::loadModel() { +void Model::loadModel(glm::vec3 position) { tinyobj::ObjReaderConfig readerConfig; tinyobj::ObjReader reader; @@ -38,15 +38,15 @@ void Model::loadModel() { auto &attrib = reader.GetAttrib(); auto &shapes = reader.GetShapes(); auto &materials = reader.GetMaterials(); - std::unordered_map uniqueVertices{}; + std::unordered_map uniqueVertices{}; for (const auto &shape : shapes) { for (const auto &index : shape.mesh.indices) { - Buffers::Vertex vertex{}; + Agnosia_T::Vertex vertex{}; - vertex.pos = {attrib.vertices[3 * index.vertex_index + 0], - attrib.vertices[3 * index.vertex_index + 1], - attrib.vertices[3 * index.vertex_index + 2]}; + 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}; // TODO: Small fix here, handle if there are no UV's unwrapped for the // model. diff --git a/src/graphics/model.h b/src/graphics/model.h index aa75d59..d00517b 100644 --- a/src/graphics/model.h +++ b/src/graphics/model.h @@ -1,6 +1,8 @@ #pragma once +#define GLM_FORCE_DEPTH_ZERO_TO_ONE +#include class Model { public: - static void loadModel(); + static void loadModel(glm::vec3 position); }; diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index be5544f..40aacf2 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -45,6 +45,7 @@ void recreateSwapChain() { void Render::drawFrame() { vkWaitForFences(DeviceControl::getDevice(), 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + vkResetFences(DeviceControl::getDevice(), 1, &inFlightFences[currentFrame]); uint32_t imageIndex; diff --git a/src/main.cpp b/src/main.cpp index d23a5f5..42bf8fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include "entrypoint.h" + #include #define VOLK_IMPLEMENTATION diff --git a/src/shaders/vertex.vert b/src/shaders/vertex.vert index 03db775..dab8105 100644 --- a/src/shaders/vertex.vert +++ b/src/shaders/vertex.vert @@ -1,4 +1,5 @@ #version 450 +#extension GL_EXT_buffer_reference : require layout(binding = 0) uniform UniformBufferObject { float time; @@ -6,20 +7,26 @@ layout(binding = 0) uniform UniformBufferObject { mat4 view; mat4 proj; } ubo; +struct Vertex { -// inPosition and inColor are vertex attributes, per-vertex properties defined in the vertex buffer! -// Layout assigns indices to access these inputs, dvec3 takes 2 slots so we must index it at 2. https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL) + vec3 pos; + vec3 color; + vec2 texCoord; +}; -layout(location = 0) in vec3 inPosition; -layout(location = 1) in vec3 inColor; -layout(location = 2) in vec2 inTexCoord; +layout(buffer_reference, std430) readonly buffer VertexBuffer{ + Vertex vertices[]; +}; +layout( push_constant ) uniform constants { + VertexBuffer vertBuffer; +}PushConstants; layout(location = 0) out vec3 fragColor; layout(location = 1) out vec2 fragTexCoord; void main() { - - gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); - fragColor = inColor; - fragTexCoord = inTexCoord; + 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; } diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..858bba4 --- /dev/null +++ b/src/types.h @@ -0,0 +1,64 @@ +#pragma once + +#define GLM_FORCE_DEPTH_ZERO_TO_ONE + +#include "vk_mem_alloc.h" +#include +#include + +class Agnosia_T { +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; + + 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; + } + }; + struct AllocatedBuffer { + VkBuffer buffer; + VmaAllocation allocation; + VmaAllocationInfo info; + }; + struct GPUMeshBuffers { + AllocatedBuffer indexBuffer; + AllocatedBuffer vertexBuffer; + VkDeviceAddress vertexBufferAddress; + }; + struct GPUPushConstants { + VkDeviceAddress vertexBuffer; + }; +};