From 538d0a89eccc71e0c0b3f520d71e73b04bed9d01 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 17 Mar 2024 22:31:31 -0700 Subject: [PATCH 1/4] vk: include Wayland in the Nvidia-Intel workaround --- blade-graphics/src/vulkan/init.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/blade-graphics/src/vulkan/init.rs b/blade-graphics/src/vulkan/init.rs index 676c309e..fc12b2a0 100644 --- a/blade-graphics/src/vulkan/init.rs +++ b/blade-graphics/src/vulkan/init.rs @@ -36,9 +36,10 @@ struct AdapterCapabilities { shader_info: bool, } +#[derive(Debug)] struct SystemBugs { /// https://gitlab.freedesktop.org/mesa/mesa/-/issues/4688 - intel_unable_to_present_on_xorg: bool, + intel_unable_to_present: bool, } // See https://github.com/canonical/nvidia-prime/blob/587c5012be9dddcc17ab4d958f10a24fa3342b4d/prime-select#L56 @@ -110,8 +111,7 @@ unsafe fn inspect_adapter( log::warn!("Rejected for not presenting to the window surface"); return None; } - if bugs.intel_unable_to_present_on_xorg - && properties2_khr.properties.vendor_id == db::intel::VENDOR + if bugs.intel_unable_to_present && properties2_khr.properties.vendor_id == db::intel::VENDOR { log::warn!("Rejecting Intel for not presenting when Nvidia is present (on Linux)"); return None; @@ -341,14 +341,13 @@ impl super::Context { entry.create_instance(&create_info, None).unwrap() }; - let is_xorg = match surface_handles { - Some((_, raw_window_handle::RawDisplayHandle::Xlib(_))) => true, - Some((_, raw_window_handle::RawDisplayHandle::Xcb(_))) => true, - _ => false, - }; let bugs = SystemBugs { - intel_unable_to_present_on_xorg: is_xorg && is_nvidia_prime_forced(), + //Note: this is somewhat broad across X11/Wayland and different drivers. + // It could be narrower, but at the end of the day if the user forced Prime + // for GLX it should be safe to assume they want it for Vulkan as well. + intel_unable_to_present: is_nvidia_prime_forced(), }; + log::debug!("Bugs {:#?}", bugs); let vk_surface = surface_handles.map(|(rwh, rdh)| { ash_window::create_surface(&entry, &core_instance, rdh, rwh, None).unwrap() From e0900a208aab5948ed303d1df587afba92bf4b63 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 18 Mar 2024 13:09:46 -0700 Subject: [PATCH 2/4] Vehicle example for using Blade engine --- examples/README.md | 15 +- examples/vehicle/config.rs | 71 +++ examples/vehicle/data/ground.glb | Bin 0 -> 1300 bytes examples/vehicle/data/level.ron | 25 + examples/vehicle/data/raceFuture-body.glb | Bin 0 -> 58292 bytes examples/vehicle/data/raceFuture.ron | 65 +++ examples/vehicle/data/wheelRacing.glb | Bin 0 -> 20152 bytes examples/vehicle/main.rs | 646 ++++++++++++++++++++++ 8 files changed, 815 insertions(+), 7 deletions(-) create mode 100644 examples/vehicle/config.rs create mode 100644 examples/vehicle/data/ground.glb create mode 100644 examples/vehicle/data/level.ron create mode 100644 examples/vehicle/data/raceFuture-body.glb create mode 100644 examples/vehicle/data/raceFuture.ron create mode 100644 examples/vehicle/data/wheelRacing.glb create mode 100644 examples/vehicle/main.rs diff --git a/examples/README.md b/examples/README.md index 36423fdf..20cb428e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,9 +1,10 @@ # Blade Examples -| Example | graphics | macros | egui | asset | render | lib | -| --------- | ----------- | ------ | ------ | ------ | ------ | --- | -| mini | :star: | | | | | | -| init | :star: | :star: | | | | | -| ray-query | :star: (RT) | :star: | | | | | -| particle | :star: | :star: | :star: | | | | -| scene | :star: (RT) | :star: | :star: | :star: | :star: | | +| Example | graphics | macros | egui | asset | render | lib | +| --------- | ----------- | ------ | ------ | ------ | ------ | ------ | +| mini | :star: | | | | | | +| init | :star: | :star: | | | | | +| ray-query | :star: (RT) | :star: | | | | | +| particle | :star: | :star: | :star: | | | | +| scene | :star: (RT) | :star: | :star: | :star: | :star: | | +| vehicle | | | | | | :star: | diff --git a/examples/vehicle/config.rs b/examples/vehicle/config.rs new file mode 100644 index 00000000..bed76bf9 --- /dev/null +++ b/examples/vehicle/config.rs @@ -0,0 +1,71 @@ +#[derive(serde::Deserialize)] +pub struct Body { + pub visual: blade::config::Visual, + pub collider: blade::config::Collider, +} + +#[derive(serde::Deserialize)] +pub struct Wheel { + pub visual: blade::config::Visual, + pub collider: blade::config::Collider, +} + +#[derive(Default, serde::Deserialize)] +pub struct Motor { + pub limit: f32, + pub stiffness: f32, + pub damping: f32, +} + +#[derive(serde::Deserialize)] +pub struct Axle { + /// Side offset for each wheel. + pub x_wheels: Vec, + /// Height offset from the body. + pub y: f32, + /// Forward offset from the body. + pub z: f32, + #[serde(default)] + pub suspension: Motor, + #[serde(default)] + pub steering: Motor, +} + +fn default_additional_mass() -> blade::config::AdditionalMass { + blade::config::AdditionalMass { + density: 0.0, + shape: blade::config::Shape::Ball { radius: 0.0 }, + } +} + +#[derive(serde::Deserialize)] +pub struct Vehicle { + pub body: Body, + pub wheel: Wheel, + #[serde(default = "default_additional_mass")] + pub suspender: blade::config::AdditionalMass, + pub drive_factor: f32, + pub jump_impulse: f32, + pub roll_impulse: f32, + pub axles: Vec, +} + +#[derive(serde::Deserialize)] +pub struct Level { + #[serde(default)] + pub environment: String, + pub gravity: f32, + pub average_luminocity: f32, + pub spawn_pos: [f32; 3], + pub ground: blade::config::Object, +} + +#[derive(serde::Deserialize)] +pub struct Camera { + pub azimuth: f32, + pub altitude: f32, + pub distance: f32, + pub speed: f32, + pub target: [f32; 3], + pub fov: f32, +} diff --git a/examples/vehicle/data/ground.glb b/examples/vehicle/data/ground.glb new file mode 100644 index 0000000000000000000000000000000000000000..c9d3f391d482891ca986817206f92de3f8846ff1 GIT binary patch literal 1300 zcmb7E?QWYe6lLx9277?WzbP{xX#;Ny(!}vN2Clw+^K_v<*lu||okzAkveOn36 zc+p7#(z(qkOQ`4!KZTvGYk5`|BNJ_@C^F6<*|9L%nW%`N019xkDp0Vf5F=~wp_z`E zi1CD~0Djs%9jpqiX|dAKCjTKPP)k)JIp7zzXF0BG-{3CxUAO1n_~xrznr2NT6&cCv z9VNVsayrWrnxH_6lEOafO^%y93u7C&H<1`qN%B04r@Ty8j22L#LJ=wGfahG?lUS>@ zz*Z0I%eH+7`;KqE#$$s5>xQ5$1>%X8YwxqVJEfM=NQ<{uwN+^&vUMi2Z4)I@iYzLn zGAr~D&W7`0IMMRX@4gShaQfv3MuBajNjM$%M<{S$d>Ko!SV3H%YLo!XPD@B)2o+q^ z5=P~6NyTGEf9j$!UvD_0OwOM-Fs!caT$?E7C4*z-sbas>1Wjz}jx#tiiz{VbDO+j` zdb}IBaEL(NquQ{;|I|L9oinua2J|^MsBq4$ow_gRu;HE!`lJ37sRh!1Lt~JkHl# literal 0 HcmV?d00001 diff --git a/examples/vehicle/data/level.ron b/examples/vehicle/data/level.ron new file mode 100644 index 00000000..d6115618 --- /dev/null +++ b/examples/vehicle/data/level.ron @@ -0,0 +1,25 @@ +( + environment: "", + gravity: 9.81, + average_luminocity: 0.1, + spawn_pos: (0, 2, 0), + ground: ( + name: "ground", + visuals: [ + ( + model: "ground.glb", + pos: (0, 1, 0), + scale: 60, + ), + ], + colliders: [ + ( + density: 1.0, + friction: 1.0, + shape: Cuboid( + half: (1000, 1.0, 1000), + ), + ), + ], + ), +) diff --git a/examples/vehicle/data/raceFuture-body.glb b/examples/vehicle/data/raceFuture-body.glb new file mode 100644 index 0000000000000000000000000000000000000000..e8e667303d19917e3b2fc59bd52c62b52a81e23a GIT binary patch literal 58292 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

n5LAEvK~`! z6cpu6eIDiOW*nksT%s5U$n$uZc1KJ zXm5H>^2-TPJSPc*;`-KKkJGf-@ZypVKns=e3FcZXA!VpCT@7z34U_Ck%~58r-Afi;qT;XTR}Nj9#Z*2A$92pb+!r5-@^kap z-=W-szi&zREr%)Jrvy>HpE(kRUd)Fe%J)eXrT+V`puGRNc~GA3-~7~n|L4y?ScT5t ztDhTX%J-LiKg##3d>_mAy?DMpF?xf3&Mqn6FCU4bJk~=$_1}m4^NF78WBv5s*Za={ z{CR#);7F9u518_~LlEWj2|wlY3qR%a4L{}ck01)W;g?KlKJdKJ?~l;@iKDn~<{$lC zT0Q@Cp8T5jhg*tyALysNPYj|cZ{|mZDDOxAZ&CmLI4JLb?ZoDegz)eTe`5h2}|~nU`_Ye;*W-M|m?p%AZF$%tt@<-=}dt z=zcGVPyX(`muB8Sip%@&6a9I<-#ilK`_v%H_qj)+eE)kSigua#97p;7I*4MPoBgKV zXCjaHmT14J_o0%4kM|g3|Gd9vMA=|`^TOEQD7t@I>n3Gj`hUxmx+f-+5aW7#`g?vC z(cklJP@K0sp*m<-l6D0HsX10u5t2 z5@1{{uYS)*Nl$sOr<>%z>ae|(kJ&zR*)aLH!~QitR)N-;QDZ(Jed3q?Zdx|(9uI$K_^*_~mom`t_kdC1!a==4zy}DgF!)IPK5eI^L*|79WuYf3^t?nk zkvwFA_-q@>`fd}AEEhA6TZ*jt8SlI*sR3}$Si7S@Vz8|s-=oD7kAb2rx4B{44Iw(94Z{T zB*y=-Qby4M`rEd!U@7qnh<`to-9r3h+zc&$w3|`1y;4l(_9b15NKX3$^QV&hc#j{8 z?>|?)G=th&1ikdHfN^>Gybh)n+=001=?UVq%+ys&ym|*u-;{AAdB|{C!d_i|J&9yw zyL|GV9w{U71FM##v(6y?gjGxA-|jA8`Jl2v@xt=Sw2W65ufJEA~RofT~%W9OZ)h( zG>_h-d3ZO?DVz9T(L8)HvOcrh?GrhIN?TwySM)+^DIk+Qx|tFy02ha zdflH^hpwZ)vHpBy{nF~hzl8XtXHf&9^(c>am$(}UR) zmVZguCA@&-5$85jKR-wE7U6GVIFrUGyo|;I@}mf^B@BySB8u@tOkP76`eDO1Du=Oz z48~0v3>(R3bf5e2oPWW;YTh3iv?dho@|Bf*iFsa=Sp|%bpE2BvEhFFCznJ+_=9+gp zcrj|yCsRFKdEuONo;!1Hx&BqgvTS*<)Guw2{2)6eANH{Qemf+21k7v7NB?v9C}M`C`sl=03LS=ls?!k9ZE>l=zsl7S_d`^OiyX4B89yr#S%qRF*MfdRP@K z$U;oeICnwT3*_?}&zZjr@&ad~EX}15^E%;zxT%aOxhcSx6&LtYHp1s4Nkiv{PD)q!xYDsRwDiv#2?@Ov!%mnPHm?7iTH-O z_99_w7yZ3=YS)}+ve4f9sl8*VUBG&ril5Cw+fDxz!Un`f^y474p5tLy?d-Gk?Im}G za{CVtzm&87l^ZEGwwcf`T%-PmrTwU$hp9fa5%FOhVT>K(2F>L#;sDKG#0uD~;|Ah_ zwhM+3s5n+i#xagv7;72RIF2F4a-2bhGPZEcL0OJ76dEct^nE(3pOPPMLy_}Ypd|6h?kSfp4^2kVeiEH&w%Ha0a=%0i*Q>q_= zWP?cYk(D`mQf{*m(Kk(s8o%po#n)|tNy@)g+^Q61`YTI+G>^A|N{B_fX|3mbD86s{Volwpyhck5J^nqw z6SRIDNk;A;ARnYh^06o3do=KmbngavzL$i&ynp2TO6s>1zL$iIyjPX?r+nW!h2)>2 z_|5mKXglwjC2mLj)(c{LZ>*~okx5*(w7-$LSnEsqA^7Z{o!EI#Y>E5fZlIXzrZX~wHyNb2gr^ZMZaX^__lVdNp8oypwAVMDUKFN1F64h8{@>(V@Krqri|6TmEY>F4 zx9hqzf9Jd`)Ft_Q3nmt}BRg>ZhWl*TH-q&2p;Xaj=H5A(?7G0>DNk8h zm&Dyg|Im40!g{5QIos6ZE#=E2GePY4q#kJxWVjE1`z7&yPwGM0Ao(CZj~B|`LB6_z z=HqP?w<)jBTX3EO{4MbZ6CXamkLKfECqrZ0JKsv{8DvOBkt%pb-&11_4kJGNU-Rjn z8u1~G<{8#DNMkPcptuZ-ed7hh$Na^)*J$EnPl)va=Sq;ldQw8`3NW2772#eO`X>;7 zA@MPfh7sOIwnB#Xk41aPR$#g>F4{zV=m#HX^b~LPITiMoSj*kjJDGD#=yoeOb) zZSJGB&UJLpoqXCATIUAB4GCjiBOT#tv9)aJPQsN5Y zw*`mK3Y0!K&}Rh$sNYa8&I-_nbPnL1bzm){z6w)c;cP7vv8Ol18mt}EZ{d-Ix6)et zAoX8k%0uQl!cAj%DaE?*`BeU&>Gw2-SR>#wwBIShc?iZGK2vOb z2K#pQ8JsmPB0l^XzVVsx8TbbK415XqBYzrS!ntcY*@!&^WU!}TAFeNaxC!ycQrjRS zW4?^@GX8VSpGAGeeuA=F$j9oDkHKfym(b6jQ~P*szDzQgk<47`+c$}S7PYgYzFpBV z746df!0RLCms~e_t%N+Um6)fzKB8~s`p9@Z@pGIzEZ?$hDhe7j*8TaYMX< zFX23g`-)@N|Mnb&UzHUbhtpj0Vq@_Qg?c%)+*}oLA5c_ZV1NPuJXTyBt z{Xg1h_SoUMB=Z3IBVq#1-#88+&TxEqhRT{b7^d|?&w)L@XO0!&U+jC%sM|+lSy8@x zw$(AVMr+$hRRQai{09S`IR?o{J+dyTN6JV{&jG!2@*qC#4RBUd%4S(MNG6Cc>yr4t zdY)JwJ-3PH!qSGa`0PJY-j9Q1g80~1U@vhKt$mBOb$o@+&-MMtt}}WsG4Z+Zn&VGj z(vzO!xj(Jedw^_`If>R#%pL66c~5c+ooBzcXLaZ+T0`p*Kb7J<<_q2hz*?0=XWdx) z`qKO*yVZj@|0a2fb0$5Pjr)ckMrJ(8^d=e1O{~{sCvYY`E7a%wn1jIlJdxjHd1T#` z#dt4exp|-DFT&4>cT4aNj9(_GEcCpVKu-`KdX^Y@DI@ue(Lc0j3>Q)i!dj2_9q1XK zZsSq3r@~r=b)TN&h3U*)V?Mh;KbZ3Y9lLOTfM*1VUD)@+g)`3=EvA8%{T&I%9#CC z82YjA!(IwHKb==8)P?Rtu^-qwX=3OZ!$(=z3K^rv!xiD1{hs!1)Jtns!g}W#KJO1v zuYXU7y&~2l-XA8ijrycQcA_otEBMk9;a95&W3LEb!uvCLj?6xWdND4OMgKE4{pO8V zB0kHYUf!?5pLvf8U*)|k>gD~a+F5;B>!Q)&eb=`M-F9#FEa3fwVNdP@H-+y#`u#Xu z*fJ@r4apQ68S-oNeZeb}&R*6w0iWWY9vjF=Y|5dUK)#fLpvf0#XzKm%aOpJ;71Do2 zu{z4akcjm|yu9Q~Tppc=W#~LJ?TYjv;hCGB&_2*CV@P=ZhQs&?gPRk74q?c*B>6{$ zJY>Mn%^VV5v^Kabtd#m$XFOlGRX51A`~Ma$$553_fQm z&}`!a6Zf6MXI16m%d+Ls86-2W`9!>jQOZuqubq7l-p?pgM%oi3zx}kPUK^zhwLM{< zNO`HhJn~WoXZz^?)L&aHmwK50Q^S_o<>5=2cwE}I51cYR;q%HM{?y1uPrlNQ!)ISX z@;{B8k9|1C1pf*c%d(O`-uls#-+aNq*jWV$NO`Ff@-p@^&f0fhLOuSvSRQ=Ff7~*a z$Di1TZT|M1qXO$nRQ8jxvpxHJj80|WiPO2R-L_0F8)UQWb7_z4hx6|n>gf-XU%TRU zuTM}fkA<{T>M4(mv`6YM4`0fZg}Hww{r#wCzrR0O26`Anp0OO$pt4f`ro5V7e@dOw zPQMKIXA?EX>su+)KI4KVQig4lIwh9!lAms z+?4wFCM}Qa2euz`fpK}{WxeImDcd634n2wbEl3`1OeAmGUPQX_uYms3c=wdbXm2V0 zgU{|ndrR?U*&w_%7RO4Jl{)=0WV4oU(qZEg#!~;y3#TM}kMY}gCa1BTFF!fk!#nnT zmc@F4becGz>wSIuKVCm{`*W~|2RSu7Jmr?{@MkSww`v0qOFMhL>3aQkS@C)gfB4Y3 z8C-TwxRHm`r=EC3J7r%<+vM1xZ@InFo^$W}&9gtK?79_~#M#4k%KnjhVDph=WE-V^ z+0W&Xk$o<)>{~e&L3}wb63cOsV-0?y{**HB?OUDD2jGW^%9_}y{Rd^sBg6eH$5h4Q zt=1!TcF0&D=7^5TT87RW1MOt}7Z)6k15!R-=i#_<_Bq*z`C7lUr{2M8p1%c^HLl9w?dsI2VIa|mDDNAN}00wtOqicRVC%Bs;Y`QN*$|?QAblcR#j6csN+?2b)2f9PEseU z6ID%hDxp&-ovKcw `GI-O`Obq0L{ouO(|?o@RqeNI(%)LH6ml|RjTSqw1>j z)p@EOrSsJVs=m5NT}Y|EYM_$U#i}8tWR;@QRhmkrl&&&Vw#rhOl(JQhYOF3%jVLu% zO;oN5s}QAJ6;aJpQQf*XQ)mmLa zw4G{C-|eX*=>L3HHhtQwD^*9;L0v_uqv}MTj_PXFndsG&I;$@9>8!4!PiNJYKAqL| z^y#d+sTUs5|TBu&2^rCu6y`mPWmnprX7OUlIsam35Rm;>0`d*=4S1Z+Ps+j01 z6;&mwNWG!fs6zF&TCLtx@2GdxTk1XfeowuxK2U4bdZO#p2Kua1AFGelhw2lepQ?@Y z`BZIIo75JyU2RpLsU2#Y+Nr)!U#id5H|i_(t@=*wQeUg@>GQSvLH$Ua-Rc+hliH*9 zs$bRb>NoYX+Nb`cXL$Q5{izP9f7IXVFG~NYgY@}F9a6SsS&EWvIaXz>lI2pWY#n7C zZB@0ZP&(Q=#;R^rvyP=y-8#-X!Kz^$Pw52fM60HCvUL)rn${^+E$cMvR7$n1)2%bD z+SVDA&a}?5&auw6>R3tExmG=^F6GX*&a*DCF0|6EhSo)t(ya{o&ag7Ai>)jx$7*0@ zTbEe%twvU3E5&MJg{@>OWaV0^R>W#bl6h7$E6uvpYHqc(F0)!tYH3|=wYFMWS5Rtg zwXxb;?X0$x+FMsz9jy-5Rg^kfovhB*HP+RXI$PITU9Ib^E|j`j*IV7KZq^M}4@xoG z)9PjATQ^ebW%aghvie$mDBWb;Oy4(K{j7o30INTxfz}{vh&9-{h0+jfD1C-l!>p0k z2x~Z{k=7_{tTo0OO=+w(&ML5Ovu>qSVBKy_v?f^NDNVE{SyQaZ)*Y0lSa(`?TT`vO zDBW${L!Y~?d+GaL>puG2Yu#^6vmUT!SktYCteMus)`Qk8>k;cw>oNK~YR#ta+14EE zN$YXzY3m8=DQhm}p0S>#&okCM`krS!XFYGtw_dasSPQL}t(UA9tXHf>))H%(wbWW{ zEvN70)@#Mrnt&)B4)_!us6$ z()z~QWqoUXWqoJuwtld_r?lJpi9Wlnz1Gjx9_v@@7s9{MXSek`eRf;>=(F4U!`g5C zX#Hs&ApE!W7vX;_WgoH*T8D^Qwre}KP06(@*;VYL?8=m?*j4FM#Xj1uW*=)GL#diw z-L7FDZy!gghJAv4vVD?$BBhhd* z8I-c@Y`d|2iJe2Ku^qA-*-h-Qoo7evTuOO%Q~GXdH?v#V&FxDmwXiRvPYb)H-O9eg zzMN7kyS3fUZfm!p)Xr{icd)OrucXw$?r2|QUu}1ybdBBFzRvDqUrXsayQ|&JzQMkp zQa8IheY)8_=(~sAlRiD{UiOW4Z@aIZZ{KA1w{N!l*aPf-_CR|OeFoaM(DyC&PGp&61NQxt9<*oJkJt~}4^eu=o@qa3KWfjS^q4){o?|~@KTc_m z{iHqDe%gMD(p>u)d%iu-ewNaFdx8Ce{k;90y^y|NuwSHapcn0z>{skX_RExBu@~D* z?N{w3l$P4d?APoS_Hs(E*{|D$_9}ZNr9!*NF0rF_F{KiFwf(03hP{T;oAz7wyY@Tw z+mzn5-=pvM?6vlX_6PR+ls>fA*&o>(?DdpBvOl&r+Mn8=P}*p3vbWfq?awG}vA5bg z?Cth8N;~YG^u5#moIX44FYK@Fuk0@=eQkeZe`oKqzoqn@{k^^0{?Yz{(r$Z?{jQr%#qEyv6+NtIo>l{O=np54W;T-QAN2!K$f^&*q~u6P(+f0%yE4(V65-aV9%=P@3Z0>D=qw?cC)| zb?$NQbMAMhJJTrL?>yi<m+<2>y= z<;-=Sb>=zGISZWml%8{*cV2WBIxkRq(Rs;v#aZOMOz9P8v9rv1)mh>!b(T9ToR!Y& z&TEubI;-eYNT`UuSJ8Jd(PE-eXN|MkDWSB+dBb^=(i-|+<-Fs(OZYv_UrY3T zXD#K|68*sW#QDhi&{^+%?0o8Ma5g%hIh&nz&L(G@v(?!`X`8d1KHHof^u5E`NuM3g z=k(d(d_kWb&X@Gr;e6$M>wM#UP3c=_7k%$?zN61B=X?78-uZ#Pe{g=J?;oAr^u62J zIR4PjruWPjF8ne6m}UzH7RtxM#Sv+*94N+|%6C z-6XdT*M!Tu*5O=f31V=Dz4IaF@7?+-KZb z?yK%A?oxN5yV#xUzUD4-Uv`(fue*=BE8P|D<8Gn5%6-a>x<&32Zi!p$KIyJ;SG!NU zZ@F){v)y;xH{JK$b?)0#?|beX_g!~``=Ptu{lxvi{mlJ{cn`Uo-L>vcccc3;akdk0 zoBM_PDan6J`LErr?k0D)yTje(KHz@ue&hb+eo0c_xqI9#?hN-=_eb{^;{D+M>3-$@ z?EdEd;qG-mclW!0)7E*Wd(eH@Rh52s|8n=yw&Nf7fP2WTT*9;Js-`2x#SIXbo6J|5*0U z@Rmingulz%pY=B3vmYH9#xRZ}8HtH?09i$y05j*6YVR3ds5ox9)+rm8-F`9Da+%c}qY literal 0 HcmV?d00001 diff --git a/examples/vehicle/data/raceFuture.ron b/examples/vehicle/data/raceFuture.ron new file mode 100644 index 00000000..80b3c318 --- /dev/null +++ b/examples/vehicle/data/raceFuture.ron @@ -0,0 +1,65 @@ +( + body: ( + visual: ( + model: "raceFuture-body.glb", + pos: (0, -0.3, 0.1), + rot: (0, 180, 0), + ), + collider: ( + density: 100.0, + shape: Cuboid( + half: (0.65, 0.2, 1.2), + ), + ), + ), + wheel: ( + visual: ( + model: "wheelRacing.glb", + ), + collider: ( + density: 100.0, + friction: 1.0, + rot: (0, 0, 90), + shape: Cylinder( + half_height: 0.1, + radius: 0.28, + ), + ), + ), + suspender: ( + density: 100.0, + shape: Ball( + radius: 0.28, + ), + ), + drive_factor: 100.0, + jump_impulse: 10, + roll_impulse: 10, + axles: [ + ( + x_wheels: [-0.5, 0.5], + y: -0.1, + z: 0.7, + suspension: ( + limit: 0.02, + stiffness: 100000, + damping: 10000, + ), + steering: ( + limit: 30, + stiffness: 100000, + damping: 10000, + ), + ), + ( + x_wheels: [-0.5, 0.5], + y: -0.1, + z: -0.8, + suspension: ( + limit: 0.03, + stiffness: 100000, + damping: 10000, + ), + ), + ], +) diff --git a/examples/vehicle/data/wheelRacing.glb b/examples/vehicle/data/wheelRacing.glb new file mode 100644 index 0000000000000000000000000000000000000000..3dbc8afa4702a2ccb4e2a102cbda2c2d2f677c5b GIT binary patch literal 20152 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 T$|-M9kl(1B@+JlOTHt>GJtG!E literal 0 HcmV?d00001 diff --git a/examples/vehicle/main.rs b/examples/vehicle/main.rs new file mode 100644 index 00000000..c5a72778 --- /dev/null +++ b/examples/vehicle/main.rs @@ -0,0 +1,646 @@ +mod config; + +use std::{f32::consts, fs, mem, path::PathBuf, time}; + +#[derive(Clone)] +struct Wheel { + object: blade::ObjectHandle, + spin_joint: blade::JointHandle, + suspender: Option, + steer_joint: Option, +} + +struct WheelAxle { + wheels: Vec, + steer: Option, +} + +struct Vehicle { + body_handle: blade::ObjectHandle, + drive_factor: f32, + jump_impulse: f32, + roll_impulse: f32, + wheel_axles: Vec, +} + +struct Game { + // engine stuff + engine: blade::Engine, + last_physics_update: time::Instant, + last_camera_update: time::Instant, + last_camera_base_quat: nalgebra::UnitQuaternion, + is_paused: bool, + // windowing + window: winit::window::Window, + egui_state: egui_winit::State, + egui_viewport_id: egui::ViewportId, + // game data + _ground_handle: blade::ObjectHandle, + vehicle: Vehicle, + cam_config: config::Camera, + spawn_pos: nalgebra::Vector3, +} + +const SUSPENSION_AXIS: rapier3d::dynamics::JointAxis = rapier3d::dynamics::JointAxis::Y; +const STEERING_AXIS: rapier3d::dynamics::JointAxis = rapier3d::dynamics::JointAxis::AngY; +const SPIN_AXIS: rapier3d::dynamics::JointAxis = rapier3d::dynamics::JointAxis::AngX; + +#[derive(Debug)] +struct QuitEvent; + +impl Drop for Game { + fn drop(&mut self) { + self.engine.destroy(); + } +} + +impl Game { + fn new(event_loop: &winit::event_loop::EventLoop<()>) -> Self { + log::info!("Initializing"); + + let window = winit::window::WindowBuilder::new() + .with_title("RayCraft") + .build(event_loop) + .unwrap(); + + let cam_config = config::Camera { + distance: 5.0, + azimuth: 0.0, + altitude: 0.5, + speed: 0.05, + target: [0.0, 1.0, 0.0], + fov: 1.0, + }; + + let data_path = PathBuf::from("examples/vehicle/data"); + let mut engine = blade::Engine::new( + &window, + &blade::config::Engine { + shader_path: "blade-render/code".to_string(), + data_path: data_path.as_os_str().to_string_lossy().into_owned(), + time_step: 0.01, + }, + ); + + let lev_config: config::Level = ron::de::from_bytes( + &fs::read(data_path.join("level.ron")).expect("Unable to open the level config"), + ) + .expect("Unable to parse the level config"); + engine.set_environment_map(&lev_config.environment); + engine.set_gravity(lev_config.gravity); + engine.set_average_luminosity(lev_config.average_luminocity); + + let ground_handle = engine.add_object( + &lev_config.ground, + nalgebra::Isometry3::default(), + blade::BodyType::Fixed, + ); + + let veh_config: config::Vehicle = ron::de::from_bytes( + &fs::read(data_path.join("raceFuture.ron")).expect("Unable to open the vehicle config"), + ) + .expect("Unable to parse the vehicle config"); + let body_config = blade::config::Object { + name: "vehicle/body".to_string(), + visuals: vec![veh_config.body.visual], + colliders: vec![veh_config.body.collider], + additional_mass: None, + }; + let spawn_pos = nalgebra::Vector3::from(lev_config.spawn_pos); + let mut vehicle = Vehicle { + body_handle: engine.add_object( + &body_config, + nalgebra::Isometry3::new(spawn_pos, nalgebra::Vector3::zeros()), + blade::BodyType::Dynamic, + ), + drive_factor: veh_config.drive_factor, + jump_impulse: veh_config.jump_impulse, + roll_impulse: veh_config.roll_impulse, + wheel_axles: Vec::new(), + }; + let wheel_config = blade::config::Object { + name: "vehicle/wheel".to_string(), + visuals: vec![veh_config.wheel.visual], + colliders: vec![veh_config.wheel.collider], + additional_mass: None, + }; + let suspender_config = blade::config::Object { + name: "vehicle/suspender".to_string(), + visuals: vec![], + colliders: vec![], + additional_mass: Some(veh_config.suspender), + }; + + //Note: in the vehicle coordinate system X=left, Y=up, Z=forward + for ac in veh_config.axles { + let joint_kind = blade::JointKind::Soft; + let mut wheels = Vec::new(); + + for wheel_x in ac.x_wheels { + let offset = nalgebra::Vector3::new(wheel_x, ac.y, ac.z); + let rotation = if wheel_x > 0.0 { + nalgebra::Vector3::y_axis().scale(consts::PI) + } else { + nalgebra::Vector3::zeros() + }; + + let wheel_handle = engine.add_object( + &wheel_config, + nalgebra::Isometry3::new(spawn_pos + offset, rotation), + blade::BodyType::Dynamic, + ); + + wheels.push(if ac.steering.limit > 0.0 || ac.suspension.limit > 0.0 { + let max_angle = ac.steering.limit.to_radians(); + let mut locked_axes = rapier3d::dynamics::JointAxesMask::LOCKED_FIXED_AXES; + if ac.steering.limit > 0.0 { + locked_axes ^= STEERING_AXIS.into(); + } + if ac.suspension.limit > 0.0 { + locked_axes ^= SUSPENSION_AXIS.into(); + } + + let suspender_handle = engine.add_object( + &suspender_config, + nalgebra::Isometry3::new(spawn_pos + offset, nalgebra::Vector3::zeros()), + blade::BodyType::Dynamic, + ); + + let suspension_joint = engine.add_joint( + vehicle.body_handle, + suspender_handle, + rapier3d::dynamics::GenericJointBuilder::new(locked_axes) + .contacts_enabled(false) + .local_anchor1(offset.into()) + .limits(SUSPENSION_AXIS, [0.0, ac.suspension.limit]) + .motor_position( + SUSPENSION_AXIS, + 0.0, + ac.suspension.stiffness, + ac.suspension.damping, + ) + .limits(STEERING_AXIS, [-max_angle, max_angle]) + .motor_position( + STEERING_AXIS, + 0.0, + ac.steering.stiffness, + ac.steering.damping, + ) + .build(), + joint_kind, + ); + + let wheel_joint = engine.add_joint( + suspender_handle, + wheel_handle, + rapier3d::dynamics::GenericJointBuilder::new( + rapier3d::dynamics::JointAxesMask::LOCKED_REVOLUTE_AXES, + ) + .contacts_enabled(false) + .local_frame2(nalgebra::Isometry3::rotation(rotation)) + .build(), + joint_kind, + ); + + let _extra_joint = engine.add_joint( + vehicle.body_handle, + wheel_handle, + rapier3d::dynamics::GenericJoint { + contacts_enabled: false, + ..Default::default() + }, + joint_kind, + ); + + Wheel { + object: wheel_handle, + spin_joint: wheel_joint, + suspender: Some(suspender_handle), + steer_joint: Some(suspension_joint), + } + } else { + let locked_axes = + rapier3d::dynamics::JointAxesMask::LOCKED_FIXED_AXES ^ SPIN_AXIS.into(); + + let wheel_joint = engine.add_joint( + vehicle.body_handle, + wheel_handle, + rapier3d::dynamics::GenericJointBuilder::new(locked_axes) + .contacts_enabled(false) + .local_anchor1(offset.into()) + .local_frame2(nalgebra::Isometry3::rotation(rotation)) + .build(), + joint_kind, + ); + + Wheel { + object: wheel_handle, + spin_joint: wheel_joint, + suspender: None, + steer_joint: None, + } + }); + } + + vehicle.wheel_axles.push(WheelAxle { + wheels, + steer: if ac.steering.limit > 0.0 { + Some(ac.steering) + } else { + None + }, + }); + } + + let egui_context = egui::Context::default(); + let egui_viewport_id = egui_context.viewport_id(); + let egui_state = + egui_winit::State::new(egui_context, egui_viewport_id, &window, None, None); + + Self { + engine, + last_physics_update: time::Instant::now(), + last_camera_update: time::Instant::now(), + last_camera_base_quat: Default::default(), + is_paused: false, + window, + egui_state, + egui_viewport_id, + _ground_handle: ground_handle, + vehicle, + cam_config, + spawn_pos, + } + } + + fn set_velocity(&mut self, velocity: f32) { + self.engine.wake_up(self.vehicle.body_handle); + self.update_time(); + for wax in self.vehicle.wheel_axles.iter() { + for wheel in wax.wheels.iter() { + self.engine[wheel.spin_joint].set_motor_velocity( + SPIN_AXIS, + velocity, + self.vehicle.drive_factor, + ); + } + } + } + + fn set_steering(&mut self, angle_rad: f32) { + self.update_time(); + for wax in self.vehicle.wheel_axles.iter() { + let steer = match wax.steer { + Some(ref steer) => steer, + None => continue, + }; + for wheel in wax.wheels.iter() { + if let Some(handle) = wheel.steer_joint { + self.engine[handle].set_motor_position( + STEERING_AXIS, + angle_rad, + steer.stiffness, + steer.damping, + ); + } + } + } + } + + fn teleport_object_rel( + &mut self, + handle: blade::ObjectHandle, + transform: &nalgebra::Isometry3, + ) { + let prev = self.engine.get_object_isometry_approx(handle); + let next = transform * prev; + self.engine.teleport_object(handle, next); + } + + fn teleport(&mut self, position: nalgebra::Vector3) { + let old_isometry_inv = self + .engine + .get_object_isometry_approx(self.vehicle.body_handle) + .inverse(); + let new_isometry = nalgebra::Isometry3 { + rotation: Default::default(), + translation: position.into(), + }; + self.engine + .teleport_object(self.vehicle.body_handle, new_isometry); + + let relative = new_isometry * old_isometry_inv; + let waxes = mem::take(&mut self.vehicle.wheel_axles); + for wax in waxes.iter() { + for wheel in wax.wheels.iter() { + if let Some(suspender) = wheel.suspender { + self.teleport_object_rel(suspender, &relative); + } + self.teleport_object_rel(wheel.object, &relative); + } + } + self.vehicle.wheel_axles = waxes; + } + + fn update_time(&mut self) { + let engine_dt = self.last_physics_update.elapsed().as_secs_f32(); + self.last_physics_update = time::Instant::now(); + if !self.is_paused { + //self.align_wheels(); + self.engine.update(engine_dt); + } + } + + fn on_event( + &mut self, + event: &winit::event::WindowEvent, + ) -> Result { + let response = self.egui_state.on_window_event(&self.window, event); + if response.repaint { + self.window.request_redraw(); + } + if response.consumed { + return Ok(winit::event_loop::ControlFlow::Poll); + } + + match *event { + winit::event::WindowEvent::KeyboardInput { + event: + winit::event::KeyEvent { + physical_key: winit::keyboard::PhysicalKey::Code(key_code), + state: winit::event::ElementState::Pressed, + .. + }, + .. + } => match key_code { + winit::keyboard::KeyCode::Escape => { + return Err(QuitEvent); + } + winit::keyboard::KeyCode::ArrowUp => { + self.set_velocity(100.0); + } + winit::keyboard::KeyCode::ArrowDown => { + self.set_velocity(-20.0); + } + winit::keyboard::KeyCode::ArrowLeft => { + self.set_steering(1.0); + } + winit::keyboard::KeyCode::ArrowRight => { + self.set_steering(-1.0); + } + winit::keyboard::KeyCode::Comma => { + let forward = self + .engine + .get_object_isometry_approx(self.vehicle.body_handle) + .transform_vector(&nalgebra::Vector3::z_axis()); + self.engine.apply_torque_impulse( + self.vehicle.body_handle, + -self.vehicle.roll_impulse * forward, + ); + } + winit::keyboard::KeyCode::Period => { + let forward = self + .engine + .get_object_isometry_approx(self.vehicle.body_handle) + .transform_vector(&nalgebra::Vector3::z_axis()); + self.engine.apply_torque_impulse( + self.vehicle.body_handle, + self.vehicle.roll_impulse * forward, + ); + } + winit::keyboard::KeyCode::Space => { + let mut up = self + .engine + .get_object_isometry_approx(self.vehicle.body_handle) + .transform_vector(&nalgebra::Vector3::y_axis()); + up.y = up.y.abs(); + self.engine + .apply_impulse(self.vehicle.body_handle, self.vehicle.jump_impulse * up); + } + _ => {} + }, + winit::event::WindowEvent::KeyboardInput { + event: + winit::event::KeyEvent { + physical_key: winit::keyboard::PhysicalKey::Code(key_code), + state: winit::event::ElementState::Released, + .. + }, + .. + } => match key_code { + winit::keyboard::KeyCode::ArrowUp | winit::keyboard::KeyCode::ArrowDown => { + self.set_velocity(0.0); + } + winit::keyboard::KeyCode::ArrowLeft | winit::keyboard::KeyCode::ArrowRight => { + self.set_steering(0.0); + } + _ => {} + }, + winit::event::WindowEvent::CloseRequested => { + return Err(QuitEvent); + } + winit::event::WindowEvent::RedrawRequested => { + let wait = self.on_draw(); + + return Ok( + if let Some(repaint_after_instant) = std::time::Instant::now().checked_add(wait) + { + winit::event_loop::ControlFlow::WaitUntil(repaint_after_instant) + } else { + winit::event_loop::ControlFlow::Wait + }, + ); + } + _ => {} + } + + Ok(winit::event_loop::ControlFlow::Poll) + } + + fn populate_hud(&mut self, ui: &mut egui::Ui) { + egui::CollapsingHeader::new("Camera") + .default_open(true) + .show(ui, |ui| { + ui.add( + egui::Slider::new(&mut self.cam_config.distance, 1.0..=1000.0) + .text("Distance") + .logarithmic(true), + ); + ui.horizontal(|ui| { + ui.label("Target"); + ui.add(egui::DragValue::new(&mut self.cam_config.target[1])); + ui.add(egui::DragValue::new(&mut self.cam_config.target[2])); + }); + ui.horizontal(|ui| { + let eps = 0.01; + ui.label("Angle"); + ui.add( + egui::DragValue::new(&mut self.cam_config.azimuth) + .clamp_range(-consts::PI..=consts::PI) + .speed(0.1), + ); + ui.add( + egui::DragValue::new(&mut self.cam_config.altitude) + .clamp_range(eps..=consts::FRAC_PI_2 - eps) + .speed(0.1), + ); + }); + ui.add(egui::Slider::new(&mut self.cam_config.fov, 0.5f32..=2.0f32).text("FOV")); + ui.add( + egui::Slider::new(&mut self.cam_config.speed, 0.0..=1.0).text("Rotate speed"), + ); + ui.toggle_value(&mut self.is_paused, "Pause"); + }); + + egui::CollapsingHeader::new("Dynamics") + .default_open(true) + .show(ui, |ui| { + ui.horizontal(|ui| { + ui.label("Spawn pos"); + ui.add(egui::DragValue::new(&mut self.spawn_pos.x)); + ui.add(egui::DragValue::new(&mut self.spawn_pos.y)); + ui.add(egui::DragValue::new(&mut self.spawn_pos.z)); + }); + ui.horizontal(|ui| { + if ui.button("Recover").clicked() { + let pos = self + .engine + .get_object_isometry_approx(self.vehicle.body_handle) + .translation + .vector; + let bounds = self.engine.get_object_bounds(self.vehicle.body_handle); + self.teleport( + pos + bounds + .half_extents() + .component_mul(&nalgebra::Vector3::y_axis()), + ); + } + if ui.button("Respawn").clicked() { + self.teleport(self.spawn_pos); + } + }); + ui.add( + egui::DragValue::new(&mut self.vehicle.jump_impulse).prefix("Jump impulse: "), + ); + ui.add( + egui::DragValue::new(&mut self.vehicle.roll_impulse).prefix("Roll impulse: "), + ); + }); + + self.engine.populate_hud(ui); + } + + fn on_draw(&mut self) -> time::Duration { + self.update_time(); + + let raw_input = self.egui_state.take_egui_input(&self.window); + let egui_context = self.egui_state.egui_ctx().clone(); + let egui_output = egui_context.run(raw_input, |egui_ctx| { + let frame = { + let mut frame = egui::Frame::side_top_panel(&egui_ctx.style()); + let mut fill = frame.fill.to_array(); + for f in fill.iter_mut() { + *f = (*f as u32 * 7 / 8) as u8; + } + frame.fill = + egui::Color32::from_rgba_premultiplied(fill[0], fill[1], fill[2], fill[3]); + frame + }; + egui::SidePanel::right("engine") + .frame(frame) + .show(egui_ctx, |ui| self.populate_hud(ui)); + }); + + self.egui_state + .handle_platform_output(&self.window, egui_output.platform_output); + + let camera = { + let veh_isometry = self.engine.get_object_isometry(self.vehicle.body_handle); + // Projection of the rotation of the vehicle on the Y axis + let projection = veh_isometry + .rotation + .quaternion() + .imag() + .dot(&nalgebra::Vector3::y_axis()); + let base_quat_nonorm = nalgebra::Quaternion::from_parts( + veh_isometry.rotation.quaternion().w, + nalgebra::Vector3::y_axis().scale(projection), + ); + let validity = base_quat_nonorm.norm(); + let base_quat = nalgebra::UnitQuaternion::new_normalize(base_quat_nonorm); + + let camera_dt = self.last_camera_update.elapsed().as_secs_f32(); + self.last_physics_update = time::Instant::now(); + + let cc = &self.cam_config; + let smooth_t = (-camera_dt * cc.speed * validity).exp(); + let smooth_quat = nalgebra::UnitQuaternion::new_normalize( + base_quat.lerp(&self.last_camera_base_quat, smooth_t), + ); + let base = + nalgebra::geometry::Isometry3::from_parts(veh_isometry.translation, smooth_quat); + self.last_camera_base_quat = smooth_quat; + + //TODO: `nalgebra::Point3::from(mint::Vector3)` doesn't exist? + let source = nalgebra::Vector3::from(cc.target) + + nalgebra::Vector3::new( + -cc.azimuth.sin() * cc.altitude.cos(), + cc.altitude.sin(), + -cc.azimuth.cos() * cc.altitude.cos(), + ) + .scale(cc.distance); + let local = nalgebra::geometry::Isometry3::look_at_rh( + &source.into(), + &nalgebra::Vector3::from(cc.target).into(), + &nalgebra::Vector3::y_axis(), + ); + blade::Camera { + isometry: base * local.inverse(), + fov_y: cc.fov, + } + }; + + let primitives = self + .egui_state + .egui_ctx() + .tessellate(egui_output.shapes, egui_output.pixels_per_point); + self.engine.render( + &camera, + &primitives, + &egui_output.textures_delta, + self.window.inner_size(), + self.window.scale_factor() as f32, + ); + + egui_output.viewport_output[&self.egui_viewport_id].repaint_delay + } +} + +fn main() { + env_logger::init(); + let event_loop = winit::event_loop::EventLoop::new().unwrap(); + let mut game = Game::new(&event_loop); + let mut last_event = time::Instant::now(); + + event_loop + .run(|event, target| { + let _delta = last_event.elapsed().as_secs_f32(); + last_event = time::Instant::now(); + + match event { + winit::event::Event::AboutToWait => { + game.window.request_redraw(); + } + winit::event::Event::WindowEvent { event, .. } => match game.on_event(&event) { + Ok(control_flow) => { + target.set_control_flow(control_flow); + } + Err(QuitEvent) => { + target.exit(); + } + }, + _ => {} + } + }) + .unwrap(); +} From 1e3a31c93e3368166bb2d3c874b0643f90c7e12a Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 18 Mar 2024 13:10:02 -0700 Subject: [PATCH 3/4] Add screenshot of the vehicle example --- docs/README.md | 3 ++- docs/raycraft-colliders.jpg | Bin 0 -> 51160 bytes examples/README.md | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100755 docs/raycraft-colliders.jpg diff --git a/docs/README.md b/docs/README.md index bc8d971b..e2c3cc20 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,8 @@ Blade is an innovative rendering solution for Rust. It starts with a lean [low-l ## Examples ![scene editor](../blade-egui/etc/scene-editor.jpg) -![particles example](../blade-graphics/etc/particles.png) +![particle example](../blade-graphics/etc/particles.png) +![vehicle example](vehicle-colliders.jpg) ![sponza scene](../blade-render/etc/sponza.jpg) ## Instructions diff --git a/docs/raycraft-colliders.jpg b/docs/raycraft-colliders.jpg new file mode 100755 index 0000000000000000000000000000000000000000..4b90b783316f2f2700dd61b434ef1ac1d6871423 GIT binary patch literal 51160 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 literal 0 HcmV?d00001 diff --git a/examples/README.md b/examples/README.md index 20cb428e..e45e1139 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,6 @@ # Blade Examples -| Example | graphics | macros | egui | asset | render | lib | +| Example | graphics | macros | egui | asset | render | lib | | --------- | ----------- | ------ | ------ | ------ | ------ | ------ | | mini | :star: | | | | | | | init | :star: | :star: | | | | | From f827e017c3436a13bd2719362076a2867d443896 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 18 Mar 2024 17:29:55 -0700 Subject: [PATCH 4/4] vk: fix sync validation error on present() --- blade-graphics/src/vulkan/command.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/blade-graphics/src/vulkan/command.rs b/blade-graphics/src/vulkan/command.rs index cc903fbf..15010a8b 100644 --- a/blade-graphics/src/vulkan/command.rs +++ b/blade-graphics/src/vulkan/command.rs @@ -297,6 +297,7 @@ impl super::CommandEncoder { pub fn present(&mut self, frame: super::Frame) { assert_eq!(self.present, None); + let wa = &self.device.workarounds; self.present = Some(super::Presentation { image_index: frame.image_index, acquire_semaphore: frame.acquire_semaphore, @@ -313,12 +314,13 @@ impl super::CommandEncoder { base_array_layer: 0, layer_count: 1, }) + .src_access_mask(vk::AccessFlags::MEMORY_WRITE | wa.extra_sync_src_access) .build(); unsafe { self.device.core.cmd_pipeline_barrier( self.buffers[0].raw, - vk::PipelineStageFlags::TOP_OF_PIPE, vk::PipelineStageFlags::ALL_COMMANDS, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, vk::DependencyFlags::empty(), &[], &[],