mod.rs 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2025 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use darkfi_serial::{
  19. async_trait, AsyncEncodable, AsyncWrite, Decodable, Encodable, FutAsyncWriteExt,
  20. SerialDecodable, SerialEncodable,
  21. };
  22. #[cfg(target_os = "android")]
  23. use miniquad::native::egl;
  24. use miniquad::{
  25. conf, window, Backend, Bindings, BlendFactor, BlendState, BlendValue, BufferLayout,
  26. BufferSource, BufferType, BufferUsage, Equation, EventHandler, KeyCode, KeyMods, MouseButton,
  27. PassAction, Pipeline, PipelineParams, RenderingBackend, ShaderMeta, ShaderSource, TouchPhase,
  28. UniformDesc, UniformType, VertexAttribute, VertexFormat,
  29. };
  30. use parking_lot::Mutex as SyncMutex;
  31. use std::{
  32. collections::HashMap,
  33. fs::File,
  34. io::Write,
  35. path::PathBuf,
  36. sync::{
  37. atomic::{AtomicU32, Ordering},
  38. Arc,
  39. },
  40. };
  41. use tracing::debug;
  42. pub mod anim;
  43. use anim::{Frame as AnimFrame, GfxSeqAnim};
  44. mod favico;
  45. mod linalg;
  46. pub use linalg::{Dimension, Point, Rectangle};
  47. mod shader;
  48. mod trax;
  49. use trax::get_trax;
  50. use crate::{
  51. error::{Error, Result},
  52. prop::{BatchGuardId, PropertyAtomicGuard},
  53. ExecutorPtr, GOD,
  54. };
  55. // This is very noisy so suppress output by default
  56. const DEBUG_RENDER: bool = false;
  57. const DEBUG_GFXAPI: bool = false;
  58. const DEBUG_TRAX: bool = false;
  59. #[macro_export]
  60. macro_rules! gfxtag {
  61. ($s:expr) => {{
  62. Some($s)
  63. }};
  64. }
  65. pub use crate::gfxtag;
  66. pub type DebugTag = Option<&'static str>;
  67. macro_rules! t { ($($arg:tt)*) => { trace!(target: "gfx", $($arg)*); } }
  68. macro_rules! e { ($($arg:tt)*) => { error!(target: "gfx", $($arg)*); } }
  69. #[cfg(target_os = "android")]
  70. pub fn get_window_size_filename() -> PathBuf {
  71. crate::android::get_appdata_path().join("window_size")
  72. }
  73. #[cfg(not(target_os = "android"))]
  74. pub fn get_window_size_filename() -> PathBuf {
  75. dirs::cache_dir().unwrap().join("darkfi/app/window_size")
  76. }
  77. #[derive(Clone, Debug, SerialEncodable, SerialDecodable)]
  78. #[repr(C)]
  79. pub struct Vertex {
  80. pub pos: [f32; 2],
  81. pub color: [f32; 4],
  82. pub uv: [f32; 2],
  83. }
  84. impl Vertex {
  85. pub fn pos(&self) -> Point {
  86. self.pos.into()
  87. }
  88. pub fn set_pos(&mut self, pos: &Point) {
  89. self.pos = pos.as_arr();
  90. }
  91. }
  92. pub type TextureId = u32;
  93. pub type BufferId = u32;
  94. pub type AnimId = u32;
  95. static NEXT_BUFFER_ID: AtomicU32 = AtomicU32::new(0);
  96. static NEXT_TEXTURE_ID: AtomicU32 = AtomicU32::new(0);
  97. static NEXT_ANIM_ID: AtomicU32 = AtomicU32::new(0);
  98. pub type ManagedTexturePtr = Arc<ManagedTexture>;
  99. /// Auto-deletes texture on drop
  100. pub struct ManagedTexture {
  101. id: TextureId,
  102. epoch: u32,
  103. render_api: RenderApi,
  104. tag: DebugTag,
  105. }
  106. impl Drop for ManagedTexture {
  107. fn drop(&mut self) {
  108. self.render_api.delete_unmanaged_texture(self.id, self.epoch, self.tag);
  109. }
  110. }
  111. impl std::fmt::Debug for ManagedTexture {
  112. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  113. f.debug_struct("ManagedTexture").field("id", &self.id).finish()
  114. }
  115. }
  116. pub type ManagedBufferPtr = Arc<ManagedBuffer>;
  117. /// Auto-deletes buffer on drop
  118. pub struct ManagedBuffer {
  119. id: BufferId,
  120. epoch: u32,
  121. render_api: RenderApi,
  122. tag: DebugTag,
  123. buftype: u8,
  124. }
  125. impl Drop for ManagedBuffer {
  126. fn drop(&mut self) {
  127. self.render_api.delete_unmanaged_buffer(self.id, self.epoch, self.tag, self.buftype);
  128. }
  129. }
  130. impl std::fmt::Debug for ManagedBuffer {
  131. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  132. f.debug_struct("ManagedBuffer").field("id", &self.id).finish()
  133. }
  134. }
  135. pub type ManagedSeqAnimPtr = Arc<ManagedSeqAnim>;
  136. pub struct ManagedSeqAnim {
  137. frames_len: usize,
  138. pub id: AnimId,
  139. epoch: u32,
  140. render_api: RenderApi,
  141. tag: DebugTag,
  142. }
  143. impl ManagedSeqAnim {
  144. pub fn update(&self, frame_idx: usize, frame: AnimFrame) {
  145. assert!(frame_idx < self.frames_len);
  146. self.render_api.update_unmanaged_anim(self.id, frame_idx, frame, self.epoch, self.tag);
  147. }
  148. }
  149. impl Drop for ManagedSeqAnim {
  150. fn drop(&mut self) {
  151. self.render_api.delete_unmanaged_anim(self.id, self.epoch, self.tag);
  152. }
  153. }
  154. impl std::fmt::Debug for ManagedSeqAnim {
  155. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  156. f.debug_struct("ManagedSeqAnim").field("id", &self.id).finish()
  157. }
  158. }
  159. pub type EpochIndex = u32;
  160. #[derive(Clone)]
  161. pub struct RenderApi {
  162. /// We are abusing async_channel since it's cloneable whereas std::sync::mpsc is shit.
  163. method_send: async_channel::Sender<(EpochIndex, GraphicsMethod)>,
  164. /// Keep track of the current UI epoch
  165. epoch: Arc<AtomicU32>,
  166. }
  167. impl RenderApi {
  168. pub fn new(method_send: async_channel::Sender<(EpochIndex, GraphicsMethod)>) -> Self {
  169. Self { method_send, epoch: Arc::new(AtomicU32::new(0)) }
  170. }
  171. fn next_epoch(&self) -> EpochIndex {
  172. self.epoch.fetch_add(1, Ordering::Relaxed) + 1
  173. }
  174. fn send(&self, method: GraphicsMethod) -> EpochIndex {
  175. let epoch = self.epoch.load(Ordering::Relaxed);
  176. self.send_with_epoch(method, epoch);
  177. epoch
  178. }
  179. fn send_with_epoch(&self, method: GraphicsMethod, epoch: EpochIndex) {
  180. let _ = self.method_send.try_send((epoch, method)).unwrap();
  181. }
  182. fn new_unmanaged_texture(
  183. &self,
  184. width: u16,
  185. height: u16,
  186. data: Vec<u8>,
  187. tag: DebugTag,
  188. ) -> (TextureId, EpochIndex) {
  189. let gfx_texture_id = NEXT_TEXTURE_ID.fetch_add(1, Ordering::Relaxed);
  190. let method = GraphicsMethod::NewTexture((width, height, data, gfx_texture_id, tag));
  191. let epoch = self.send(method);
  192. (gfx_texture_id, epoch)
  193. }
  194. pub fn new_texture(
  195. &self,
  196. width: u16,
  197. height: u16,
  198. data: Vec<u8>,
  199. tag: DebugTag,
  200. ) -> ManagedTexturePtr {
  201. let (id, epoch) = self.new_unmanaged_texture(width, height, data, tag);
  202. Arc::new(ManagedTexture { id, epoch, render_api: self.clone(), tag })
  203. }
  204. fn delete_unmanaged_texture(&self, texture: TextureId, epoch: EpochIndex, tag: DebugTag) {
  205. let method = GraphicsMethod::DeleteTexture((texture, tag));
  206. self.send_with_epoch(method, epoch);
  207. }
  208. fn new_unmanaged_vertex_buffer(
  209. &self,
  210. verts: Vec<Vertex>,
  211. tag: DebugTag,
  212. ) -> (BufferId, EpochIndex) {
  213. let gfx_buffer_id = NEXT_BUFFER_ID.fetch_add(1, Ordering::Relaxed);
  214. let method = GraphicsMethod::NewVertexBuffer((verts, gfx_buffer_id, tag));
  215. let epoch = self.send(method);
  216. (gfx_buffer_id, epoch)
  217. }
  218. fn new_unmanaged_index_buffer(
  219. &self,
  220. indices: Vec<u16>,
  221. tag: DebugTag,
  222. ) -> (BufferId, EpochIndex) {
  223. let gfx_buffer_id = NEXT_BUFFER_ID.fetch_add(1, Ordering::Relaxed);
  224. let method = GraphicsMethod::NewIndexBuffer((indices, gfx_buffer_id, tag));
  225. let epoch = self.send(method);
  226. (gfx_buffer_id, epoch)
  227. }
  228. pub fn new_vertex_buffer(&self, verts: Vec<Vertex>, tag: DebugTag) -> ManagedBufferPtr {
  229. let (id, epoch) = self.new_unmanaged_vertex_buffer(verts, tag);
  230. Arc::new(ManagedBuffer { id, epoch, render_api: self.clone(), tag, buftype: 0 })
  231. }
  232. pub fn new_index_buffer(&self, indices: Vec<u16>, tag: DebugTag) -> ManagedBufferPtr {
  233. let (id, epoch) = self.new_unmanaged_index_buffer(indices, tag);
  234. Arc::new(ManagedBuffer { id, epoch, render_api: self.clone(), tag, buftype: 1 })
  235. }
  236. fn delete_unmanaged_buffer(
  237. &self,
  238. buffer: BufferId,
  239. epoch: EpochIndex,
  240. tag: DebugTag,
  241. buftype: u8,
  242. ) {
  243. let method = GraphicsMethod::DeleteBuffer((buffer, tag, buftype));
  244. self.send_with_epoch(method, epoch);
  245. }
  246. fn new_unmanaged_anim(
  247. &self,
  248. frames_len: usize,
  249. oneshot: bool,
  250. tag: DebugTag,
  251. ) -> (AnimId, EpochIndex) {
  252. let gfx_anim_id = NEXT_ANIM_ID.fetch_add(1, Ordering::Relaxed);
  253. let method = GraphicsMethod::NewSeqAnim { id: gfx_anim_id, frames_len, oneshot, tag };
  254. let epoch = self.send(method);
  255. (gfx_anim_id, epoch)
  256. }
  257. pub fn new_anim(&self, frames_len: usize, oneshot: bool, tag: DebugTag) -> ManagedSeqAnimPtr {
  258. let (id, epoch) = self.new_unmanaged_anim(frames_len, oneshot, tag);
  259. Arc::new(ManagedSeqAnim { frames_len, id, epoch, render_api: self.clone(), tag })
  260. }
  261. pub fn update_unmanaged_anim(
  262. &self,
  263. anim: AnimId,
  264. frame_idx: usize,
  265. frame: AnimFrame,
  266. epoch: EpochIndex,
  267. tag: DebugTag,
  268. ) {
  269. let method = GraphicsMethod::UpdateSeqAnim { id: anim, frame_idx, frame, tag };
  270. self.send_with_epoch(method, epoch);
  271. }
  272. fn delete_unmanaged_anim(&self, anim: AnimId, epoch: EpochIndex, tag: DebugTag) {
  273. let method = GraphicsMethod::DeleteSeqAnim((anim, tag));
  274. self.send_with_epoch(method, epoch);
  275. }
  276. pub fn replace_draw_calls(
  277. &self,
  278. batch_id: BatchGuardId,
  279. timest: Timestamp,
  280. dcs: Vec<(DcId, DrawCall)>,
  281. ) {
  282. let method = GraphicsMethod::ReplaceGfxDrawCalls { batch_id, timest, dcs };
  283. self.send(method);
  284. }
  285. fn start_batch(&self, batch_id: BatchGuardId, debug_str: Option<&'static str>) {
  286. let method = GraphicsMethod::StartBatch((batch_id, debug_str));
  287. self.send(method);
  288. }
  289. fn end_batch(&self, batch_id: BatchGuardId) {
  290. let method = GraphicsMethod::EndBatch(batch_id);
  291. self.send(method);
  292. }
  293. pub fn make_guard(&self, debug_str: Option<&'static str>) -> PropertyAtomicGuard {
  294. let r = self.clone();
  295. let start_batch = Box::new(move |bid| r.start_batch(bid, debug_str));
  296. let r = self.clone();
  297. let end_batch = Box::new(move |bid| r.end_batch(bid));
  298. PropertyAtomicGuard::new(start_batch, end_batch)
  299. }
  300. }
  301. #[derive(Clone, Debug)]
  302. pub struct DrawMesh {
  303. pub vertex_buffer: ManagedBufferPtr,
  304. pub index_buffer: ManagedBufferPtr,
  305. pub texture: Option<ManagedTexturePtr>,
  306. pub num_elements: i32,
  307. }
  308. impl DrawMesh {
  309. fn compile(
  310. self,
  311. textures: &HashMap<TextureId, miniquad::TextureId>,
  312. buffers: &HashMap<BufferId, miniquad::BufferId>,
  313. debug_str: &'static str,
  314. ) -> Option<GfxDrawMesh> {
  315. let vertex_buffer_id = self.vertex_buffer.id;
  316. let index_buffer_id = self.index_buffer.id;
  317. let _buffers_keep_alive = [self.vertex_buffer, self.index_buffer];
  318. let texture = match self.texture {
  319. Some(gfx_texture) => Self::try_get_texture(textures, gfx_texture, debug_str),
  320. None => None,
  321. };
  322. Some(GfxDrawMesh {
  323. vertex_buffer: Self::try_get_buffer(buffers, vertex_buffer_id, debug_str)?,
  324. index_buffer: Self::try_get_buffer(buffers, index_buffer_id, debug_str)?,
  325. _buffers_keep_alive,
  326. texture,
  327. num_elements: self.num_elements,
  328. })
  329. }
  330. fn try_get_texture(
  331. textures: &HashMap<TextureId, miniquad::TextureId>,
  332. gfx_texture: ManagedTexturePtr,
  333. debug_str: &'static str,
  334. ) -> Option<(ManagedTexturePtr, miniquad::TextureId)> {
  335. let gfx_texture_id = gfx_texture.id;
  336. let Some(_mq_texture_id) = textures.get(&gfx_texture_id) else {
  337. error!(target: "gfx", "Serious error: missing texture ID={gfx_texture_id}, debug={debug_str}");
  338. error!(target: "gfx", "Dumping textures:");
  339. for (gfx_texture_id, texture_id) in textures {
  340. error!(target: "gfx", "{gfx_texture_id} => {texture_id:?}");
  341. }
  342. panic!("Missing texture ID={gfx_texture_id}")
  343. };
  344. Some((gfx_texture, textures[&gfx_texture_id]))
  345. }
  346. fn try_get_buffer(
  347. buffers: &HashMap<BufferId, miniquad::BufferId>,
  348. gfx_buffer_id: BufferId,
  349. debug_str: &'static str,
  350. ) -> Option<miniquad::BufferId> {
  351. let Some(mq_buffer_id) = buffers.get(&gfx_buffer_id) else {
  352. error!(target: "gfx", "Serious error: missing buffer ID={gfx_buffer_id}, debug={debug_str}");
  353. error!(target: "gfx", "Dumping buffers:");
  354. for (gfx_buffer_id, buffer_id) in buffers {
  355. error!(target: "gfx", "{gfx_buffer_id} => {buffer_id:?}");
  356. }
  357. panic!("Missing buffer ID={gfx_buffer_id}")
  358. };
  359. Some(*mq_buffer_id)
  360. }
  361. }
  362. impl Encodable for DrawMesh {
  363. fn encode<S: Write>(&self, s: &mut S) -> std::result::Result<usize, std::io::Error> {
  364. let mut len = 0;
  365. len += self.vertex_buffer.id.encode(s)?;
  366. len += self.vertex_buffer.epoch.encode(s)?;
  367. len += self.vertex_buffer.tag.encode(s)?;
  368. len += self.vertex_buffer.buftype.encode(s)?;
  369. len += self.index_buffer.id.encode(s)?;
  370. len += self.index_buffer.epoch.encode(s)?;
  371. len += self.index_buffer.tag.encode(s)?;
  372. len += self.index_buffer.buftype.encode(s)?;
  373. match &self.texture {
  374. Some(t) => {
  375. len += 1u8.encode(s)?;
  376. len += t.id.encode(s)?;
  377. len += t.epoch.encode(s)?;
  378. len += t.tag.encode(s)?;
  379. }
  380. None => {
  381. len += 0u8.encode(s)?;
  382. }
  383. }
  384. len += self.num_elements.encode(s)?;
  385. Ok(len)
  386. }
  387. }
  388. #[async_trait]
  389. impl AsyncEncodable for DrawMesh {
  390. async fn encode_async<W: AsyncWrite + Unpin + Send>(
  391. &self,
  392. _: &mut W,
  393. ) -> std::io::Result<usize> {
  394. Ok(0)
  395. }
  396. }
  397. #[derive(Debug, Clone, SerialEncodable)]
  398. pub enum DrawInstruction {
  399. SetScale(f32),
  400. Move(Point),
  401. SetPos(Point),
  402. ApplyView(Rectangle),
  403. Draw(DrawMesh),
  404. Animation(AnimId),
  405. EnableDebug,
  406. }
  407. impl DrawInstruction {
  408. fn compile(
  409. self,
  410. textures: &HashMap<TextureId, miniquad::TextureId>,
  411. buffers: &HashMap<BufferId, miniquad::BufferId>,
  412. debug_str: &'static str,
  413. ) -> Option<GfxDrawInstruction> {
  414. let instr = match self {
  415. Self::SetScale(scale) => GfxDrawInstruction::SetScale(scale),
  416. Self::Move(off) => GfxDrawInstruction::Move(off),
  417. Self::SetPos(pos) => GfxDrawInstruction::SetPos(pos),
  418. Self::ApplyView(view) => GfxDrawInstruction::ApplyView(view),
  419. Self::Draw(mesh) => {
  420. GfxDrawInstruction::Draw(mesh.compile(textures, buffers, debug_str)?)
  421. }
  422. Self::Animation(anim) => GfxDrawInstruction::Animation(anim),
  423. Self::EnableDebug => GfxDrawInstruction::EnableDebug,
  424. };
  425. Some(instr)
  426. }
  427. }
  428. #[derive(Clone, Debug, Default, SerialEncodable)]
  429. pub struct DrawCall {
  430. pub instrs: Vec<DrawInstruction>,
  431. pub dcs: Vec<DcId>,
  432. pub z_index: u32,
  433. pub debug_str: &'static str,
  434. }
  435. impl DrawCall {
  436. pub fn new(
  437. instrs: Vec<DrawInstruction>,
  438. dcs: Vec<DcId>,
  439. z_index: u32,
  440. debug_str: &'static str,
  441. ) -> Self {
  442. Self { instrs, dcs, z_index, debug_str }
  443. }
  444. fn compile(
  445. self,
  446. textures: &HashMap<TextureId, miniquad::TextureId>,
  447. buffers: &HashMap<BufferId, miniquad::BufferId>,
  448. timest: Timestamp,
  449. ) -> Option<GfxDrawCall> {
  450. Some(GfxDrawCall {
  451. instrs: self
  452. .instrs
  453. .into_iter()
  454. .map(|i| i.compile(textures, buffers, self.debug_str))
  455. .collect::<Option<Vec<_>>>()?,
  456. dcs: self.dcs,
  457. z_index: self.z_index,
  458. timest,
  459. })
  460. }
  461. }
  462. #[derive(Clone, Debug)]
  463. struct GfxDrawMesh {
  464. vertex_buffer: miniquad::BufferId,
  465. index_buffer: miniquad::BufferId,
  466. /// Keeps the buffers alive for the duration of this draw call
  467. _buffers_keep_alive: [ManagedBufferPtr; 2],
  468. texture: Option<(ManagedTexturePtr, miniquad::TextureId)>,
  469. num_elements: i32,
  470. }
  471. #[derive(Debug, Clone)]
  472. enum GfxDrawInstruction {
  473. SetScale(f32),
  474. Move(Point),
  475. SetPos(Point),
  476. ApplyView(Rectangle),
  477. Draw(GfxDrawMesh),
  478. Animation(AnimId),
  479. EnableDebug,
  480. }
  481. #[derive(Clone, Debug)]
  482. struct GfxDrawCall {
  483. instrs: Vec<GfxDrawInstruction>,
  484. dcs: Vec<DcId>,
  485. z_index: u32,
  486. timest: Timestamp,
  487. }
  488. struct RenderContext<'a> {
  489. ctx: &'a mut Box<dyn RenderingBackend>,
  490. draw_calls: &'a HashMap<DcId, GfxDrawCall>,
  491. uniforms_data: [u8; 128],
  492. white_texture: miniquad::TextureId,
  493. scale: f32,
  494. view: Rectangle,
  495. cursor: Point,
  496. anims: &'a mut HashMap<AnimId, GfxSeqAnim>,
  497. }
  498. impl<'a> RenderContext<'a> {
  499. fn draw(&mut self) {
  500. if DEBUG_RENDER {
  501. debug!(target: "gfx", "RenderContext::draw()");
  502. }
  503. if DEBUG_TRAX {
  504. get_trax().lock().set_curr(0);
  505. }
  506. self.draw_call(&self.draw_calls[&0], 0, DEBUG_RENDER);
  507. if DEBUG_RENDER {
  508. debug!(target: "gfx", "RenderContext::draw() [DONE]");
  509. }
  510. }
  511. fn apply_view(&mut self) {
  512. // Actual physical view
  513. let view = self.view * self.scale;
  514. let (_, screen_height) = window::screen_size();
  515. let view_x = view.x.round() as i32;
  516. let view_y = screen_height - (view.y + view.h);
  517. let view_y = view_y.round() as i32;
  518. let view_w = view.w.round() as i32;
  519. let view_h = view.h.round() as i32;
  520. // OpenGL does not like negative values here
  521. if view_w <= 0 || view_h <= 0 {
  522. return
  523. }
  524. if DEBUG_RENDER {
  525. debug!(target: "gfx", "=> viewport {view_x} {view_y} {view_w} {view_h}");
  526. }
  527. self.ctx.apply_viewport(view_x, view_y, view_w, view_h);
  528. self.ctx.apply_scissor_rect(view_x, view_y, view_w, view_h);
  529. }
  530. fn apply_model(&mut self) {
  531. let off_x = self.cursor.x / self.view.w;
  532. let off_y = self.cursor.y / self.view.h;
  533. let scale_w = 1. / self.view.w;
  534. let scale_h = 1. / self.view.h;
  535. let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
  536. glam::Mat4::from_scale(glam::Vec3::new(scale_w, scale_h, 1.));
  537. let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
  538. self.uniforms_data[64..].copy_from_slice(&data);
  539. self.ctx.apply_uniforms_from_bytes(self.uniforms_data.as_ptr(), self.uniforms_data.len());
  540. }
  541. fn draw_call(&mut self, draw_call: &GfxDrawCall, mut indent: u32, mut is_debug: bool) {
  542. let ws = if is_debug { " ".repeat(indent as usize * 4) } else { String::new() };
  543. let old_scale = self.scale;
  544. let old_view = self.view;
  545. let old_cursor = self.cursor;
  546. for (idx, instr) in draw_call.instrs.iter().enumerate() {
  547. if DEBUG_TRAX {
  548. get_trax().lock().set_instr(idx);
  549. }
  550. match instr {
  551. GfxDrawInstruction::SetScale(scale) => {
  552. self.scale = *scale;
  553. self.view.w /= self.scale;
  554. self.view.h /= self.scale;
  555. if is_debug {
  556. debug!(target: "gfx", "{ws}set_scale({scale})");
  557. }
  558. }
  559. GfxDrawInstruction::Move(off) => {
  560. self.cursor += *off;
  561. if is_debug {
  562. debug!(target: "gfx",
  563. "{ws}move({off:?}) cursor={:?}, scale={}, view={:?}",
  564. self.cursor, self.scale, self.view
  565. );
  566. }
  567. self.apply_model();
  568. }
  569. GfxDrawInstruction::SetPos(pos) => {
  570. self.cursor = old_cursor + *pos;
  571. if is_debug {
  572. debug!(target: "gfx",
  573. "{ws}set_pos({pos:?}) cursor={:?}, scale={}, view={:?}",
  574. self.cursor, self.scale, self.view
  575. );
  576. }
  577. self.apply_model();
  578. }
  579. GfxDrawInstruction::ApplyView(view) => {
  580. // Adjust view relative to cursor
  581. self.view = *view + self.cursor;
  582. // We could just skip drawing when clipping rect isn't visible
  583. // using an is_visible flag.
  584. match self.view.clip(&old_view) {
  585. Some(clipped) => self.view = clipped,
  586. None => self.view = Rectangle::zero(),
  587. }
  588. // Cursor resets within the view
  589. self.cursor = Point::zero();
  590. if is_debug {
  591. debug!(target: "gfx",
  592. "{ws}apply_view({view:?}) scale={}, view={:?}",
  593. self.scale, self.view
  594. );
  595. }
  596. self.apply_view();
  597. self.apply_model();
  598. }
  599. GfxDrawInstruction::Draw(mesh) => {
  600. if is_debug {
  601. debug!(target: "gfx", "{ws}draw({mesh:?})");
  602. }
  603. let texture = match mesh.texture {
  604. Some((_, texture)) => texture,
  605. None => self.white_texture,
  606. };
  607. let bindings = Bindings {
  608. vertex_buffers: vec![mesh.vertex_buffer],
  609. index_buffer: mesh.index_buffer,
  610. images: vec![texture],
  611. };
  612. self.ctx.apply_bindings(&bindings);
  613. self.ctx.draw(0, mesh.num_elements, 1);
  614. }
  615. GfxDrawInstruction::Animation(anim_id) => {
  616. let anim = self.anims.get_mut(&anim_id).unwrap();
  617. anim.is_visible = true;
  618. if let Some(dc) = anim.tick() {
  619. self.draw_call(&dc, indent + 1, is_debug);
  620. }
  621. }
  622. GfxDrawInstruction::EnableDebug => {
  623. if !is_debug {
  624. indent = 0;
  625. }
  626. is_debug = true;
  627. debug!(target: "gfx", "Frame start");
  628. }
  629. }
  630. }
  631. let mut draw_calls: Vec<_> =
  632. draw_call.dcs.iter().map(|key| (key, &self.draw_calls[key])).collect();
  633. draw_calls.sort_unstable_by_key(|(_, dc)| dc.z_index);
  634. for (dc_key, dc) in draw_calls {
  635. if DEBUG_TRAX {
  636. get_trax().lock().set_curr(*dc_key);
  637. }
  638. if is_debug {
  639. debug!(target: "gfx", "{ws}drawcall {dc_key}");
  640. }
  641. self.draw_call(dc, indent + 1, is_debug);
  642. }
  643. self.scale = old_scale;
  644. if is_debug {
  645. debug!(target: "gfx", "{ws}Frame close: cursor={old_cursor:?}, view={old_view:?}");
  646. }
  647. self.view = old_view;
  648. self.apply_view();
  649. self.cursor = old_cursor;
  650. self.apply_model();
  651. }
  652. }
  653. type Timestamp = u64;
  654. type DcId = u64;
  655. #[derive(Clone)]
  656. pub enum GraphicsMethod {
  657. NewTexture((u16, u16, Vec<u8>, TextureId, DebugTag)),
  658. DeleteTexture((TextureId, DebugTag)),
  659. NewVertexBuffer((Vec<Vertex>, BufferId, DebugTag)),
  660. NewIndexBuffer((Vec<u16>, BufferId, DebugTag)),
  661. DeleteBuffer((BufferId, DebugTag, u8)),
  662. NewSeqAnim { id: AnimId, frames_len: usize, oneshot: bool, tag: DebugTag },
  663. UpdateSeqAnim { id: AnimId, frame_idx: usize, frame: AnimFrame, tag: DebugTag },
  664. DeleteSeqAnim((AnimId, DebugTag)),
  665. ReplaceGfxDrawCalls { batch_id: BatchGuardId, timest: Timestamp, dcs: Vec<(DcId, DrawCall)> },
  666. StartBatch((BatchGuardId, Option<&'static str>)),
  667. EndBatch(BatchGuardId),
  668. }
  669. impl std::fmt::Debug for GraphicsMethod {
  670. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  671. match self {
  672. Self::NewTexture(_) => write!(f, "NewTexture"),
  673. Self::DeleteTexture(_) => write!(f, "DeleteTexture"),
  674. Self::NewVertexBuffer(_) => write!(f, "NewVertexBuffer"),
  675. Self::NewIndexBuffer(_) => write!(f, "NewIndexBuffer"),
  676. Self::DeleteBuffer(_) => write!(f, "DeleteBuffer"),
  677. Self::NewSeqAnim { .. } => write!(f, "NewSeqAnim"),
  678. Self::UpdateSeqAnim { .. } => write!(f, "UpdateSeqAnim"),
  679. Self::DeleteSeqAnim(_) => write!(f, "DeleteSeqAnim"),
  680. Self::ReplaceGfxDrawCalls { batch_id: bid, timest: _, dcs: _ } => {
  681. write!(f, "ReplaceGfxDrawCalls({bid})")
  682. }
  683. Self::StartBatch((bid, debug_str)) => write!(f, "StartBatch({bid}, {debug_str:?})"),
  684. Self::EndBatch(bid) => write!(f, "EndBatch({bid})"),
  685. }
  686. }
  687. }
  688. struct EventChannel<T> {
  689. sender: async_channel::Sender<T>,
  690. recvr: async_channel::Receiver<T>,
  691. }
  692. impl<T> EventChannel<T> {
  693. fn new() -> Self {
  694. let (sender, recvr) = async_channel::unbounded();
  695. Self { sender, recvr }
  696. }
  697. fn notify(&self, ev: T) {
  698. self.sender.try_send(ev).unwrap();
  699. }
  700. fn clone_recvr(&self) -> async_channel::Receiver<T> {
  701. self.recvr.clone()
  702. }
  703. }
  704. pub type GraphicsEventPublisherPtr = Arc<GraphicsEventPublisher>;
  705. pub struct GraphicsEventPublisher {
  706. resize: EventChannel<Dimension>,
  707. key_down: EventChannel<(KeyCode, KeyMods, bool)>,
  708. key_up: EventChannel<(KeyCode, KeyMods)>,
  709. chr: EventChannel<(char, KeyMods, bool)>,
  710. mouse_btn_down: EventChannel<(MouseButton, Point)>,
  711. mouse_btn_up: EventChannel<(MouseButton, Point)>,
  712. mouse_move: EventChannel<Point>,
  713. mouse_wheel: EventChannel<Point>,
  714. touch: EventChannel<(TouchPhase, u64, Point)>,
  715. }
  716. pub type GraphicsEventResizeSub = async_channel::Receiver<Dimension>;
  717. pub type GraphicsEventKeyDownSub = async_channel::Receiver<(KeyCode, KeyMods, bool)>;
  718. pub type GraphicsEventKeyUpSub = async_channel::Receiver<(KeyCode, KeyMods)>;
  719. pub type GraphicsEventCharSub = async_channel::Receiver<(char, KeyMods, bool)>;
  720. pub type GraphicsEventMouseButtonDownSub = async_channel::Receiver<(MouseButton, Point)>;
  721. pub type GraphicsEventMouseButtonUpSub = async_channel::Receiver<(MouseButton, Point)>;
  722. pub type GraphicsEventMouseMoveSub = async_channel::Receiver<Point>;
  723. pub type GraphicsEventMouseWheelSub = async_channel::Receiver<Point>;
  724. pub type GraphicsEventTouchSub = async_channel::Receiver<(TouchPhase, u64, Point)>;
  725. impl GraphicsEventPublisher {
  726. pub fn new() -> Arc<Self> {
  727. Arc::new(Self {
  728. resize: EventChannel::new(),
  729. key_down: EventChannel::new(),
  730. key_up: EventChannel::new(),
  731. chr: EventChannel::new(),
  732. mouse_btn_down: EventChannel::new(),
  733. mouse_btn_up: EventChannel::new(),
  734. mouse_move: EventChannel::new(),
  735. mouse_wheel: EventChannel::new(),
  736. touch: EventChannel::new(),
  737. })
  738. }
  739. fn notify_resize(&self, screen_size: Dimension) {
  740. self.resize.notify(screen_size);
  741. }
  742. fn notify_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) {
  743. let ev = (key, mods, repeat);
  744. self.key_down.notify(ev);
  745. }
  746. fn notify_key_up(&self, key: KeyCode, mods: KeyMods) {
  747. let ev = (key, mods);
  748. self.key_up.notify(ev);
  749. }
  750. fn notify_char(&self, chr: char, mods: KeyMods, repeat: bool) {
  751. let ev = (chr, mods, repeat);
  752. self.chr.notify(ev);
  753. }
  754. fn notify_mouse_btn_down(&self, button: MouseButton, mouse_pos: Point) {
  755. let ev = (button, mouse_pos);
  756. self.mouse_btn_down.notify(ev);
  757. }
  758. fn notify_mouse_btn_up(&self, button: MouseButton, mouse_pos: Point) {
  759. let ev = (button, mouse_pos);
  760. self.mouse_btn_up.notify(ev);
  761. }
  762. fn notify_mouse_move(&self, mouse_pos: Point) {
  763. self.mouse_move.notify(mouse_pos);
  764. }
  765. fn notify_mouse_wheel(&self, wheel_pos: Point) {
  766. self.mouse_wheel.notify(wheel_pos);
  767. }
  768. fn notify_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) {
  769. let ev = (phase, id, touch_pos);
  770. self.touch.notify(ev);
  771. }
  772. pub fn subscribe_resize(&self) -> GraphicsEventResizeSub {
  773. self.resize.clone_recvr()
  774. }
  775. pub fn subscribe_key_down(&self) -> GraphicsEventKeyDownSub {
  776. self.key_down.clone_recvr()
  777. }
  778. pub fn subscribe_key_up(&self) -> GraphicsEventKeyUpSub {
  779. self.key_up.clone_recvr()
  780. }
  781. pub fn subscribe_char(&self) -> GraphicsEventCharSub {
  782. self.chr.clone_recvr()
  783. }
  784. pub fn subscribe_mouse_btn_down(&self) -> GraphicsEventMouseButtonDownSub {
  785. self.mouse_btn_down.clone_recvr()
  786. }
  787. pub fn subscribe_mouse_btn_up(&self) -> GraphicsEventMouseButtonUpSub {
  788. self.mouse_btn_up.clone_recvr()
  789. }
  790. pub fn subscribe_mouse_move(&self) -> GraphicsEventMouseMoveSub {
  791. self.mouse_move.clone_recvr()
  792. }
  793. pub fn subscribe_mouse_wheel(&self) -> GraphicsEventMouseWheelSub {
  794. self.mouse_wheel.clone_recvr()
  795. }
  796. pub fn subscribe_touch(&self) -> GraphicsEventTouchSub {
  797. self.touch.clone_recvr()
  798. }
  799. }
  800. struct Stage {
  801. ctx: Box<dyn RenderingBackend>,
  802. #[cfg(target_os = "android")]
  803. libegl: egl::LibEgl,
  804. pipeline: Pipeline,
  805. white_texture: miniquad::TextureId,
  806. draw_calls: HashMap<DcId, GfxDrawCall>,
  807. batches: HashMap<BatchGuardId, Vec<GraphicsMethod>>,
  808. textures: HashMap<TextureId, miniquad::TextureId>,
  809. buffers: HashMap<BufferId, miniquad::BufferId>,
  810. anims: HashMap<AnimId, GfxSeqAnim>,
  811. epoch: EpochIndex,
  812. method_queue: Arc<SyncMutex<Vec<(EpochIndex, GraphicsMethod)>>>,
  813. event_pub: GraphicsEventPublisherPtr,
  814. pruner: PruneMethodHeap,
  815. screen_was_off: bool,
  816. #[cfg(target_os = "android")]
  817. ex: ExecutorPtr,
  818. #[cfg(target_os = "android")]
  819. refresh_task: Option<smol::Task<()>>,
  820. }
  821. impl Stage {
  822. pub fn new() -> Self {
  823. if DEBUG_TRAX {
  824. get_trax().lock().clear();
  825. }
  826. let mut ctx: Box<dyn RenderingBackend> = window::new_rendering_backend();
  827. let god = GOD.get().unwrap();
  828. // Start a new epoch. This is a brand new UI run.
  829. let epoch = god.render_api.next_epoch();
  830. // This will start the app to start. Needed since we cannot get window size for init
  831. // until window is created.
  832. god.start_app(epoch);
  833. let method_recv = god.method_recv.clone();
  834. let event_pub = god.event_pub.clone();
  835. let ex = god.fg_ex.clone();
  836. let method_queue = Arc::new(SyncMutex::new(vec![]));
  837. let method_queue2 = method_queue.clone();
  838. let sink_task = ex.spawn(async move {
  839. // Pull from render_api
  840. while let Ok((epoch, method)) = method_recv.recv().await {
  841. let is_replace_dc = matches!(method, GraphicsMethod::ReplaceGfxDrawCalls { .. });
  842. // Append to stage data
  843. method_queue2.lock().push((epoch, method));
  844. // If ReplaceGfxDrawCall then wake up miniquad
  845. if is_replace_dc {
  846. #[cfg(target_os = "android")]
  847. miniquad::window::schedule_update();
  848. }
  849. }
  850. });
  851. god.fg_runtime.push_task(sink_task);
  852. let white_texture = ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]);
  853. let mut shader_meta: ShaderMeta = shader::meta();
  854. shader_meta.uniforms.uniforms.push(UniformDesc::new("Projection", UniformType::Mat4));
  855. shader_meta.uniforms.uniforms.push(UniformDesc::new("Model", UniformType::Mat4));
  856. let shader = ctx
  857. .new_shader(
  858. match ctx.info().backend {
  859. Backend::OpenGl => ShaderSource::Glsl {
  860. vertex: shader::GL_VERTEX,
  861. fragment: shader::GL_FRAGMENT,
  862. },
  863. Backend::Metal => ShaderSource::Msl { program: shader::METAL },
  864. },
  865. shader_meta,
  866. )
  867. .unwrap();
  868. let params = PipelineParams {
  869. color_blend: Some(BlendState::new(
  870. Equation::Add,
  871. BlendFactor::Value(BlendValue::SourceAlpha),
  872. BlendFactor::OneMinusValue(BlendValue::SourceAlpha),
  873. )),
  874. ..Default::default()
  875. };
  876. let pipeline = ctx.new_pipeline(
  877. &[BufferLayout::default()],
  878. &[
  879. VertexAttribute::new("in_pos", VertexFormat::Float2),
  880. VertexAttribute::new("in_color", VertexFormat::Float4),
  881. VertexAttribute::new("in_uv", VertexFormat::Float2),
  882. ],
  883. shader,
  884. params,
  885. );
  886. #[cfg(target_os = "android")]
  887. let libegl = egl::LibEgl::try_load().expect("Cant load LibEGL");
  888. Stage {
  889. ctx,
  890. #[cfg(target_os = "android")]
  891. libegl,
  892. pipeline,
  893. white_texture,
  894. draw_calls: HashMap::from([(
  895. 0,
  896. GfxDrawCall { instrs: vec![], dcs: vec![], z_index: 0, timest: 0 },
  897. )]),
  898. batches: HashMap::new(),
  899. textures: HashMap::new(),
  900. buffers: HashMap::new(),
  901. anims: HashMap::new(),
  902. epoch,
  903. method_queue,
  904. event_pub,
  905. pruner: PruneMethodHeap::new(epoch),
  906. screen_was_off: false,
  907. #[cfg(target_os = "android")]
  908. ex,
  909. #[cfg(target_os = "android")]
  910. refresh_task: None,
  911. }
  912. }
  913. fn process_method(&mut self, mut method: GraphicsMethod) {
  914. //debug!(target: "gfx", "Received method: {:?}", method);
  915. let res = match &mut method {
  916. GraphicsMethod::NewTexture((width, height, data, gtex_id, _)) => {
  917. self.method_new_texture(*width, *height, data, *gtex_id)
  918. }
  919. GraphicsMethod::DeleteTexture((gtex_id, _)) => self.method_delete_texture(*gtex_id),
  920. GraphicsMethod::NewVertexBuffer((verts, gbuff_id, _)) => {
  921. self.method_new_vertex_buffer(verts, *gbuff_id)
  922. }
  923. GraphicsMethod::NewIndexBuffer((indices, gbuff_id, _)) => {
  924. self.method_new_index_buffer(indices, *gbuff_id)
  925. }
  926. GraphicsMethod::DeleteBuffer((gbuff_id, _, _)) => self.method_delete_buffer(*gbuff_id),
  927. GraphicsMethod::NewSeqAnim { id, frames_len, oneshot, tag: _ } => {
  928. self.method_new_anim(*id, *frames_len, *oneshot)
  929. }
  930. GraphicsMethod::UpdateSeqAnim { id, frame_idx, frame, tag: _ } => {
  931. self.method_update_anim(*id, *frame_idx, frame.clone())
  932. }
  933. GraphicsMethod::DeleteSeqAnim((ganim_id, _)) => self.method_delete_anim(*ganim_id),
  934. GraphicsMethod::ReplaceGfxDrawCalls { batch_id, timest, dcs } => {
  935. //let debug_strs: Vec<_> = dcs.iter().map(|(_, dc)| dc.debug_str).collect();
  936. //t!("Commit dc to {batch_id}: {debug_strs:?}");
  937. let batch = self.batches.get_mut(batch_id).unwrap();
  938. let dcs = std::mem::take(dcs);
  939. batch.push(GraphicsMethod::ReplaceGfxDrawCalls {
  940. batch_id: *batch_id,
  941. timest: *timest,
  942. dcs,
  943. });
  944. if DEBUG_TRAX {
  945. get_trax().lock().put_stat(0);
  946. }
  947. Ok(())
  948. }
  949. GraphicsMethod::StartBatch((batch_id, _debug_str)) => {
  950. //t!("Start batch {batch_id}: {debug_str:?}");
  951. if !self.batches.insert(*batch_id, vec![]).is_none() {
  952. panic!("Batch {batch_id} already open!")
  953. }
  954. if DEBUG_TRAX {
  955. get_trax().lock().put_stat(0);
  956. }
  957. Ok(())
  958. }
  959. GraphicsMethod::EndBatch(batch_id) => {
  960. //t!("End batch {batch_id}");
  961. let batch = self.batches.remove(batch_id).unwrap();
  962. for mut method in batch {
  963. let res = match &mut method {
  964. GraphicsMethod::ReplaceGfxDrawCalls { batch_id: _, timest, dcs } => {
  965. let dcs = std::mem::take(dcs);
  966. self.method_replace_draw_calls(*timest, dcs)
  967. }
  968. _ => panic!("unexpected method in batch!"),
  969. };
  970. if let Err(err) = res {
  971. e!("process_method(method={method:?}) failed with err: {err:?}");
  972. panic!("process_method failed!")
  973. }
  974. }
  975. Ok(())
  976. }
  977. };
  978. if let Err(err) = res {
  979. e!("process_method(method={method:?}) failed with err: {err:?}");
  980. panic!("process_method failed!")
  981. }
  982. }
  983. fn method_new_texture(
  984. &mut self,
  985. width: u16,
  986. height: u16,
  987. data: &Vec<u8>,
  988. gfx_texture_id: TextureId,
  989. ) -> Result<()> {
  990. let texture = self.ctx.new_texture_from_rgba8(width, height, data);
  991. if DEBUG_GFXAPI {
  992. debug!(target: "gfx", "Invoked method: new_texture({}, {}, ..., {}) -> {:?}",
  993. width, height, gfx_texture_id, texture);
  994. //debug!(target: "gfx", "Invoked method: new_texture({}, {}, ..., {}) -> {:?}\n{}",
  995. // width, height, gfx_texture_id, texture,
  996. // ansi_texture(width as usize, height as usize, &data));
  997. }
  998. if let Some(_) = self.textures.insert(gfx_texture_id, texture) {
  999. if DEBUG_TRAX {
  1000. get_trax().lock().put_stat(2);
  1001. }
  1002. //panic!("Duplicate texture ID={gfx_texture_id} detected!");
  1003. return Err(Error::GfxDuplicateTextureID)
  1004. }
  1005. if DEBUG_TRAX {
  1006. get_trax().lock().put_stat(0);
  1007. }
  1008. Ok(())
  1009. }
  1010. fn method_delete_texture(&mut self, gfx_texture_id: TextureId) -> Result<()> {
  1011. let Some(texture) = self.textures.remove(&gfx_texture_id) else {
  1012. if DEBUG_TRAX {
  1013. get_trax().lock().put_stat(2);
  1014. }
  1015. //.expect("couldn't find gfx_texture_id");
  1016. return Err(Error::GfxUnknownTextureID)
  1017. };
  1018. if DEBUG_GFXAPI {
  1019. debug!(target: "gfx", "Invoked method: delete_texture({} => {:?})",
  1020. gfx_texture_id, texture);
  1021. }
  1022. self.ctx.delete_texture(texture);
  1023. if DEBUG_TRAX {
  1024. get_trax().lock().put_stat(0);
  1025. }
  1026. Ok(())
  1027. }
  1028. fn method_new_vertex_buffer(
  1029. &mut self,
  1030. verts: &[Vertex],
  1031. gfx_buffer_id: BufferId,
  1032. ) -> Result<()> {
  1033. let buffer = self.ctx.new_buffer(
  1034. BufferType::VertexBuffer,
  1035. BufferUsage::Immutable,
  1036. BufferSource::slice(verts),
  1037. );
  1038. if DEBUG_GFXAPI {
  1039. debug!(target: "gfx", "Invoked method: new_vertex_buffer(..., {}) -> {:?}",
  1040. gfx_buffer_id, buffer);
  1041. //debug!(target: "gfx", "Invoked method: new_vertex_buffer({:?}, {}) -> {:?}",
  1042. // verts, gfx_buffer_id, buffer);
  1043. }
  1044. if let Some(_) = self.buffers.insert(gfx_buffer_id, buffer) {
  1045. if DEBUG_TRAX {
  1046. get_trax().lock().put_stat(2);
  1047. }
  1048. //panic!("Duplicate vertex buffer ID={gfx_buffer_id} detected!");
  1049. return Err(Error::GfxDuplicateBufferID)
  1050. }
  1051. if DEBUG_TRAX {
  1052. get_trax().lock().put_stat(0);
  1053. }
  1054. Ok(())
  1055. }
  1056. fn method_new_index_buffer(&mut self, indices: &[u16], gfx_buffer_id: BufferId) -> Result<()> {
  1057. let buffer = self.ctx.new_buffer(
  1058. BufferType::IndexBuffer,
  1059. BufferUsage::Immutable,
  1060. BufferSource::slice(&indices),
  1061. );
  1062. if DEBUG_GFXAPI {
  1063. debug!(target: "gfx", "Invoked method: new_index_buffer({}) -> {:?}",
  1064. gfx_buffer_id, buffer);
  1065. //debug!(target: "gfx", "Invoked method: new_index_buffer({:?}, {}) -> {:?}",
  1066. // indices, gfx_buffer_id, buffer);
  1067. }
  1068. if let Some(_) = self.buffers.insert(gfx_buffer_id, buffer) {
  1069. if DEBUG_TRAX {
  1070. get_trax().lock().put_stat(2);
  1071. }
  1072. //panic!("Duplicate index buffer ID={gfx_buffer_id} detected!");
  1073. return Err(Error::GfxDuplicateBufferID)
  1074. }
  1075. if DEBUG_TRAX {
  1076. get_trax().lock().put_stat(0);
  1077. }
  1078. Ok(())
  1079. }
  1080. fn method_delete_buffer(&mut self, gfx_buffer_id: BufferId) -> Result<()> {
  1081. let Some(buffer) = self.buffers.remove(&gfx_buffer_id) else {
  1082. if DEBUG_TRAX {
  1083. get_trax().lock().put_stat(2);
  1084. }
  1085. //.expect("couldn't find gfx_buffer_id");
  1086. return Err(Error::GfxUnknownBufferID)
  1087. };
  1088. if DEBUG_GFXAPI {
  1089. debug!(target: "gfx", "Invoked method: delete_buffer({} => {:?})",
  1090. gfx_buffer_id, buffer);
  1091. }
  1092. self.ctx.delete_buffer(buffer);
  1093. if DEBUG_TRAX {
  1094. get_trax().lock().put_stat(0);
  1095. }
  1096. Ok(())
  1097. }
  1098. fn method_new_anim(
  1099. &mut self,
  1100. gfx_anim_id: AnimId,
  1101. frames_len: usize,
  1102. oneshot: bool,
  1103. ) -> Result<()> {
  1104. if DEBUG_GFXAPI {
  1105. debug!(target: "gfx", "Invoked method: new_anim({gfx_anim_id}, {frames_len}, {oneshot})");
  1106. }
  1107. if let Some(_) = self.anims.insert(gfx_anim_id, GfxSeqAnim::new(frames_len, oneshot)) {
  1108. //panic!("Duplicate index buffer ID={gfx_buffer_id} detected!");
  1109. return Err(Error::GfxDuplicateAnimID)
  1110. }
  1111. Ok(())
  1112. }
  1113. fn method_update_anim(
  1114. &mut self,
  1115. gfx_anim_id: AnimId,
  1116. frame_idx: usize,
  1117. frame: AnimFrame,
  1118. ) -> Result<()> {
  1119. let Some(anim) = self.anims.get_mut(&gfx_anim_id) else {
  1120. //.expect("couldn't find gfx_anim_id");
  1121. return Err(Error::GfxUnknownAnimID)
  1122. };
  1123. if DEBUG_GFXAPI {
  1124. debug!(target: "gfx", "Invoked method: update_anim({gfx_anim_id}[{frame_idx}] => {frame:?})");
  1125. }
  1126. anim.set(frame_idx, frame, &self.textures, &self.buffers);
  1127. Ok(())
  1128. }
  1129. fn method_delete_anim(&mut self, gfx_anim_id: AnimId) -> Result<()> {
  1130. let Some(anim) = self.anims.remove(&gfx_anim_id) else {
  1131. //.expect("couldn't find gfx_anim_id");
  1132. return Err(Error::GfxUnknownAnimID)
  1133. };
  1134. if DEBUG_GFXAPI {
  1135. debug!(target: "gfx", "Invoked method: delete_anim({} => {:?})", gfx_anim_id, anim);
  1136. }
  1137. Ok(())
  1138. }
  1139. fn method_replace_draw_calls(
  1140. &mut self,
  1141. timest: Timestamp,
  1142. dcs: Vec<(DcId, DrawCall)>,
  1143. ) -> Result<()> {
  1144. if DEBUG_GFXAPI {
  1145. debug!(target: "gfx", "Invoked method: replace_draw_calls({:?})", dcs);
  1146. }
  1147. for (key, val) in dcs {
  1148. let Some(val) = val.compile(&self.textures, &self.buffers, timest) else {
  1149. if DEBUG_TRAX {
  1150. get_trax().lock().put_stat(3);
  1151. }
  1152. error!(target: "gfx", "fatal: replace_draw_calls({timest}, ...) failed with item ID={key}");
  1153. continue
  1154. };
  1155. //self.draw_calls.insert(key, val);
  1156. match self.draw_calls.get_mut(&key) {
  1157. Some(old_val) => {
  1158. // Only replace the draw call if it is more recent
  1159. if old_val.timest < timest {
  1160. if DEBUG_TRAX {
  1161. get_trax().lock().put_stat(0);
  1162. }
  1163. *old_val = val;
  1164. } else {
  1165. trace!(target: "gfx", "Rejected stale draw_call {key}: {val:?}");
  1166. if DEBUG_TRAX {
  1167. get_trax().lock().put_stat(2);
  1168. }
  1169. }
  1170. }
  1171. None => {
  1172. self.draw_calls.insert(key, val);
  1173. if DEBUG_TRAX {
  1174. get_trax().lock().put_stat(1);
  1175. }
  1176. }
  1177. }
  1178. }
  1179. Ok(())
  1180. }
  1181. fn trax_method(&self, epoch: EpochIndex, method: &GraphicsMethod) {
  1182. let mut trax = get_trax().lock();
  1183. match method {
  1184. GraphicsMethod::NewTexture((_, _, _, gtex_id, tag)) => {
  1185. trax.put_tex(epoch, *gtex_id, *tag);
  1186. }
  1187. GraphicsMethod::DeleteTexture((gtex_id, tag)) => {
  1188. trax.del_tex(epoch, *gtex_id, *tag);
  1189. }
  1190. GraphicsMethod::NewVertexBuffer((verts, gbuff_id, tag)) => {
  1191. trax.put_verts(epoch, verts.clone(), *gbuff_id, *tag, 0);
  1192. }
  1193. GraphicsMethod::NewIndexBuffer((idxs, gbuff_id, tag)) => {
  1194. trax.put_idxs(epoch, idxs.clone(), *gbuff_id, *tag, 1);
  1195. }
  1196. GraphicsMethod::DeleteBuffer((gbuff_id, tag, buftype)) => {
  1197. trax.del_buf(epoch, *gbuff_id, *tag, *buftype);
  1198. }
  1199. GraphicsMethod::NewSeqAnim { .. } => {
  1200. //trax.put_idxs(epoch, idxs.clone(), *gbuff_id, *tag, 1);
  1201. }
  1202. GraphicsMethod::UpdateSeqAnim { .. } => {
  1203. //trax.put_idxs(epoch, idxs.clone(), *gbuff_id, *tag, 1);
  1204. }
  1205. GraphicsMethod::DeleteSeqAnim(..) => {
  1206. //trax.del_buf(epoch, *gbuff_id, *tag, *buftype);
  1207. }
  1208. GraphicsMethod::ReplaceGfxDrawCalls { batch_id, timest, dcs } => {
  1209. trax.put_dcs(epoch, *batch_id, *timest, dcs);
  1210. }
  1211. GraphicsMethod::StartBatch((batch_id, debug_str)) => {
  1212. trax.put_start_batch(epoch, *batch_id, *debug_str);
  1213. }
  1214. GraphicsMethod::EndBatch(batch_id) => {
  1215. trax.put_end_batch(epoch, *batch_id);
  1216. }
  1217. };
  1218. }
  1219. fn egl_ctx_is_disabled(&self) -> bool {
  1220. #[cfg(target_os = "android")]
  1221. {
  1222. let egl_ctx = unsafe { (self.libegl.eglGetCurrentContext)() };
  1223. egl_ctx.is_null()
  1224. }
  1225. #[cfg(not(target_os = "android"))]
  1226. false
  1227. }
  1228. }
  1229. /// This is used to process the method queue while the screen is off to avoid the queue
  1230. /// becoming congested and using up all the memory.
  1231. /// Will drop alloc/delete pairs, and merge draw calls together.
  1232. struct PruneMethodHeap {
  1233. /// Newly allocated buffers while screen was off
  1234. new_buf: HashMap<BufferId, GraphicsMethod>,
  1235. /// Newly allocated textures while screen was off
  1236. new_tex: HashMap<TextureId, GraphicsMethod>,
  1237. /// Deleted objects
  1238. del: Vec<GraphicsMethod>,
  1239. /// Draw calls
  1240. dcs: HashMap<DcId, (BatchGuardId, Timestamp, DrawCall)>,
  1241. epoch: EpochIndex,
  1242. }
  1243. impl PruneMethodHeap {
  1244. fn new(epoch: EpochIndex) -> Self {
  1245. Self {
  1246. new_buf: HashMap::new(),
  1247. new_tex: HashMap::new(),
  1248. del: vec![],
  1249. dcs: HashMap::new(),
  1250. epoch,
  1251. }
  1252. }
  1253. fn drain(&mut self, methods: Vec<(EpochIndex, GraphicsMethod)>) {
  1254. // Process as many methods as we can
  1255. for (epoch, method) in methods {
  1256. if epoch < self.epoch {
  1257. // Discard old rubbish
  1258. trace!(target: "gfx::pruner", "Discard method with old epoch: {epoch} curr: {} [method={method:?}]", self.epoch);
  1259. continue
  1260. }
  1261. assert_eq!(epoch, self.epoch);
  1262. self.process_method(method);
  1263. }
  1264. }
  1265. fn process_method(&mut self, method: GraphicsMethod) {
  1266. match method.clone() {
  1267. GraphicsMethod::NewTexture((_, _, _, gtex_id, _)) => {
  1268. self.new_tex.insert(gtex_id, method);
  1269. }
  1270. GraphicsMethod::DeleteTexture((gtex_id, _)) => {
  1271. if self.new_tex.remove(&gtex_id).is_none() {
  1272. self.del.push(method);
  1273. }
  1274. }
  1275. GraphicsMethod::NewVertexBuffer((_, gbuff_id, _)) => {
  1276. self.new_buf.insert(gbuff_id, method);
  1277. }
  1278. GraphicsMethod::NewIndexBuffer((_, gbuff_id, _)) => {
  1279. self.new_buf.insert(gbuff_id, method);
  1280. }
  1281. GraphicsMethod::DeleteBuffer((gbuff_id, _, _)) => {
  1282. if self.new_buf.remove(&gbuff_id).is_none() {
  1283. self.del.push(method);
  1284. }
  1285. }
  1286. GraphicsMethod::NewSeqAnim { .. } => {
  1287. //self.new_buf.insert(gbuff_id, method);
  1288. }
  1289. GraphicsMethod::UpdateSeqAnim { .. } => {
  1290. //self.new_buf.insert(gbuff_id, method);
  1291. }
  1292. GraphicsMethod::DeleteSeqAnim(..) => {
  1293. //if self.new_buf.remove(&gbuff_id).is_none() {
  1294. // self.del.push(method);
  1295. //}
  1296. }
  1297. GraphicsMethod::ReplaceGfxDrawCalls { batch_id, timest, dcs } => {
  1298. self.method_replace_draw_calls(batch_id, timest, dcs)
  1299. }
  1300. // Discard batches since we will apply everything all at once anyway
  1301. // once the screen is switched on.
  1302. GraphicsMethod::StartBatch(_) => {}
  1303. GraphicsMethod::EndBatch(_) => {}
  1304. }
  1305. }
  1306. fn method_replace_draw_calls(
  1307. &mut self,
  1308. batch_id: BatchGuardId,
  1309. timest: Timestamp,
  1310. dcs: Vec<(DcId, DrawCall)>,
  1311. ) {
  1312. for (key, val) in dcs {
  1313. match self.dcs.get_mut(&key) {
  1314. Some(old_val) => {
  1315. // Only replace the draw call if it is more recent
  1316. if old_val.1 < timest {
  1317. *old_val = (batch_id, timest, val);
  1318. } else {
  1319. trace!(target: "gfx::pruner", "Rejected stale draw_call {key}: {val:?}");
  1320. }
  1321. }
  1322. None => {
  1323. self.dcs.insert(key, (batch_id, timest, val));
  1324. }
  1325. }
  1326. }
  1327. }
  1328. /// Collect everything now the screen is on
  1329. fn recv_all(&mut self) -> Vec<GraphicsMethod> {
  1330. // Inhale that smoke deep
  1331. let mut meth = Vec::with_capacity(
  1332. self.new_buf.len() + self.new_tex.len() + self.del.len() + self.dcs.len(),
  1333. );
  1334. let new_buf = std::mem::take(&mut self.new_buf);
  1335. let new_tex = std::mem::take(&mut self.new_tex);
  1336. meth.extend(new_buf.into_values());
  1337. meth.extend(new_tex.into_values());
  1338. meth.append(&mut self.del);
  1339. for (dc_id, (batch_id, timest, dc)) in std::mem::take(&mut self.dcs) {
  1340. meth.push(GraphicsMethod::ReplaceGfxDrawCalls {
  1341. batch_id,
  1342. timest,
  1343. dcs: vec![(dc_id, dc)],
  1344. });
  1345. }
  1346. meth
  1347. }
  1348. }
  1349. impl EventHandler for Stage {
  1350. fn update(&mut self) {
  1351. // todo: trax is all messed up in this func
  1352. let methods = std::mem::take(&mut *self.method_queue.lock());
  1353. if self.egl_ctx_is_disabled() {
  1354. #[cfg(target_os = "android")]
  1355. {
  1356. self.refresh_task = None;
  1357. }
  1358. // Immediately apply any pending batches when the screen is switched off
  1359. let batch_ids: Vec<_> = self.batches.keys().cloned().collect();
  1360. for batch_id in batch_ids {
  1361. self.process_method(GraphicsMethod::EndBatch(batch_id));
  1362. }
  1363. self.batches.clear();
  1364. // Screen is off so collect all methods into the pruner
  1365. self.pruner.drain(methods);
  1366. self.screen_was_off = true;
  1367. return
  1368. }
  1369. #[cfg(target_os = "android")]
  1370. if self.refresh_task.is_none() {
  1371. // For animations do periodic refresh every 40 ms
  1372. self.refresh_task = Some(self.ex.spawn(async move {
  1373. loop {
  1374. darkfi::system::msleep(40).await;
  1375. miniquad::window::schedule_update();
  1376. }
  1377. }));
  1378. }
  1379. // We actually want to skip draining the prune queue the first time so
  1380. // draw actually gets a chance to be called first.
  1381. // Otherwise we will just see a black screen for a sec or so.
  1382. if self.screen_was_off {
  1383. self.screen_was_off = false;
  1384. } else {
  1385. let methods = self.pruner.recv_all();
  1386. assert!(methods.is_empty() || self.batches.is_empty());
  1387. // Process all cached methods by the pruner from while the screen was off.
  1388. for method in methods {
  1389. // Stale methods will be dropped by pruner, so they will not be caught by trax
  1390. // while the screen is off.
  1391. if DEBUG_TRAX {
  1392. self.trax_method(self.epoch, &method);
  1393. }
  1394. // We discard batches here but process_method uses them so implement this
  1395. // workaround.
  1396. match method {
  1397. GraphicsMethod::ReplaceGfxDrawCalls { batch_id: _, timest, dcs } => {
  1398. if let Err(err) = self.method_replace_draw_calls(timest, dcs) {
  1399. e!("process_method for ReplaceGfxDrawCalls failed err: {err:?}");
  1400. panic!("process_method failed!")
  1401. }
  1402. }
  1403. _ => self.process_method(method),
  1404. }
  1405. if DEBUG_TRAX {
  1406. get_trax().lock().flush();
  1407. }
  1408. }
  1409. }
  1410. // Process as many methods as we can
  1411. for (epoch, method) in methods {
  1412. if DEBUG_TRAX {
  1413. self.trax_method(epoch, &method);
  1414. }
  1415. if epoch < self.epoch {
  1416. if DEBUG_TRAX {
  1417. let mut trax = get_trax().lock();
  1418. trax.put_stat(1);
  1419. trax.flush();
  1420. }
  1421. // Discard old rubbish
  1422. trace!(target: "gfx", "Discard method with old epoch: {epoch} curr: {} [method={method:?}]", self.epoch);
  1423. continue
  1424. }
  1425. assert_eq!(epoch, self.epoch);
  1426. self.process_method(method);
  1427. if DEBUG_TRAX {
  1428. get_trax().lock().flush();
  1429. }
  1430. }
  1431. }
  1432. fn draw(&mut self) {
  1433. self.ctx.begin_default_pass(PassAction::clear_color(0., 0., 0., 1.));
  1434. self.ctx.apply_pipeline(&self.pipeline);
  1435. // This will make the top left (0, 0) and the bottom right (1, 1)
  1436. // Default is (-1, 1) -> (1, -1)
  1437. let proj = glam::Mat4::from_translation(glam::Vec3::new(-1., 1., 0.)) *
  1438. glam::Mat4::from_scale(glam::Vec3::new(2., -2., 1.));
  1439. let mut uniforms_data = [0u8; 128];
  1440. let data: [u8; 64] = unsafe { std::mem::transmute_copy(&proj) };
  1441. uniforms_data[0..64].copy_from_slice(&data);
  1442. //let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
  1443. //uniforms_data[64..].copy_from_slice(&data);
  1444. assert_eq!(128, 2 * UniformType::Mat4.size());
  1445. let (screen_w, screen_h) = miniquad::window::screen_size();
  1446. // Mark all anims as invisible
  1447. for (_, anim) in self.anims.iter_mut() {
  1448. anim.is_visible = false;
  1449. }
  1450. let mut render_ctx = RenderContext {
  1451. ctx: &mut self.ctx,
  1452. draw_calls: &self.draw_calls,
  1453. uniforms_data,
  1454. white_texture: self.white_texture,
  1455. scale: 1.,
  1456. view: Rectangle::from([0., 0., screen_w, screen_h]),
  1457. cursor: Point::from([0., 0.]),
  1458. anims: &mut self.anims,
  1459. };
  1460. render_ctx.draw();
  1461. self.ctx.commit_frame();
  1462. }
  1463. fn resize_event(&mut self, width: f32, height: f32) {
  1464. t!("resize_event({width}, {height})");
  1465. let filename = get_window_size_filename();
  1466. if let Some(parent) = filename.parent() {
  1467. let _ = std::fs::create_dir_all(parent);
  1468. }
  1469. if let Ok(mut file) = File::create(filename) {
  1470. (width as i32).encode(&mut file).unwrap();
  1471. (height as i32).encode(&mut file).unwrap();
  1472. }
  1473. self.event_pub.notify_resize(Dimension::from([width, height]));
  1474. }
  1475. fn key_down_event(&mut self, keycode: KeyCode, mods: KeyMods, repeat: bool) {
  1476. self.event_pub.notify_key_down(keycode, mods, repeat);
  1477. }
  1478. fn key_up_event(&mut self, keycode: KeyCode, mods: KeyMods) {
  1479. self.event_pub.notify_key_up(keycode, mods);
  1480. }
  1481. fn char_event(&mut self, chr: char, mods: KeyMods, repeat: bool) {
  1482. self.event_pub.notify_char(chr, mods, repeat);
  1483. }
  1484. fn mouse_button_down_event(&mut self, button: MouseButton, x: f32, y: f32) {
  1485. let pos = Point::from([x, y]);
  1486. self.event_pub.notify_mouse_btn_down(button, pos);
  1487. }
  1488. fn mouse_button_up_event(&mut self, button: MouseButton, x: f32, y: f32) {
  1489. let pos = Point::from([x, y]);
  1490. self.event_pub.notify_mouse_btn_up(button, pos);
  1491. }
  1492. fn mouse_motion_event(&mut self, x: f32, y: f32) {
  1493. let pos = Point::from([x, y]);
  1494. self.event_pub.notify_mouse_move(pos);
  1495. }
  1496. fn mouse_wheel_event(&mut self, x: f32, y: f32) {
  1497. let pos = Point::from([x, y]);
  1498. self.event_pub.notify_mouse_wheel(pos);
  1499. }
  1500. /// The id corresponds to multi-touch. Multiple touch events have different ids.
  1501. fn touch_event(&mut self, phase: TouchPhase, id: u64, x: f32, y: f32) {
  1502. let pos = Point::from([x, y]);
  1503. self.event_pub.notify_touch(phase, id, pos);
  1504. }
  1505. fn quit_requested_event(&mut self) {
  1506. debug!(target: "gfx", "quit requested");
  1507. let god = GOD.get().unwrap();
  1508. god.stop_app();
  1509. }
  1510. }
  1511. pub fn run_gui(linux_backend: miniquad::conf::LinuxBackend) {
  1512. let mut window_width = 1024;
  1513. let mut window_height = 768;
  1514. if let Ok(mut file) = File::open(get_window_size_filename()) {
  1515. window_width = Decodable::decode(&mut file).unwrap();
  1516. window_height = Decodable::decode(&mut file).unwrap();
  1517. }
  1518. debug!(target: "gfx", "Window size {window_width} x {window_height}");
  1519. let mut conf = miniquad::conf::Conf {
  1520. window_title: "DarkFi".to_string(),
  1521. window_width,
  1522. window_height,
  1523. high_dpi: true,
  1524. window_resizable: true,
  1525. platform: miniquad::conf::Platform {
  1526. linux_backend,
  1527. #[cfg(target_os = "android")]
  1528. blocking_event_loop: true,
  1529. android_panic_hook: false,
  1530. ..Default::default()
  1531. },
  1532. icon: Some(miniquad::conf::Icon {
  1533. small: favico::SMALL,
  1534. medium: favico::MEDIUM,
  1535. big: favico::BIG,
  1536. }),
  1537. ..Default::default()
  1538. };
  1539. let metal = std::env::args().nth(1).as_deref() == Some("metal");
  1540. conf.platform.apple_gfx_api =
  1541. if metal { conf::AppleGfxApi::Metal } else { conf::AppleGfxApi::OpenGl };
  1542. miniquad::start(conf, || Box::new(Stage::new()));
  1543. }