From 65578228e4064accf1b772596e0d9ed3da374920 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Tue, 31 Mar 2026 18:17:02 -0400 Subject: [PATCH] cemu: 2 CWE-407 defects, MOAD 0002-0005 CLEAN --- defects/cemu-0001/patch/cemu-0001.patch | 28 ++++++ defects/cemu-0001/test/cemu_0001_test | Bin 0 -> 22720 bytes defects/cemu-0001/test/cemu_0001_test.cpp | 97 ++++++++++++++++++++ defects/cemu-0002/patch/cemu-0002.patch | 50 +++++++++++ defects/cemu-0002/test/cemu_0002_test | Bin 0 -> 22512 bytes defects/cemu-0002/test/cemu_0002_test.cpp | 104 ++++++++++++++++++++++ 6 files changed, 279 insertions(+) create mode 100644 defects/cemu-0001/patch/cemu-0001.patch create mode 100755 defects/cemu-0001/test/cemu_0001_test create mode 100644 defects/cemu-0001/test/cemu_0001_test.cpp create mode 100644 defects/cemu-0002/patch/cemu-0002.patch create mode 100755 defects/cemu-0002/test/cemu_0002_test create mode 100644 defects/cemu-0002/test/cemu_0002_test.cpp diff --git a/defects/cemu-0001/patch/cemu-0001.patch b/defects/cemu-0001/patch/cemu-0001.patch new file mode 100644 index 000000000..ef3878a31 --- /dev/null +++ b/defects/cemu-0001/patch/cemu-0001.patch @@ -0,0 +1,28 @@ +# UNDF: UNDF-2026-XXXXXXXXX +--- a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h ++++ b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h +@@ -19,7 +19,7 @@ class LatteTextureViewVk : public LatteTextureView + LatteTextureVk* GetBaseImage() const { return (LatteTextureVk*)baseTexture; } + +- void AddDescriptorSetReference(struct VkDescriptorSetInfo* dsInfo) { if (std::find(list_descriptorSets.begin(), list_descriptorSets.end(), dsInfo) == list_descriptorSets.end()) list_descriptorSets.emplace_back(dsInfo); }; +- void RemoveDescriptorSetReference(struct VkDescriptorSetInfo* dsInfo) { list_descriptorSets.erase(std::remove(list_descriptorSets.begin(), list_descriptorSets.end(), dsInfo), list_descriptorSets.end()); }; ++ void AddDescriptorSetReference(struct VkDescriptorSetInfo* dsInfo) { list_descriptorSets.insert(dsInfo); }; ++ void RemoveDescriptorSetReference(struct VkDescriptorSetInfo* dsInfo) { list_descriptorSets.erase(dsInfo); }; + + private: +@@ -38,7 +38,7 @@ private: + VkDevice m_device; + VkFormat m_format; +- std::vector list_descriptorSets; // list of descriptors sets referencing this view ++ std::unordered_set list_descriptorSets; // list of descriptors sets referencing this view + + uint64 m_uniqueId; + }; +--- a/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp ++++ b/src/Cafe/HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp +@@ -75,8 +75,8 @@ LatteTextureViewVk::~LatteTextureViewVk() + { +- while (!list_descriptorSets.empty()) +- delete list_descriptorSets[0]; ++ while (!list_descriptorSets.empty()) ++ delete *list_descriptorSets.begin(); diff --git a/defects/cemu-0001/test/cemu_0001_test b/defects/cemu-0001/test/cemu_0001_test new file mode 100755 index 0000000000000000000000000000000000000000..f3d9b2340ec64a5a1435a15e3d1c0a0275712968 GIT binary patch literal 22720 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 unordered_set +// Tests that AddDescriptorSetReference is O(1) amortized, not O(D). + +#include +#include +#include +#include +#include + +// Simulates the original defect: std::vector with linear scan for uniqueness +struct DefectRef { + std::vector refs; + + void add(int* p) { + for (auto* r : refs) + if (r == p) return; + refs.push_back(p); + } + + void remove(int* p) { + refs.erase(std::remove(refs.begin(), refs.end(), p), refs.end()); + } +}; + +// Fixed implementation: unordered_set, O(1) amortized +struct FixedRef { + std::unordered_set refs; + + void add(int* p) { + refs.insert(p); + } + + void remove(int* p) { + refs.erase(p); + } +}; + +static long long bench_ns(auto fn) { + auto t0 = std::chrono::steady_clock::now(); + fn(); + auto t1 = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(t1 - t0).count(); +} + +int main() { + // Correctness test: both implementations maintain unique set + DefectRef defect; + FixedRef fixed; + + const int N = 500; + int ptrs[N]; + for (int i = 0; i < N; i++) { + defect.add(&ptrs[i]); + fixed.add(&ptrs[i]); + } + // Duplicate add should not grow the collection + for (int i = 0; i < N; i++) { + defect.add(&ptrs[i]); + fixed.add(&ptrs[i]); + } + assert(defect.refs.size() == N); + assert(fixed.refs.size() == N); + + // Remove half + for (int i = 0; i < N / 2; i++) { + defect.remove(&ptrs[i]); + fixed.remove(&ptrs[i]); + } + assert(defect.refs.size() == N / 2); + assert(fixed.refs.size() == N / 2); + + // Performance: add D = 1000 unique pointers to each, measure time + const int D = 1000; + int ds_ptrs[D]; + + DefectRef defect2; + auto t_defect = bench_ns([&]() { + for (int i = 0; i < D; i++) + defect2.add(&ds_ptrs[i]); + }); + + FixedRef fixed2; + auto t_fixed = bench_ns([&]() { + for (int i = 0; i < D; i++) + fixed2.add(&ds_ptrs[i]); + }); + + double ratio = (double)t_defect / (double)t_fixed; + printf("cemu-0001: defect=%lldns fixed=%lldns ratio=%.1fx (D=%d)\n", + (long long)t_defect, (long long)t_fixed, ratio, D); + + // Fixed should be significantly faster for large D + assert(ratio > 2.0 && "Fixed implementation not measurably faster — check test"); + + printf("cemu-0001: PASS\n"); + return 0; +} diff --git a/defects/cemu-0002/patch/cemu-0002.patch b/defects/cemu-0002/patch/cemu-0002.patch new file mode 100644 index 000000000..1a73bb737 --- /dev/null +++ b/defects/cemu-0002/patch/cemu-0002.patch @@ -0,0 +1,50 @@ +# UNDF: UNDF-2026-XXXXXXXXX +--- a/src/Cafe/GraphicPack/GraphicPack2.h ++++ b/src/Cafe/GraphicPack/GraphicPack2.h +@@ -35,10 +35,10 @@ struct FILTER_SETTINGS + sint32 width = -1; + sint32 height = -1; + sint32 depth = -1; + MEM1_FILTER inMEM1 = MEM1_FILTER::BOTH; +- std::vector format_whitelist{}; +- std::vector format_blacklist{}; +- std::vector tilemode_whitelist{}; +- std::vector tilemode_blacklist{}; ++ std::unordered_set format_whitelist{}; ++ std::unordered_set format_blacklist{}; ++ std::unordered_set tilemode_whitelist{}; ++ std::unordered_set tilemode_blacklist{}; + } filter_settings; +--- a/src/Cafe/HW/Latte/Core/LatteTexture.cpp ++++ b/src/Cafe/HW/Latte/Core/LatteTexture.cpp +@@ -1243,13 +1243,13 @@ void LatteTexture_init() +- if (!rule.filter_settings.format_whitelist.empty() && std::find(rule.filter_settings.format_whitelist.begin(), rule.filter_settings.format_whitelist.end(), (uint32)format) == rule.filter_settings.format_whitelist.end()) ++ if (!rule.filter_settings.format_whitelist.empty() && rule.filter_settings.format_whitelist.find((sint32)format) == rule.filter_settings.format_whitelist.end()) + continue; + +- if (!rule.filter_settings.format_blacklist.empty() && std::find(rule.filter_settings.format_blacklist.begin(), rule.filter_settings.format_blacklist.end(), (uint32)format) != rule.filter_settings.format_blacklist.end()) ++ if (!rule.filter_settings.format_blacklist.empty() && rule.filter_settings.format_blacklist.find((sint32)format) != rule.filter_settings.format_blacklist.end()) + continue; + +- if (!rule.filter_settings.tilemode_whitelist.empty() && std::find(rule.filter_settings.tilemode_whitelist.begin(), rule.filter_settings.tilemode_whitelist.end(), (int)tileMode) == rule.filter_settings.tilemode_whitelist.end()) ++ if (!rule.filter_settings.tilemode_whitelist.empty() && rule.filter_settings.tilemode_whitelist.find((sint32)tileMode) == rule.filter_settings.tilemode_whitelist.end()) + continue; + +- if (!rule.filter_settings.tilemode_blacklist.empty() && std::find(rule.filter_settings.tilemode_blacklist.begin(), rule.filter_settings.tilemode_blacklist.end(), (int)tileMode) != rule.filter_settings.tilemode_blacklist.end()) ++ if (!rule.filter_settings.tilemode_blacklist.empty() && rule.filter_settings.tilemode_blacklist.find((sint32)tileMode) != rule.filter_settings.tilemode_blacklist.end()) + continue; +--- a/src/Cafe/GraphicPack/GraphicPack2.cpp ++++ b/src/Cafe/GraphicPack/GraphicPack2.cpp +@@ -978,12 +978,16 @@ void GraphicPack2::_loadTextureRules(IniParser& parser) +- rule.filter_settings.format_whitelist = ParseList(parser, rules, "formats"); +- rule.filter_settings.format_blacklist = ParseList(parser, rules, "formatsExcluded"); +- rule.filter_settings.tilemode_whitelist = ParseList(parser, rules, "tilemodes"); +- rule.filter_settings.tilemode_blacklist = ParseList(parser, rules, "tilemodesExcluded"); ++ auto fmt_wl = ParseList(parser, rules, "formats"); ++ rule.filter_settings.format_whitelist = std::unordered_set(fmt_wl.begin(), fmt_wl.end()); ++ auto fmt_bl = ParseList(parser, rules, "formatsExcluded"); ++ rule.filter_settings.format_blacklist = std::unordered_set(fmt_bl.begin(), fmt_bl.end()); ++ auto tm_wl = ParseList(parser, rules, "tilemodes"); ++ rule.filter_settings.tilemode_whitelist = std::unordered_set(tm_wl.begin(), tm_wl.end()); ++ auto tm_bl = ParseList(parser, rules, "tilemodesExcluded"); ++ rule.filter_settings.tilemode_blacklist = std::unordered_set(tm_bl.begin(), tm_bl.end()); diff --git a/defects/cemu-0002/test/cemu_0002_test b/defects/cemu-0002/test/cemu_0002_test new file mode 100755 index 0000000000000000000000000000000000000000..94d9a371ceb4ceed625861a33f5121366d4e3479 GIT binary patch literal 22512 zcmeHPdvIITnLm={L?pp-AW%bqh%^Bkh>)B|qkxH(Sjkn9gBvH1^18^99IM!}k**Sl zU5JCNY;F~Yl-ac1cH7CeTb9l)Ez<#-vZUA!iFve)2^2h~TLM#Bi2!*}5|cu-zwg|0 zBw1MOPN)BL#>XRn=ljn0I^TKRd(OT0Z1wtAnN21pQyIH~5w`bIgHkUFj^89Q0QGDs zI~V`$Yz~_O+`?%_dA$KpYNS!~cxV;$GC-26qRa&F5`)YnlpYc!xl(C#x=|t_t2A(u zE2pf$i{XOv4L%8FdCs2GC}%~`ES!y0_uQTNAjpN~%hzk?-mvTfVIj4|w3ONZY zg`9*kp5zAomI{5vu*hvzEY~LFieUqEQj(BtP-=ch3;i|rnxve|(6h7%wi{$7p|tlE z$WdJWzezUpT4AqLI~)=5lu-7wYuh6&OBP+*9$L^Ii6*)ibT3`9V96p!ywg$3T_C^g z_)(u)y=FawngOE8<}~*hxYS2wvqU-Zk6sb4_L>$BFMIU)-tGtg(zAL+^S=-e`Ass! zLy7bW7dcM(3-Kc!BOcp0ooJL^4!_i4E*)ni77J`qqhaY71aDn)W3vXPP*@8@+TnRqEYcBHW8tx!DxE866 zkP3$c+1t@URfulJf9`KA%&RM~a5N-+Umx8Z0k$?APjnz^6x-n4YOw8YwH4Mt=!!+6 zS}Re21mf{9LeGmj!W|u*n@9%JPf#>UM<5afg+kOCp^DxcW?+nmH3qJ@77AW{HBC%Q z2;CTj7-25Lf(Cu{%9ZLuhm)=L`&O*numSl+4wrj8x|E^Bv%;sk919)e%WB;PWrb2k zUafU3%Ht3Y&_Ym7T+;vFV_e<7dIVHUmEspI1qO^1nvk zmE)u7iY>Up&tUJ+0N~|Q*e^wS7x2i~w=hLcXYUJI8^@=yPXxZ@<)1>{%BF*hbZ+70 zQ(2Y3U-{|Xn9?omN`Y%N58+Z-#%h66GNJ4Zp^NZYk;u6Wb+|$BKmLtxVp^WU6oDr$ zj+{eRiDs$qBEro~75e0TfxI5~Tt-?Ms}|RNNv{`lYm-qf=@K6m_h)oXkkmG;yf5$bNDp1FWEv~rzd#ZJD}_G@37c6!-yrzOCY8x_R{>pwfETqD(B(Ch z3c3pDbbXYmyMWH`>qJ3M0iEg~(~bhVj2)HjE})}fa_QRz^l3RNV|xqe;#QNFK2kuR zQNaIL0UaGBm-ZFVD{@rE1`6o50{U|Wbn3Sgp(1NjKE|BJ~sj%+Acez z^uA|RQsr+gXG}>BXy)vQ()+CS0GG=yo(pnz{yhA*&8cU^-$o^wx3k%7>u!$I1fMya z$7y2E4CQf}&@(^F<1~?Hevrp$0?#~@$7$luJdnrfB9e*aahkw0EqR+l6|6>vU`y%|cBK)Ny{7@18%Od>ABK#*s_`^l`_lxjv=I~$A#YoZL zOve!}CDky`8d9#d%&!JDVO8{F=@&syxAMaMjPhSbbd~<3ok;!rmjd|o;q*Mj*so{P zmjEg$?{nj{yqQjtB#BUeTipV|WFKkw}y*CCJQc{K! zitc^RHK6o+du%m^O+DGfO?&r&;zSmpo@=fwKZ)%oNbG=roM||@0VSkZ8BX4XEOL}> z%V)cDS#OU~>fLP!c)u<1y#znB%}jQI7ts5NGVFa!6z=0tPmZB`9bK>V4p#_16!|XOlA|;N(TLVWY4cZYIwoxl(`ZlMIZ9( zXQ|ul{FE3@q3QiKXVVT~M+XrEQ*4>?6#0lWM{$oRw$(%a{%dGA0+r*z^6SUoJOe}e zPoI#=13uHi3^kH(KX6K0kG_JI-{EP6rTNh9~m!QT(Q1tI{W72W11kB`3 zW)c4R^>;;B7b61su)h8m)cYxAp9~9R$Ub@rf%GFNM)+WbQ%$3yJo7c^Lab+-T_6>ew@%7(cj)vf7hj}NW z7|?a4(fxtuZ%qEdwkw6JgI`Y{b@KVO!M(wPUWqFRa#ma4EF(9=)^{;-aIR8ukJ|b! z<>acwh|hh<)^|M>rJgXFH_2m`+TTW%aUaq&ML((34EglK%HSD`Z}5znT3}Zr$qc@2 zQA|TeZ)kPc=Jb(k%&oxuKcOtuZ-oN40v~o1pm0PdN$$vqt?v_bHNk3448fkQZ#I=D zv$muiWlHaTdzt4Mnjvh-HvxKi7)-{Hs=EdhTk;rM#nXGhYrb4d8uaEd^(0Uj_6!VV}??q)4**o08=z4Zt6Y9GY*mX^)>AgIII?}dqF4#3htxUYiW4#E7QJ+TlZ?)UF zp(nPjnGHkpBSTal!_cUbe15AdLqn0;o7*{utB0M_pkCJmI|q4)s8JEP-9+VfhCH@C z!?q`R1bT-}4X&{rJijelc_qcC3c*NU{xR>;)JzJE!e`SqP|!E2<b~9SOAQX&o&nF&VOw7o^IhhL=nJ?xF#6kg zBe0r&7vlj+ZOK8J9Jko!^njll!;-OqlE8_USNiK_VY4B< z`gG2&Pe19y;DQ|=xAQQ#k%OJD8#z;=!ib5x($@DoGM3D0-~Md1KSW5W+I??;C!-Xq=H*iABxXFR@3t%12k6AiSMN$h`{d|%Df4I^a~&1zSnl_dAjNH>u)PHqbGf~ ztA2H}=NI%O@O3&p;2`M~ZPkOVitYXv(=YrP z`V?FAl~&ue7d#5a4YzK(6CLVj*uLO0u7UK!;K|+m@SBMA`7{iS`^EHW%H!tY7{S7u zMO*Uc+=1|@Yv4A|?VdY4cY4%24xsBUf%d0DhIfmRN7sD|Aia|;?L#|L!kxwt?leYl zr?CgDEb|R?>~RJXx5`_?{<`O}Z-$ANK`=dkA~j0Cg{zU5)170*+GfCw2cRbX&WBJ^ z4<&p(rXR?>ggniYguCAN=`Y`@Aojg~INr3*>l$!955W^)$zCuUt?=jk^$K(kC`y0% zG6L=~?E)+EhCydqm6+2jo1_UpJHUFP=uA1~+B6a(u8HcQ9p&>5%gBS#1^+ z{8}imW{()_7%5Nw_0&-@OI(fH*SZHNi(7MThLWm#7)bj0lc-w=z8)(75pdHTkY$DP znawDa{se~<{bSc*+&d_zPbjHHuhE4Xlkh?%HKid{Z*6c7YxDdmYh&_x?J94|gGI(6 z*I{390PH^Z;lzc0{Wl7iayFEA4V!@~XdA*{4+KiQNWvZfJv4Hgp zRtvpbt8mM=4HfaHR$KkSjO#GQzwh*MB{kKbnlS>GeEMtT!B$_&l>_R@@*X8(-O^F(+3dMI$o(IzeT;{9P^vH44JNZW9 zD6H4xa=a9sqJP#w@;j|j8AveQhSgaU5djqrQ;B*7-R>M^@Q-EbuX9eg4nr62F4W&u zpdY^6$DqIWpfhKq(y)Q1PmDr$TG%+kq5GJ2K5e^@joa?X%mXjgk4U%OaUgepz$1~G zN9^{D>a8Q9~~3d#{>DQ=k_P4#~U%yKjeEoy=P6fz87E?W5TDustl&DwIF-bAcpGc4}JZXSB;U90K5UUN_S$H|jH<_30xJO&p{U z!kX3ZeqF@A0zRPISp9lfM21ERjT3+WEly>4wUfMR)L+fq%=;wbH+Zy8D^EZ10eaG1 z_-FK_;dBUj9|i|j`_<{wxq<`f>DGJ`hi}Ta|7~0_gDW6P}5txj?WCSK7Fd2c# z2uwy`G6Itkn2f+=1pZDVVB&8V_blHO4r-mTWy|o+V=T;H%Tyy#dNGmVRYg0!f>>R{ zg5i#Y>U275RV^IX9Ko(GPFg^uW%eddb2B}8=P6{btFw38ueicKw<8kopqD!BEn#hQ zI2^Ty!mY4o4@5)u)<}0aG*=1%k_n;DV5^(o-X4m^4Ni%~0$QZAZob3S+HJ31RX0Cm zUq}D6aJQC-h2zNA2v0bt{RX?sv6wrviVWH#9bN6=j&M{XJyCnK6IBcc;)xjEbKPQZ z4bZEv_P2Zf*&b{Q2k(a7xK`*S8(VIgU3MM4Gfl4&q<^2y?gbosGn=IiQ0tLw_6WhZ zvRQiXuom!k?1pv&(lem8_p;d?DBtjYHaiBm5AbpD?ZA?A24FYV{`G*50BV4g16HhTpzHcaI!^LfZF!ml2_Rg#X1%2gFrU$#x(Z0%vo zFLqzGa2~G_$={A&HGJoS$WtP^hMyhu!Wa06XD5CeP=`AKDbb>PFMb-9(SHJD-ipfY z=7x%@-m-=YyZPB^6;+^kDy+WgAbNO~^t_6n6Lr3vWRR#HluY+F`FSm;e)pbB8JjKZ zdOgahUe`dT4t2870)16QWxshvMb*wSPlbKErJaJb7-$Rl6U1tW6d#5y1)SDliR^bE%dccv>V~5y( z;b%c?t|IsY{Hv(%owB`rr=`Da7n&Gf$S^j7UlVxVLtnIP2g2jxJ9kRMgJructNY7$ zPFd4Cwd_0RI4VO9X1-|}+SG%``F5~UKX?gkw+ea7dLm4z?47#ZymLx_`7XkHMc!kMySOO3~=gh^NZ&nxqy?d z2Wc`*Mqn}mlM$GVz+?m_BQP0($p{oiKz@%SzrQi=t{DI3i1;K<-zXL0^o!4g4YlF!>$OY=K(^yjVc_J&?RdmgOIv&2|#YWBiU#eB(@nDfBM~*r+I% z3$(lGCKXA3!%}6S>;eHL5Mr#JUi(23Vys8hkJbs4rW$~KLBI{dkfisBk87mgqk>P6 znY4eIpi4fyv%~8LbDV@%a5%nKFk}TT{lt=qm$!>@yaC2>SuWK7&4BzayI7%9w0FIL zO#;!+;wXmGpoxY{pDH_Hu3h_?P zg#5I&HM2^#TXYb)_WV@Pj|)1j>nJTpMWt^{QR!q zAHhPjFnv$Zne4Elkj5dmTSPwux{1wZRZRw?aFZS8^e^PT_meh07JB6TBkAQ9(sN;M zzL4}%{?Ft5v)I0ck~0q{s&6g$89SfJ@A4(Lm+Prw#r=H^r(cwtA0$5=4j@7-NgUn> z`dq|+z;2NFegU!%fnLcj&c(+lWsE+GC;rDSHz)$Ip9%UAL7@ErO3z`!u|p3NaW<%o z^GrrTi+>|pC_{GEJ0rh z`b_i1tazL@mEivx=$C_EexE7l)9u7>p2_6($M6z+Iwkb{81#waKU9MMSBU?4XfJtw z$Q8oCMJ)uW{%}nRx*v4AtYiUfE5X0D1plrQbk-bRPHccH(RMKy+hRC3+;f5qexIzRzz6w+0gJno$_yrLt&(PxpBdP8!I0 zrE0N2M2q|UEl#h$#l?SKuXho?rw@78-WYLo;q(KC(-Dcs0{BjzpFZ$+TPxQRY~mZ6 z_i7>7T7=>w656IK5Q!N~i&T=Bb?uGoTjRkNLMkvwI zu>~Rp7`jwM8^@}utJZoNy{dOj1DzbA;>;S=s|cdfu$HL}UtQyA^sQv-YX40uJbv}2 zRjZo4>(q6g6@D*CrJF0>;=}Vmz+$ z8FP)-D5oPs{%!}Yn?HD?S0onnN|IMM_0EILM| zK7XW#D3k|?G3& zEZpjt_%Ijho{%j%4J9X!(?g%jZA$^EB@w|HUy+a?;Q*Wkbg#qNX*4oNXiF3(45-D7 zl1(^s2oH|OAu7sZ;r0Lth+J2@#vHsyIFNO0>;y`gcsR%$^st&acn5dHI{Dn@2)BtD zw=D#15`!6>>J#n`hPyP}iP2F^T&pn|8#-hj#seJ@s5ca%O&KmA9rUcb;4bm6&(i)B zEw$u6Q1SVFa(_x%5X&-&vM(ZsTi~30sdMV20B%pmr6s@$<%`d&n>UV|*7W0(uSj0* zKS*dF&;A+tTTp%qdrGqYa^FotX&=iSo{IesfJ%FAQeN)MNmwlksE!mnDKGcOBA{Vw zASW;P`6QJ4dSKzHIR5v5N_%F~zT791&>`fc|B_F_`%zANa1xjMa1tI7^5j4HDg75@ z){hLeiIkW7f)dJoL1|yg%lgy#Y$U$|KT2|+QNkWkVUnl4)nfU71C3au{@glJtS9CE zD_D3c?mzzuYN5Pc$VtfGwGgrdR{mqr% zPbFcyWE8Nt{pghsG9m3h;xZT|oFxl{e6js^isYXb`)U%d5^@q0+dqv0I$ngv1|_-g zFVCkewkL53%iz2Xa#CJC7pSh|>T>KDW O(1) +// Per-texture-creation std::find in active graphic pack rules replaced with unordered_set lookup. + +#include +#include +#include +#include +#include + +// Simulates defect: vector + std::find membership check, called per texture creation +struct DefectFilter { + std::vector format_whitelist; + std::vector tilemode_whitelist; + + bool matches(int format, int tilemode) const { + if (!format_whitelist.empty()) { + if (std::find(format_whitelist.begin(), format_whitelist.end(), format) + == format_whitelist.end()) + return false; + } + if (!tilemode_whitelist.empty()) { + if (std::find(tilemode_whitelist.begin(), tilemode_whitelist.end(), tilemode) + == tilemode_whitelist.end()) + return false; + } + return true; + } +}; + +// Fixed: unordered_set membership, O(1) per lookup +struct FixedFilter { + std::unordered_set format_whitelist; + std::unordered_set tilemode_whitelist; + + bool matches(int format, int tilemode) const { + if (!format_whitelist.empty()) { + if (format_whitelist.find(format) == format_whitelist.end()) + return false; + } + if (!tilemode_whitelist.empty()) { + if (tilemode_whitelist.find(tilemode) == tilemode_whitelist.end()) + return false; + } + return true; + } +}; + +static long long bench_ns(auto fn) { + auto t0 = std::chrono::steady_clock::now(); + fn(); + auto t1 = std::chrono::steady_clock::now(); + return std::chrono::duration_cast(t1 - t0).count(); +} + +int main() { + // Build filters with F = 200 format entries and T = 50 tilemode entries + // (large texture packs may enumerate many formats and tile modes) + const int F = 200; + const int T = 50; + const int TEXTURES = 10000; // simulate 10k texture creations + + DefectFilter defect; + FixedFilter fixed; + + for (int i = 0; i < F; i++) { + defect.format_whitelist.push_back(i * 3 + 1); // scattered to avoid trivial branch pred + fixed.format_whitelist.insert(i * 3 + 1); + } + for (int i = 0; i < T; i++) { + defect.tilemode_whitelist.push_back(i * 5 + 2); + fixed.tilemode_whitelist.insert(i * 5 + 2); + } + + // Correctness: both should agree on membership + for (int f = 0; f < F * 4; f++) { + for (int t = 0; t < T * 6; t++) { + bool d = defect.matches(f, t); + bool x = fixed.matches(f, t); + assert(d == x && "mismatch between defect and fixed"); + } + } + + // Performance: TEXTURES texture lookups, scattered format/tilemode queries + volatile bool sink = false; + + auto t_defect = bench_ns([&]() { + for (int i = 0; i < TEXTURES; i++) + sink ^= defect.matches(i % (F * 4), i % (T * 6)); + }); + + auto t_fixed = bench_ns([&]() { + for (int i = 0; i < TEXTURES; i++) + sink ^= fixed.matches(i % (F * 4), i % (T * 6)); + }); + + double ratio = (double)t_defect / (double)t_fixed; + printf("cemu-0002: defect=%lldns fixed=%lldns ratio=%.1fx (F=%d T=%d textures=%d)\n", + (long long)t_defect, (long long)t_fixed, ratio, F, T, TEXTURES); + + assert(ratio > 1.5 && "Fixed implementation not measurably faster — check test"); + + printf("cemu-0002: PASS\n"); + return 0; +}