api.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 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 std::{
  19. cell::Cell,
  20. sync::{
  21. atomic::{AtomicU32, Ordering},
  22. Arc,
  23. },
  24. };
  25. use super::{
  26. anim::Frame as AnimFrame, AnimId, BufferId, DebugTag, DrawCall, TextureFormat, TextureId,
  27. Vertex,
  28. };
  29. use crate::prop::PropertyAtomicGuard;
  30. pub type EpochIndex = u32;
  31. type DcId = u64;
  32. static NEXT_BUFFER_ID: AtomicU32 = AtomicU32::new(0);
  33. static NEXT_TEXTURE_ID: AtomicU32 = AtomicU32::new(0);
  34. static NEXT_ANIM_ID: AtomicU32 = AtomicU32::new(0);
  35. pub type ManagedTexturePtr = Arc<ManagedTexture>;
  36. pub type ManagedBufferPtr = Arc<ManagedBuffer>;
  37. pub type ManagedSeqAnimPtr = Arc<ManagedSeqAnim>;
  38. /// Auto-deletes texture on drop
  39. pub struct ManagedTexture {
  40. pub(super) id: TextureId,
  41. pub(super) epoch: u32,
  42. renderer: Renderer,
  43. pub(super) tag: DebugTag,
  44. }
  45. impl Drop for ManagedTexture {
  46. fn drop(&mut self) {
  47. self.renderer.delete_unmanaged_texture(self.id, self.epoch, self.tag);
  48. }
  49. }
  50. impl std::fmt::Debug for ManagedTexture {
  51. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  52. f.debug_struct("ManagedTexture").field("id", &self.id).finish()
  53. }
  54. }
  55. /// Auto-deletes buffer on drop
  56. pub struct ManagedBuffer {
  57. pub(super) id: BufferId,
  58. pub(super) epoch: u32,
  59. renderer: Renderer,
  60. pub(super) tag: DebugTag,
  61. pub(super) buftype: u8,
  62. }
  63. impl Drop for ManagedBuffer {
  64. fn drop(&mut self) {
  65. self.renderer.delete_unmanaged_buffer(self.id, self.epoch, self.tag, self.buftype);
  66. }
  67. }
  68. impl std::fmt::Debug for ManagedBuffer {
  69. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  70. f.debug_struct("ManagedBuffer").field("id", &self.id).finish()
  71. }
  72. }
  73. pub struct ManagedSeqAnim {
  74. frames_len: usize,
  75. pub id: AnimId,
  76. epoch: u32,
  77. renderer: Renderer,
  78. tag: DebugTag,
  79. }
  80. impl ManagedSeqAnim {
  81. pub fn update(&self, frame_idx: usize, frame: AnimFrame) {
  82. assert!(frame_idx < self.frames_len);
  83. self.renderer.update_unmanaged_anim(self.id, frame_idx, frame, self.epoch, self.tag);
  84. }
  85. /// Hold `frame_idx` for `duration_ms`, then resume ticking. Re-issuing
  86. /// re-arms the hold. No timed commit happens app-side; the anim's own
  87. /// frame ticks implement the resume.
  88. pub fn pause(&self, frame_idx: usize, duration_ms: u64) {
  89. self.renderer.pause_unmanaged_anim(self.id, frame_idx, duration_ms, self.epoch, self.tag);
  90. }
  91. }
  92. impl Drop for ManagedSeqAnim {
  93. fn drop(&mut self) {
  94. self.renderer.delete_unmanaged_anim(self.id, self.epoch, self.tag);
  95. }
  96. }
  97. impl std::fmt::Debug for ManagedSeqAnim {
  98. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  99. f.debug_struct("ManagedSeqAnim").field("id", &self.id).finish()
  100. }
  101. }
  102. /// The async renderer API: allocates GPU resources and modifies the render
  103. /// tree by sending methods to the gfx Stage thread.
  104. pub trait RenderApi {
  105. /// Allocate a texture on the gfx card
  106. fn new_texture(
  107. &self,
  108. width: u16,
  109. height: u16,
  110. data: Vec<u8>,
  111. fmt: TextureFormat,
  112. tag: DebugTag,
  113. ) -> ManagedTexturePtr;
  114. /// Create a buffer to store vertices
  115. fn new_vertex_buffer(&self, verts: Vec<Vertex>, tag: DebugTag) -> ManagedBufferPtr;
  116. /// Create a buffer to store triangle faces
  117. fn new_index_buffer(&self, indices: Vec<u16>, tag: DebugTag) -> ManagedBufferPtr;
  118. /// Modify render tree.
  119. fn replace_draw_calls(&self, dcs: Vec<(DcId, DrawCall)>);
  120. }
  121. #[derive(Clone)]
  122. pub struct Renderer {
  123. /// We are abusing async_channel since it's cloneable whereas std::sync::mpsc is shit.
  124. method_send: async_channel::Sender<(EpochIndex, GraphicsMethod)>,
  125. /// Keep track of the current UI epoch
  126. epoch: Arc<AtomicU32>,
  127. }
  128. impl Renderer {
  129. pub fn new(method_send: async_channel::Sender<(EpochIndex, GraphicsMethod)>) -> Self {
  130. Self { method_send, epoch: Arc::new(AtomicU32::new(0)) }
  131. }
  132. pub(super) fn next_epoch(&self) -> EpochIndex {
  133. self.epoch.fetch_add(1, Ordering::Relaxed) + 1
  134. }
  135. fn send(&self, method: GraphicsMethod) -> EpochIndex {
  136. let epoch = self.epoch.load(Ordering::Relaxed);
  137. self.send_with_epoch(method, epoch);
  138. epoch
  139. }
  140. fn send_with_epoch(&self, method: GraphicsMethod, epoch: EpochIndex) {
  141. let _ = self.method_send.try_send((epoch, method)).unwrap();
  142. }
  143. fn new_unmanaged_texture(
  144. &self,
  145. width: u16,
  146. height: u16,
  147. data: Vec<u8>,
  148. fmt: TextureFormat,
  149. tag: DebugTag,
  150. ) -> (TextureId, EpochIndex) {
  151. let gfx_texture_id = NEXT_TEXTURE_ID.fetch_add(1, Ordering::Relaxed);
  152. let method = GraphicsMethod::NewTexture((width, height, data, fmt, gfx_texture_id, tag));
  153. let epoch = self.send(method);
  154. (gfx_texture_id, epoch)
  155. }
  156. fn delete_unmanaged_texture(&self, texture: TextureId, epoch: EpochIndex, tag: DebugTag) {
  157. let method = GraphicsMethod::DeleteTexture((texture, tag));
  158. self.send_with_epoch(method, epoch);
  159. }
  160. fn new_unmanaged_vertex_buffer(
  161. &self,
  162. verts: Vec<Vertex>,
  163. tag: DebugTag,
  164. ) -> (BufferId, EpochIndex) {
  165. let gfx_buffer_id = NEXT_BUFFER_ID.fetch_add(1, Ordering::Relaxed);
  166. let method = GraphicsMethod::NewVertexBuffer((verts, gfx_buffer_id, tag));
  167. let epoch = self.send(method);
  168. (gfx_buffer_id, epoch)
  169. }
  170. fn new_unmanaged_index_buffer(
  171. &self,
  172. indices: Vec<u16>,
  173. tag: DebugTag,
  174. ) -> (BufferId, EpochIndex) {
  175. let gfx_buffer_id = NEXT_BUFFER_ID.fetch_add(1, Ordering::Relaxed);
  176. let method = GraphicsMethod::NewIndexBuffer((indices, gfx_buffer_id, tag));
  177. let epoch = self.send(method);
  178. (gfx_buffer_id, epoch)
  179. }
  180. fn delete_unmanaged_buffer(
  181. &self,
  182. buffer: BufferId,
  183. epoch: EpochIndex,
  184. tag: DebugTag,
  185. buftype: u8,
  186. ) {
  187. let method = GraphicsMethod::DeleteBuffer((buffer, tag, buftype));
  188. self.send_with_epoch(method, epoch);
  189. }
  190. fn new_unmanaged_anim(
  191. &self,
  192. frames_len: usize,
  193. oneshot: bool,
  194. tag: DebugTag,
  195. ) -> (AnimId, EpochIndex) {
  196. let gfx_anim_id = NEXT_ANIM_ID.fetch_add(1, Ordering::Relaxed);
  197. let method = GraphicsMethod::NewSeqAnim { id: gfx_anim_id, frames_len, oneshot, tag };
  198. let epoch = self.send(method);
  199. (gfx_anim_id, epoch)
  200. }
  201. pub fn new_anim(&self, frames_len: usize, oneshot: bool, tag: DebugTag) -> ManagedSeqAnimPtr {
  202. let (id, epoch) = self.new_unmanaged_anim(frames_len, oneshot, tag);
  203. Arc::new(ManagedSeqAnim { frames_len, id, epoch, renderer: self.clone(), tag })
  204. }
  205. pub fn update_unmanaged_anim(
  206. &self,
  207. anim: AnimId,
  208. frame_idx: usize,
  209. frame: AnimFrame,
  210. epoch: EpochIndex,
  211. tag: DebugTag,
  212. ) {
  213. let method = GraphicsMethod::UpdateSeqAnim { id: anim, frame_idx, frame, tag };
  214. self.send_with_epoch(method, epoch);
  215. }
  216. pub fn pause_unmanaged_anim(
  217. &self,
  218. anim: AnimId,
  219. frame_idx: usize,
  220. duration_ms: u64,
  221. epoch: EpochIndex,
  222. tag: DebugTag,
  223. ) {
  224. let method = GraphicsMethod::PauseSeqAnim { id: anim, frame_idx, duration_ms, tag };
  225. self.send_with_epoch(method, epoch);
  226. // Force an update so the pause takes effect promptly on Android
  227. #[cfg(target_os = "android")]
  228. miniquad::window::schedule_update();
  229. }
  230. fn delete_unmanaged_anim(&self, anim: AnimId, epoch: EpochIndex, tag: DebugTag) {
  231. let method = GraphicsMethod::DeleteSeqAnim((anim, tag));
  232. self.send_with_epoch(method, epoch);
  233. }
  234. /// Property transactions only: notifications are deferred until the
  235. /// guard drops. Since the draw-pass migration there is no gfx-side
  236. /// batching anymore — draw commits are single immediate messages —
  237. /// so the guard no longer opens or closes renderer batches.
  238. pub fn make_guard(&self, _debug_str: Option<&'static str>) -> PropertyAtomicGuard {
  239. PropertyAtomicGuard::none()
  240. }
  241. }
  242. impl RenderApi for Renderer {
  243. fn new_texture(
  244. &self,
  245. width: u16,
  246. height: u16,
  247. data: Vec<u8>,
  248. fmt: TextureFormat,
  249. tag: DebugTag,
  250. ) -> ManagedTexturePtr {
  251. let (id, epoch) = self.new_unmanaged_texture(width, height, data, fmt, tag);
  252. Arc::new(ManagedTexture { id, epoch, renderer: self.clone(), tag })
  253. }
  254. fn new_vertex_buffer(&self, verts: Vec<Vertex>, tag: DebugTag) -> ManagedBufferPtr {
  255. let (id, epoch) = self.new_unmanaged_vertex_buffer(verts, tag);
  256. Arc::new(ManagedBuffer { id, epoch, renderer: self.clone(), tag, buftype: 0 })
  257. }
  258. fn new_index_buffer(&self, indices: Vec<u16>, tag: DebugTag) -> ManagedBufferPtr {
  259. let (id, epoch) = self.new_unmanaged_index_buffer(indices, tag);
  260. Arc::new(ManagedBuffer { id, epoch, renderer: self.clone(), tag, buftype: 1 })
  261. }
  262. fn replace_draw_calls(&self, dcs: Vec<(DcId, DrawCall)>) {
  263. let method = GraphicsMethod::ReplaceGfxDrawCalls { dcs };
  264. self.send(method);
  265. // I'm not sure whether we need this. Anyway its not fully reliable either since
  266. // we have no guarantee that when `Stage::update()` whether this method is ready
  267. // in the receiver.
  268. #[cfg(target_os = "android")]
  269. miniquad::window::schedule_update();
  270. }
  271. }
  272. #[derive(Clone)]
  273. pub enum GraphicsMethod {
  274. NewTexture((u16, u16, Vec<u8>, TextureFormat, TextureId, DebugTag)),
  275. DeleteTexture((TextureId, DebugTag)),
  276. NewVertexBuffer((Vec<Vertex>, BufferId, DebugTag)),
  277. NewIndexBuffer((Vec<u16>, BufferId, DebugTag)),
  278. DeleteBuffer((BufferId, DebugTag, u8)),
  279. NewSeqAnim { id: AnimId, frames_len: usize, oneshot: bool, tag: DebugTag },
  280. UpdateSeqAnim { id: AnimId, frame_idx: usize, frame: AnimFrame, tag: DebugTag },
  281. PauseSeqAnim { id: AnimId, frame_idx: usize, duration_ms: u64, tag: DebugTag },
  282. DeleteSeqAnim((AnimId, DebugTag)),
  283. ReplaceGfxDrawCalls { dcs: Vec<(DcId, DrawCall)> },
  284. Noop,
  285. }
  286. impl std::fmt::Debug for GraphicsMethod {
  287. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  288. match self {
  289. Self::NewTexture(_) => write!(f, "NewTexture"),
  290. Self::DeleteTexture(_) => write!(f, "DeleteTexture"),
  291. Self::NewVertexBuffer(_) => write!(f, "NewVertexBuffer"),
  292. Self::NewIndexBuffer(_) => write!(f, "NewIndexBuffer"),
  293. Self::DeleteBuffer(_) => write!(f, "DeleteBuffer"),
  294. Self::NewSeqAnim { .. } => write!(f, "NewSeqAnim"),
  295. Self::UpdateSeqAnim { .. } => write!(f, "UpdateSeqAnim"),
  296. Self::PauseSeqAnim { .. } => write!(f, "PauseSeqAnim"),
  297. Self::DeleteSeqAnim(_) => write!(f, "DeleteSeqAnim"),
  298. Self::ReplaceGfxDrawCalls { dcs: _ } => write!(f, "ReplaceGfxDrawCalls"),
  299. Self::Noop => write!(f, "Noop"),
  300. }
  301. }
  302. }
  303. impl Default for GraphicsMethod {
  304. fn default() -> Self {
  305. GraphicsMethod::Noop
  306. }
  307. }