mod.rs 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698
  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. use log::debug;
  23. #[cfg(target_os = "android")]
  24. use miniquad::native::egl;
  25. use miniquad::{
  26. conf, window, Backend, Bindings, BlendFactor, BlendState, BlendValue, BufferLayout,
  27. BufferSource, BufferType, BufferUsage, Equation, EventHandler, KeyCode, KeyMods, MouseButton,
  28. PassAction, Pipeline, PipelineParams, RenderingBackend, ShaderMeta, ShaderSource, TouchPhase,
  29. UniformDesc, UniformType, VertexAttribute, VertexFormat,
  30. };
  31. use parking_lot::Mutex as SyncMutex;
  32. use std::{
  33. collections::HashMap,
  34. fs::File,
  35. io::Write,
  36. path::PathBuf,
  37. sync::{
  38. atomic::{AtomicU32, Ordering},
  39. Arc,
  40. },
  41. };
  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. 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. refresh_task: Option<smol::Task<()>>,
  818. }
  819. impl Stage {
  820. pub fn new() -> Self {
  821. if DEBUG_TRAX {
  822. get_trax().lock().clear();
  823. }
  824. let mut ctx: Box<dyn RenderingBackend> = window::new_rendering_backend();
  825. let god = GOD.get().unwrap();
  826. // Start a new epoch. This is a brand new UI run.
  827. let epoch = god.render_api.next_epoch();
  828. // This will start the app to start. Needed since we cannot get window size for init
  829. // until window is created.
  830. god.start_app(epoch);
  831. let method_recv = god.method_recv.clone();
  832. let event_pub = god.event_pub.clone();
  833. let ex = god.fg_ex.clone();
  834. let method_queue = Arc::new(SyncMutex::new(vec![]));
  835. let method_queue2 = method_queue.clone();
  836. let sink_task = ex.spawn(async move {
  837. // Pull from render_api
  838. while let Ok((epoch, method)) = method_recv.recv().await {
  839. let is_replace_dc = matches!(method, GraphicsMethod::ReplaceGfxDrawCalls { .. });
  840. // Append to stage data
  841. method_queue2.lock().push((epoch, method));
  842. // If ReplaceGfxDrawCall then wake up miniquad
  843. if is_replace_dc {
  844. #[cfg(target_os = "android")]
  845. miniquad::window::schedule_update();
  846. }
  847. }
  848. });
  849. god.fg_runtime.push_task(sink_task);
  850. let white_texture = ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]);
  851. let mut shader_meta: ShaderMeta = shader::meta();
  852. shader_meta.uniforms.uniforms.push(UniformDesc::new("Projection", UniformType::Mat4));
  853. shader_meta.uniforms.uniforms.push(UniformDesc::new("Model", UniformType::Mat4));
  854. let shader = ctx
  855. .new_shader(
  856. match ctx.info().backend {
  857. Backend::OpenGl => ShaderSource::Glsl {
  858. vertex: shader::GL_VERTEX,
  859. fragment: shader::GL_FRAGMENT,
  860. },
  861. Backend::Metal => ShaderSource::Msl { program: shader::METAL },
  862. },
  863. shader_meta,
  864. )
  865. .unwrap();
  866. let params = PipelineParams {
  867. color_blend: Some(BlendState::new(
  868. Equation::Add,
  869. BlendFactor::Value(BlendValue::SourceAlpha),
  870. BlendFactor::OneMinusValue(BlendValue::SourceAlpha),
  871. )),
  872. ..Default::default()
  873. };
  874. let pipeline = ctx.new_pipeline(
  875. &[BufferLayout::default()],
  876. &[
  877. VertexAttribute::new("in_pos", VertexFormat::Float2),
  878. VertexAttribute::new("in_color", VertexFormat::Float4),
  879. VertexAttribute::new("in_uv", VertexFormat::Float2),
  880. ],
  881. shader,
  882. params,
  883. );
  884. #[cfg(target_os = "android")]
  885. let libegl = egl::LibEgl::try_load().expect("Cant load LibEGL");
  886. Stage {
  887. ctx,
  888. #[cfg(target_os = "android")]
  889. libegl,
  890. pipeline,
  891. white_texture,
  892. draw_calls: HashMap::from([(
  893. 0,
  894. GfxDrawCall { instrs: vec![], dcs: vec![], z_index: 0, timest: 0 },
  895. )]),
  896. batches: HashMap::new(),
  897. textures: HashMap::new(),
  898. buffers: HashMap::new(),
  899. anims: HashMap::new(),
  900. epoch,
  901. method_queue,
  902. event_pub,
  903. pruner: PruneMethodHeap::new(epoch),
  904. screen_was_off: false,
  905. #[cfg(target_os = "android")]
  906. refresh_task: None,
  907. }
  908. }
  909. fn process_method(&mut self, mut method: GraphicsMethod) {
  910. //debug!(target: "gfx", "Received method: {:?}", method);
  911. let res = match &mut method {
  912. GraphicsMethod::NewTexture((width, height, data, gtex_id, _)) => {
  913. self.method_new_texture(*width, *height, data, *gtex_id)
  914. }
  915. GraphicsMethod::DeleteTexture((gtex_id, _)) => self.method_delete_texture(*gtex_id),
  916. GraphicsMethod::NewVertexBuffer((verts, gbuff_id, _)) => {
  917. self.method_new_vertex_buffer(verts, *gbuff_id)
  918. }
  919. GraphicsMethod::NewIndexBuffer((indices, gbuff_id, _)) => {
  920. self.method_new_index_buffer(indices, *gbuff_id)
  921. }
  922. GraphicsMethod::DeleteBuffer((gbuff_id, _, _)) => self.method_delete_buffer(*gbuff_id),
  923. GraphicsMethod::NewSeqAnim { id, frames_len, oneshot, tag: _ } => {
  924. self.method_new_anim(*id, *frames_len, *oneshot)
  925. }
  926. GraphicsMethod::UpdateSeqAnim { id, frame_idx, frame, tag: _ } => {
  927. self.method_update_anim(*id, *frame_idx, frame.clone())
  928. }
  929. GraphicsMethod::DeleteSeqAnim((ganim_id, _)) => self.method_delete_anim(*ganim_id),
  930. GraphicsMethod::ReplaceGfxDrawCalls { batch_id, timest, dcs } => {
  931. //let debug_strs: Vec<_> = dcs.iter().map(|(_, dc)| dc.debug_str).collect();
  932. //t!("Commit dc to {batch_id}: {debug_strs:?}");
  933. let batch = self.batches.get_mut(batch_id).unwrap();
  934. let dcs = std::mem::take(dcs);
  935. batch.push(GraphicsMethod::ReplaceGfxDrawCalls {
  936. batch_id: *batch_id,
  937. timest: *timest,
  938. dcs,
  939. });
  940. if DEBUG_TRAX {
  941. get_trax().lock().put_stat(0);
  942. }
  943. Ok(())
  944. }
  945. GraphicsMethod::StartBatch((batch_id, _debug_str)) => {
  946. //t!("Start batch {batch_id}: {debug_str:?}");
  947. if !self.batches.insert(*batch_id, vec![]).is_none() {
  948. panic!("Batch {batch_id} already open!")
  949. }
  950. if DEBUG_TRAX {
  951. get_trax().lock().put_stat(0);
  952. }
  953. Ok(())
  954. }
  955. GraphicsMethod::EndBatch(batch_id) => {
  956. //t!("End batch {batch_id}");
  957. let batch = self.batches.remove(batch_id).unwrap();
  958. for mut method in batch {
  959. let res = match &mut method {
  960. GraphicsMethod::ReplaceGfxDrawCalls { batch_id: _, timest, dcs } => {
  961. let dcs = std::mem::take(dcs);
  962. self.method_replace_draw_calls(*timest, dcs)
  963. }
  964. _ => panic!("unexpected method in batch!"),
  965. };
  966. if let Err(err) = res {
  967. e!("process_method(method={method:?}) failed with err: {err:?}");
  968. panic!("process_method failed!")
  969. }
  970. }
  971. Ok(())
  972. }
  973. };
  974. if let Err(err) = res {
  975. e!("process_method(method={method:?}) failed with err: {err:?}");
  976. panic!("process_method failed!")
  977. }
  978. }
  979. fn method_new_texture(
  980. &mut self,
  981. width: u16,
  982. height: u16,
  983. data: &Vec<u8>,
  984. gfx_texture_id: TextureId,
  985. ) -> Result<()> {
  986. let texture = self.ctx.new_texture_from_rgba8(width, height, data);
  987. if DEBUG_GFXAPI {
  988. debug!(target: "gfx", "Invoked method: new_texture({}, {}, ..., {}) -> {:?}",
  989. width, height, gfx_texture_id, texture);
  990. //debug!(target: "gfx", "Invoked method: new_texture({}, {}, ..., {}) -> {:?}\n{}",
  991. // width, height, gfx_texture_id, texture,
  992. // ansi_texture(width as usize, height as usize, &data));
  993. }
  994. if let Some(_) = self.textures.insert(gfx_texture_id, texture) {
  995. if DEBUG_TRAX {
  996. get_trax().lock().put_stat(2);
  997. }
  998. //panic!("Duplicate texture ID={gfx_texture_id} detected!");
  999. return Err(Error::GfxDuplicateTextureID)
  1000. }
  1001. if DEBUG_TRAX {
  1002. get_trax().lock().put_stat(0);
  1003. }
  1004. Ok(())
  1005. }
  1006. fn method_delete_texture(&mut self, gfx_texture_id: TextureId) -> Result<()> {
  1007. let Some(texture) = self.textures.remove(&gfx_texture_id) else {
  1008. if DEBUG_TRAX {
  1009. get_trax().lock().put_stat(2);
  1010. }
  1011. //.expect("couldn't find gfx_texture_id");
  1012. return Err(Error::GfxUnknownTextureID)
  1013. };
  1014. if DEBUG_GFXAPI {
  1015. debug!(target: "gfx", "Invoked method: delete_texture({} => {:?})",
  1016. gfx_texture_id, texture);
  1017. }
  1018. self.ctx.delete_texture(texture);
  1019. if DEBUG_TRAX {
  1020. get_trax().lock().put_stat(0);
  1021. }
  1022. Ok(())
  1023. }
  1024. fn method_new_vertex_buffer(
  1025. &mut self,
  1026. verts: &[Vertex],
  1027. gfx_buffer_id: BufferId,
  1028. ) -> Result<()> {
  1029. let buffer = self.ctx.new_buffer(
  1030. BufferType::VertexBuffer,
  1031. BufferUsage::Immutable,
  1032. BufferSource::slice(verts),
  1033. );
  1034. if DEBUG_GFXAPI {
  1035. debug!(target: "gfx", "Invoked method: new_vertex_buffer(..., {}) -> {:?}",
  1036. gfx_buffer_id, buffer);
  1037. //debug!(target: "gfx", "Invoked method: new_vertex_buffer({:?}, {}) -> {:?}",
  1038. // verts, gfx_buffer_id, buffer);
  1039. }
  1040. if let Some(_) = self.buffers.insert(gfx_buffer_id, buffer) {
  1041. if DEBUG_TRAX {
  1042. get_trax().lock().put_stat(2);
  1043. }
  1044. //panic!("Duplicate vertex buffer ID={gfx_buffer_id} detected!");
  1045. return Err(Error::GfxDuplicateBufferID)
  1046. }
  1047. if DEBUG_TRAX {
  1048. get_trax().lock().put_stat(0);
  1049. }
  1050. Ok(())
  1051. }
  1052. fn method_new_index_buffer(&mut self, indices: &[u16], gfx_buffer_id: BufferId) -> Result<()> {
  1053. let buffer = self.ctx.new_buffer(
  1054. BufferType::IndexBuffer,
  1055. BufferUsage::Immutable,
  1056. BufferSource::slice(&indices),
  1057. );
  1058. if DEBUG_GFXAPI {
  1059. debug!(target: "gfx", "Invoked method: new_index_buffer({}) -> {:?}",
  1060. gfx_buffer_id, buffer);
  1061. //debug!(target: "gfx", "Invoked method: new_index_buffer({:?}, {}) -> {:?}",
  1062. // indices, gfx_buffer_id, buffer);
  1063. }
  1064. if let Some(_) = self.buffers.insert(gfx_buffer_id, buffer) {
  1065. if DEBUG_TRAX {
  1066. get_trax().lock().put_stat(2);
  1067. }
  1068. //panic!("Duplicate index buffer ID={gfx_buffer_id} detected!");
  1069. return Err(Error::GfxDuplicateBufferID)
  1070. }
  1071. if DEBUG_TRAX {
  1072. get_trax().lock().put_stat(0);
  1073. }
  1074. Ok(())
  1075. }
  1076. fn method_delete_buffer(&mut self, gfx_buffer_id: BufferId) -> Result<()> {
  1077. let Some(buffer) = self.buffers.remove(&gfx_buffer_id) else {
  1078. if DEBUG_TRAX {
  1079. get_trax().lock().put_stat(2);
  1080. }
  1081. //.expect("couldn't find gfx_buffer_id");
  1082. return Err(Error::GfxUnknownBufferID)
  1083. };
  1084. if DEBUG_GFXAPI {
  1085. debug!(target: "gfx", "Invoked method: delete_buffer({} => {:?})",
  1086. gfx_buffer_id, buffer);
  1087. }
  1088. self.ctx.delete_buffer(buffer);
  1089. if DEBUG_TRAX {
  1090. get_trax().lock().put_stat(0);
  1091. }
  1092. Ok(())
  1093. }
  1094. fn method_new_anim(
  1095. &mut self,
  1096. gfx_anim_id: AnimId,
  1097. frames_len: usize,
  1098. oneshot: bool,
  1099. ) -> Result<()> {
  1100. if DEBUG_GFXAPI {
  1101. debug!(target: "gfx", "Invoked method: new_anim({gfx_anim_id}, {frames_len}, {oneshot})");
  1102. }
  1103. if let Some(_) = self.anims.insert(gfx_anim_id, GfxSeqAnim::new(frames_len, oneshot)) {
  1104. //panic!("Duplicate index buffer ID={gfx_buffer_id} detected!");
  1105. return Err(Error::GfxDuplicateAnimID)
  1106. }
  1107. Ok(())
  1108. }
  1109. fn method_update_anim(
  1110. &mut self,
  1111. gfx_anim_id: AnimId,
  1112. frame_idx: usize,
  1113. frame: AnimFrame,
  1114. ) -> Result<()> {
  1115. let Some(anim) = self.anims.get_mut(&gfx_anim_id) else {
  1116. //.expect("couldn't find gfx_anim_id");
  1117. return Err(Error::GfxUnknownAnimID)
  1118. };
  1119. if DEBUG_GFXAPI {
  1120. debug!(target: "gfx", "Invoked method: update_anim({gfx_anim_id}[{frame_idx}] => {frame:?})");
  1121. }
  1122. anim.set(frame_idx, frame, &self.textures, &self.buffers);
  1123. Ok(())
  1124. }
  1125. fn method_delete_anim(&mut self, gfx_anim_id: AnimId) -> Result<()> {
  1126. let Some(anim) = self.anims.remove(&gfx_anim_id) else {
  1127. //.expect("couldn't find gfx_anim_id");
  1128. return Err(Error::GfxUnknownAnimID)
  1129. };
  1130. if DEBUG_GFXAPI {
  1131. debug!(target: "gfx", "Invoked method: delete_anim({} => {:?})", gfx_anim_id, anim);
  1132. }
  1133. Ok(())
  1134. }
  1135. fn method_replace_draw_calls(
  1136. &mut self,
  1137. timest: Timestamp,
  1138. dcs: Vec<(DcId, DrawCall)>,
  1139. ) -> Result<()> {
  1140. if DEBUG_GFXAPI {
  1141. debug!(target: "gfx", "Invoked method: replace_draw_calls({:?})", dcs);
  1142. }
  1143. for (key, val) in dcs {
  1144. let Some(val) = val.compile(&self.textures, &self.buffers, timest) else {
  1145. if DEBUG_TRAX {
  1146. get_trax().lock().put_stat(3);
  1147. }
  1148. error!(target: "gfx", "fatal: replace_draw_calls({timest}, ...) failed with item ID={key}");
  1149. continue
  1150. };
  1151. //self.draw_calls.insert(key, val);
  1152. match self.draw_calls.get_mut(&key) {
  1153. Some(old_val) => {
  1154. // Only replace the draw call if it is more recent
  1155. if old_val.timest < timest {
  1156. if DEBUG_TRAX {
  1157. get_trax().lock().put_stat(0);
  1158. }
  1159. *old_val = val;
  1160. } else {
  1161. trace!(target: "gfx", "Rejected stale draw_call {key}: {val:?}");
  1162. if DEBUG_TRAX {
  1163. get_trax().lock().put_stat(2);
  1164. }
  1165. }
  1166. }
  1167. None => {
  1168. self.draw_calls.insert(key, val);
  1169. if DEBUG_TRAX {
  1170. get_trax().lock().put_stat(1);
  1171. }
  1172. }
  1173. }
  1174. }
  1175. Ok(())
  1176. }
  1177. fn trax_method(&self, epoch: EpochIndex, method: &GraphicsMethod) {
  1178. let mut trax = get_trax().lock();
  1179. match method {
  1180. GraphicsMethod::NewTexture((_, _, _, gtex_id, tag)) => {
  1181. trax.put_tex(epoch, *gtex_id, *tag);
  1182. }
  1183. GraphicsMethod::DeleteTexture((gtex_id, tag)) => {
  1184. trax.del_tex(epoch, *gtex_id, *tag);
  1185. }
  1186. GraphicsMethod::NewVertexBuffer((verts, gbuff_id, tag)) => {
  1187. trax.put_verts(epoch, verts.clone(), *gbuff_id, *tag, 0);
  1188. }
  1189. GraphicsMethod::NewIndexBuffer((idxs, gbuff_id, tag)) => {
  1190. trax.put_idxs(epoch, idxs.clone(), *gbuff_id, *tag, 1);
  1191. }
  1192. GraphicsMethod::DeleteBuffer((gbuff_id, tag, buftype)) => {
  1193. trax.del_buf(epoch, *gbuff_id, *tag, *buftype);
  1194. }
  1195. GraphicsMethod::NewSeqAnim { .. } => {
  1196. //trax.put_idxs(epoch, idxs.clone(), *gbuff_id, *tag, 1);
  1197. }
  1198. GraphicsMethod::UpdateSeqAnim { .. } => {
  1199. //trax.put_idxs(epoch, idxs.clone(), *gbuff_id, *tag, 1);
  1200. }
  1201. GraphicsMethod::DeleteSeqAnim(..) => {
  1202. //trax.del_buf(epoch, *gbuff_id, *tag, *buftype);
  1203. }
  1204. GraphicsMethod::ReplaceGfxDrawCalls { batch_id, timest, dcs } => {
  1205. trax.put_dcs(epoch, *batch_id, *timest, dcs);
  1206. }
  1207. GraphicsMethod::StartBatch((batch_id, debug_str)) => {
  1208. trax.put_start_batch(epoch, *batch_id, *debug_str);
  1209. }
  1210. GraphicsMethod::EndBatch(batch_id) => {
  1211. trax.put_end_batch(epoch, *batch_id);
  1212. }
  1213. };
  1214. }
  1215. fn egl_ctx_is_disabled(&self) -> bool {
  1216. #[cfg(target_os = "android")]
  1217. {
  1218. let egl_ctx = unsafe { (self.libegl.eglGetCurrentContext)() };
  1219. egl_ctx.is_null()
  1220. }
  1221. #[cfg(not(target_os = "android"))]
  1222. false
  1223. }
  1224. }
  1225. /// This is used to process the method queue while the screen is off to avoid the queue
  1226. /// becoming congested and using up all the memory.
  1227. /// Will drop alloc/delete pairs, and merge draw calls together.
  1228. struct PruneMethodHeap {
  1229. /// Newly allocated buffers while screen was off
  1230. new_buf: HashMap<BufferId, GraphicsMethod>,
  1231. /// Newly allocated textures while screen was off
  1232. new_tex: HashMap<TextureId, GraphicsMethod>,
  1233. /// Deleted objects
  1234. del: Vec<GraphicsMethod>,
  1235. /// Draw calls
  1236. dcs: HashMap<DcId, (BatchGuardId, Timestamp, DrawCall)>,
  1237. epoch: EpochIndex,
  1238. }
  1239. impl PruneMethodHeap {
  1240. fn new(epoch: EpochIndex) -> Self {
  1241. Self {
  1242. new_buf: HashMap::new(),
  1243. new_tex: HashMap::new(),
  1244. del: vec![],
  1245. dcs: HashMap::new(),
  1246. epoch,
  1247. }
  1248. }
  1249. fn drain(&mut self, methods: Vec<(EpochIndex, GraphicsMethod)>) {
  1250. // Process as many methods as we can
  1251. for (epoch, method) in methods {
  1252. if epoch < self.epoch {
  1253. // Discard old rubbish
  1254. trace!(target: "gfx::pruner", "Discard method with old epoch: {epoch} curr: {} [method={method:?}]", self.epoch);
  1255. continue
  1256. }
  1257. assert_eq!(epoch, self.epoch);
  1258. self.process_method(method);
  1259. }
  1260. }
  1261. fn process_method(&mut self, method: GraphicsMethod) {
  1262. match method.clone() {
  1263. GraphicsMethod::NewTexture((_, _, _, gtex_id, _)) => {
  1264. self.new_tex.insert(gtex_id, method);
  1265. }
  1266. GraphicsMethod::DeleteTexture((gtex_id, _)) => {
  1267. if self.new_tex.remove(&gtex_id).is_none() {
  1268. self.del.push(method);
  1269. }
  1270. }
  1271. GraphicsMethod::NewVertexBuffer((_, gbuff_id, _)) => {
  1272. self.new_buf.insert(gbuff_id, method);
  1273. }
  1274. GraphicsMethod::NewIndexBuffer((_, gbuff_id, _)) => {
  1275. self.new_buf.insert(gbuff_id, method);
  1276. }
  1277. GraphicsMethod::DeleteBuffer((gbuff_id, _, _)) => {
  1278. if self.new_buf.remove(&gbuff_id).is_none() {
  1279. self.del.push(method);
  1280. }
  1281. }
  1282. GraphicsMethod::NewSeqAnim { .. } => {
  1283. //self.new_buf.insert(gbuff_id, method);
  1284. }
  1285. GraphicsMethod::UpdateSeqAnim { .. } => {
  1286. //self.new_buf.insert(gbuff_id, method);
  1287. }
  1288. GraphicsMethod::DeleteSeqAnim(..) => {
  1289. //if self.new_buf.remove(&gbuff_id).is_none() {
  1290. // self.del.push(method);
  1291. //}
  1292. }
  1293. GraphicsMethod::ReplaceGfxDrawCalls { batch_id, timest, dcs } => {
  1294. self.method_replace_draw_calls(batch_id, timest, dcs)
  1295. }
  1296. // Discard batches since we will apply everything all at once anyway
  1297. // once the screen is switched on.
  1298. GraphicsMethod::StartBatch(_) => {}
  1299. GraphicsMethod::EndBatch(_) => {}
  1300. }
  1301. }
  1302. fn method_replace_draw_calls(
  1303. &mut self,
  1304. batch_id: BatchGuardId,
  1305. timest: Timestamp,
  1306. dcs: Vec<(DcId, DrawCall)>,
  1307. ) {
  1308. for (key, val) in dcs {
  1309. match self.dcs.get_mut(&key) {
  1310. Some(old_val) => {
  1311. // Only replace the draw call if it is more recent
  1312. if old_val.1 < timest {
  1313. *old_val = (batch_id, timest, val);
  1314. } else {
  1315. trace!(target: "gfx::pruner", "Rejected stale draw_call {key}: {val:?}");
  1316. }
  1317. }
  1318. None => {
  1319. self.dcs.insert(key, (batch_id, timest, val));
  1320. }
  1321. }
  1322. }
  1323. }
  1324. /// Collect everything now the screen is on
  1325. fn recv_all(&mut self) -> Vec<GraphicsMethod> {
  1326. // Inhale that smoke deep
  1327. let mut meth = Vec::with_capacity(
  1328. self.new_buf.len() + self.new_tex.len() + self.del.len() + self.dcs.len(),
  1329. );
  1330. let new_buf = std::mem::take(&mut self.new_buf);
  1331. let new_tex = std::mem::take(&mut self.new_tex);
  1332. meth.extend(new_buf.into_values());
  1333. meth.extend(new_tex.into_values());
  1334. meth.append(&mut self.del);
  1335. for (dc_id, (batch_id, timest, dc)) in std::mem::take(&mut self.dcs) {
  1336. meth.push(GraphicsMethod::ReplaceGfxDrawCalls {
  1337. batch_id,
  1338. timest,
  1339. dcs: vec![(dc_id, dc)],
  1340. });
  1341. }
  1342. meth
  1343. }
  1344. }
  1345. impl EventHandler for Stage {
  1346. fn update(&mut self) {
  1347. // todo: trax is all messed up in this func
  1348. let methods = std::mem::take(&mut *self.method_queue.lock());
  1349. if self.egl_ctx_is_disabled() {
  1350. #[cfg(target_os = "android")]
  1351. {
  1352. self.refresh_task = None;
  1353. }
  1354. // Immediately apply any pending batches when the screen is switched off
  1355. let batch_ids: Vec<_> = self.batches.keys().cloned().collect();
  1356. for batch_id in batch_ids {
  1357. self.process_method(GraphicsMethod::EndBatch(batch_id));
  1358. }
  1359. self.batches.clear();
  1360. // Screen is off so collect all methods into the pruner
  1361. self.pruner.drain(methods);
  1362. self.screen_was_off = true;
  1363. return
  1364. }
  1365. #[cfg(target_os = "android")]
  1366. if self.refresh_task.is_none() {
  1367. // For animations do periodic refresh every 40 ms
  1368. self.refresh_task = Some(self.ex.spawn(async move {
  1369. loop {
  1370. darkfi::system::msleep(40).await;
  1371. miniquad::window::schedule_update();
  1372. }
  1373. }));
  1374. }
  1375. // We actually want to skip draining the prune queue the first time so
  1376. // draw actually gets a chance to be called first.
  1377. // Otherwise we will just see a black screen for a sec or so.
  1378. if self.screen_was_off {
  1379. self.screen_was_off = false;
  1380. } else {
  1381. let methods = self.pruner.recv_all();
  1382. assert!(methods.is_empty() || self.batches.is_empty());
  1383. // Process all cached methods by the pruner from while the screen was off.
  1384. for method in methods {
  1385. // Stale methods will be dropped by pruner, so they will not be caught by trax
  1386. // while the screen is off.
  1387. if DEBUG_TRAX {
  1388. self.trax_method(self.epoch, &method);
  1389. }
  1390. // We discard batches here but process_method uses them so implement this
  1391. // workaround.
  1392. match method {
  1393. GraphicsMethod::ReplaceGfxDrawCalls { batch_id: _, timest, dcs } => {
  1394. if let Err(err) = self.method_replace_draw_calls(timest, dcs) {
  1395. e!("process_method for ReplaceGfxDrawCalls failed err: {err:?}");
  1396. panic!("process_method failed!")
  1397. }
  1398. }
  1399. _ => self.process_method(method),
  1400. }
  1401. if DEBUG_TRAX {
  1402. get_trax().lock().flush();
  1403. }
  1404. }
  1405. }
  1406. // Process as many methods as we can
  1407. for (epoch, method) in methods {
  1408. if DEBUG_TRAX {
  1409. self.trax_method(epoch, &method);
  1410. }
  1411. if epoch < self.epoch {
  1412. if DEBUG_TRAX {
  1413. let mut trax = get_trax().lock();
  1414. trax.put_stat(1);
  1415. trax.flush();
  1416. }
  1417. // Discard old rubbish
  1418. trace!(target: "gfx", "Discard method with old epoch: {epoch} curr: {} [method={method:?}]", self.epoch);
  1419. continue
  1420. }
  1421. assert_eq!(epoch, self.epoch);
  1422. self.process_method(method);
  1423. if DEBUG_TRAX {
  1424. get_trax().lock().flush();
  1425. }
  1426. }
  1427. }
  1428. fn draw(&mut self) {
  1429. self.ctx.begin_default_pass(PassAction::clear_color(0., 0., 0., 1.));
  1430. self.ctx.apply_pipeline(&self.pipeline);
  1431. // This will make the top left (0, 0) and the bottom right (1, 1)
  1432. // Default is (-1, 1) -> (1, -1)
  1433. let proj = glam::Mat4::from_translation(glam::Vec3::new(-1., 1., 0.)) *
  1434. glam::Mat4::from_scale(glam::Vec3::new(2., -2., 1.));
  1435. let mut uniforms_data = [0u8; 128];
  1436. let data: [u8; 64] = unsafe { std::mem::transmute_copy(&proj) };
  1437. uniforms_data[0..64].copy_from_slice(&data);
  1438. //let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
  1439. //uniforms_data[64..].copy_from_slice(&data);
  1440. assert_eq!(128, 2 * UniformType::Mat4.size());
  1441. let (screen_w, screen_h) = miniquad::window::screen_size();
  1442. // Mark all anims as invisible
  1443. for (_, anim) in self.anims.iter_mut() {
  1444. anim.is_visible = false;
  1445. }
  1446. let mut render_ctx = RenderContext {
  1447. ctx: &mut self.ctx,
  1448. draw_calls: &self.draw_calls,
  1449. uniforms_data,
  1450. white_texture: self.white_texture,
  1451. scale: 1.,
  1452. view: Rectangle::from([0., 0., screen_w, screen_h]),
  1453. cursor: Point::from([0., 0.]),
  1454. anims: &mut self.anims,
  1455. };
  1456. render_ctx.draw();
  1457. self.ctx.commit_frame();
  1458. }
  1459. fn resize_event(&mut self, width: f32, height: f32) {
  1460. t!("resize_event({width}, {height})");
  1461. let filename = get_window_size_filename();
  1462. if let Some(parent) = filename.parent() {
  1463. let _ = std::fs::create_dir_all(parent);
  1464. }
  1465. if let Ok(mut file) = File::create(filename) {
  1466. (width as i32).encode(&mut file).unwrap();
  1467. (height as i32).encode(&mut file).unwrap();
  1468. }
  1469. self.event_pub.notify_resize(Dimension::from([width, height]));
  1470. }
  1471. fn key_down_event(&mut self, keycode: KeyCode, mods: KeyMods, repeat: bool) {
  1472. self.event_pub.notify_key_down(keycode, mods, repeat);
  1473. }
  1474. fn key_up_event(&mut self, keycode: KeyCode, mods: KeyMods) {
  1475. self.event_pub.notify_key_up(keycode, mods);
  1476. }
  1477. fn char_event(&mut self, chr: char, mods: KeyMods, repeat: bool) {
  1478. self.event_pub.notify_char(chr, mods, repeat);
  1479. }
  1480. fn mouse_button_down_event(&mut self, button: MouseButton, x: f32, y: f32) {
  1481. let pos = Point::from([x, y]);
  1482. self.event_pub.notify_mouse_btn_down(button, pos);
  1483. }
  1484. fn mouse_button_up_event(&mut self, button: MouseButton, x: f32, y: f32) {
  1485. let pos = Point::from([x, y]);
  1486. self.event_pub.notify_mouse_btn_up(button, pos);
  1487. }
  1488. fn mouse_motion_event(&mut self, x: f32, y: f32) {
  1489. let pos = Point::from([x, y]);
  1490. self.event_pub.notify_mouse_move(pos);
  1491. }
  1492. fn mouse_wheel_event(&mut self, x: f32, y: f32) {
  1493. let pos = Point::from([x, y]);
  1494. self.event_pub.notify_mouse_wheel(pos);
  1495. }
  1496. /// The id corresponds to multi-touch. Multiple touch events have different ids.
  1497. fn touch_event(&mut self, phase: TouchPhase, id: u64, x: f32, y: f32) {
  1498. let pos = Point::from([x, y]);
  1499. self.event_pub.notify_touch(phase, id, pos);
  1500. }
  1501. fn quit_requested_event(&mut self) {
  1502. debug!(target: "gfx", "quit requested");
  1503. let god = GOD.get().unwrap();
  1504. god.stop_app();
  1505. }
  1506. }
  1507. pub fn run_gui(linux_backend: miniquad::conf::LinuxBackend) {
  1508. let mut window_width = 1024;
  1509. let mut window_height = 768;
  1510. if let Ok(mut file) = File::open(get_window_size_filename()) {
  1511. window_width = Decodable::decode(&mut file).unwrap();
  1512. window_height = Decodable::decode(&mut file).unwrap();
  1513. }
  1514. debug!(target: "gfx", "Window size {window_width} x {window_height}");
  1515. let mut conf = miniquad::conf::Conf {
  1516. window_title: "DarkFi".to_string(),
  1517. window_width,
  1518. window_height,
  1519. high_dpi: true,
  1520. window_resizable: true,
  1521. platform: miniquad::conf::Platform {
  1522. linux_backend,
  1523. #[cfg(target_os = "android")]
  1524. blocking_event_loop: true,
  1525. android_panic_hook: false,
  1526. ..Default::default()
  1527. },
  1528. icon: Some(miniquad::conf::Icon {
  1529. small: favico::SMALL,
  1530. medium: favico::MEDIUM,
  1531. big: favico::BIG,
  1532. }),
  1533. ..Default::default()
  1534. };
  1535. let metal = std::env::args().nth(1).as_deref() == Some("metal");
  1536. conf.platform.apple_gfx_api =
  1537. if metal { conf::AppleGfxApi::Metal } else { conf::AppleGfxApi::OpenGl };
  1538. miniquad::start(conf, || Box::new(Stage::new()));
  1539. }