1
0

wgpu_child_window.rs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. //! Demonstrate how to use dioxus as a child window for use in alternative renderers like wgpu.
  2. //!
  3. //! The code here is borrowed from wry's example:
  4. //! https://github.com/tauri-apps/wry/blob/dev/examples/wgpu.rs
  5. //!
  6. //! To use this feature set `with_as_child_window()` on your desktop config which will then let you
  7. use dioxus::desktop::{
  8. tao::{event::Event as WryEvent, window::WindowBuilder},
  9. use_wry_event_handler, window, Config, DesktopContext,
  10. };
  11. use dioxus::prelude::*;
  12. fn main() {
  13. let config = Config::new()
  14. .with_window(WindowBuilder::new().with_transparent(true))
  15. .with_as_child_window();
  16. dioxus::LaunchBuilder::desktop()
  17. .with_cfg(config)
  18. .launch(app);
  19. }
  20. fn app() -> Element {
  21. let graphics_resources = use_resource(move || async {
  22. GraphicsContextAsyncBuilder {
  23. desktop: window(),
  24. resources_builder: |ctx| Box::pin(GraphicsResources::new(ctx)),
  25. }
  26. .build()
  27. .await
  28. });
  29. // on first render request a redraw
  30. use_effect(|| {
  31. window().window.request_redraw();
  32. });
  33. use_wry_event_handler(move |event, _| {
  34. use dioxus::desktop::tao::event::WindowEvent;
  35. if let WryEvent::RedrawRequested(_id) = event {
  36. let resources = graphics_resources.read();
  37. if let Some(resources) = resources.as_ref() {
  38. resources.with_resources(|resources| resources.render());
  39. }
  40. }
  41. if let WryEvent::WindowEvent {
  42. event: WindowEvent::Resized(new_size),
  43. ..
  44. } = event
  45. {
  46. let ctx = graphics_resources.value();
  47. ctx.as_ref().unwrap().with_resources(|srcs| {
  48. let mut cfg = srcs.config.clone();
  49. cfg.width = new_size.width;
  50. cfg.height = new_size.height;
  51. srcs.surface.configure(&srcs.device, &cfg);
  52. });
  53. window().window.request_redraw();
  54. }
  55. });
  56. rsx! {
  57. div {
  58. color: "blue",
  59. width: "100vw",
  60. height: "100vh",
  61. display: "flex",
  62. justify_content: "center",
  63. align_items: "center",
  64. font_size: "20px",
  65. div { "text overlaied on wgpu surface!" }
  66. }
  67. }
  68. }
  69. /// This borrows from the `window` which is contained within an `Arc` so we need to wrap it in a self-borrowing struct
  70. /// to be able to borrow the window for the wgpu::Surface
  71. #[ouroboros::self_referencing]
  72. struct GraphicsContext {
  73. desktop: DesktopContext,
  74. #[borrows(desktop)]
  75. #[not_covariant]
  76. resources: GraphicsResources<'this>,
  77. }
  78. struct GraphicsResources<'a> {
  79. surface: wgpu::Surface<'a>,
  80. device: wgpu::Device,
  81. pipeline: wgpu::RenderPipeline,
  82. queue: wgpu::Queue,
  83. config: wgpu::SurfaceConfiguration,
  84. }
  85. impl<'a> GraphicsResources<'a> {
  86. async fn new(context: &'a DesktopContext) -> Self {
  87. let window = &context.window;
  88. let size = window.inner_size();
  89. let instance = wgpu::Instance::default();
  90. let surface: wgpu::Surface<'a> = instance.create_surface(window).unwrap();
  91. let adapter = instance
  92. .request_adapter(&wgpu::RequestAdapterOptions {
  93. power_preference: wgpu::PowerPreference::default(),
  94. force_fallback_adapter: false,
  95. // Request an adapter which can render to our surface
  96. compatible_surface: Some(&surface),
  97. })
  98. .await
  99. .expect("Failed to find an appropriate adapter");
  100. // Create the logical device and command queue
  101. let (device, queue) = adapter
  102. .request_device(
  103. &wgpu::DeviceDescriptor {
  104. label: None,
  105. required_features: wgpu::Features::empty(),
  106. // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
  107. required_limits: wgpu::Limits::downlevel_webgl2_defaults()
  108. .using_resolution(adapter.limits()),
  109. },
  110. None,
  111. )
  112. .await
  113. .expect("Failed to create device");
  114. // Load the shaders from disk
  115. let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
  116. label: None,
  117. source: wgpu::ShaderSource::Wgsl(
  118. r#"
  119. @vertex
  120. fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
  121. let x = f32(i32(in_vertex_index) - 1);
  122. let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
  123. return vec4<f32>(x, y, 0.0, 1.0);
  124. }
  125. @fragment
  126. fn fs_main() -> @location(0) vec4<f32> {
  127. return vec4<f32>(1.0, 0.0, 0.0, 1.0);
  128. }
  129. "#
  130. .into(),
  131. ),
  132. });
  133. let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
  134. label: None,
  135. bind_group_layouts: &[],
  136. push_constant_ranges: &[],
  137. });
  138. let swapchain_capabilities = surface.get_capabilities(&adapter);
  139. let swapchain_format = swapchain_capabilities.formats[0];
  140. let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
  141. label: None,
  142. layout: Some(&pipeline_layout),
  143. vertex: wgpu::VertexState {
  144. module: &shader,
  145. entry_point: "vs_main",
  146. buffers: &[],
  147. },
  148. fragment: Some(wgpu::FragmentState {
  149. module: &shader,
  150. entry_point: "fs_main",
  151. targets: &[Some(swapchain_format.into())],
  152. }),
  153. primitive: wgpu::PrimitiveState::default(),
  154. depth_stencil: None,
  155. multisample: wgpu::MultisampleState::default(),
  156. multiview: None,
  157. });
  158. let config = wgpu::SurfaceConfiguration {
  159. usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
  160. format: swapchain_format,
  161. width: size.width,
  162. height: size.height,
  163. present_mode: wgpu::PresentMode::Fifo,
  164. desired_maximum_frame_latency: 2,
  165. alpha_mode: wgpu::CompositeAlphaMode::PostMultiplied,
  166. view_formats: vec![],
  167. };
  168. surface.configure(&device, &config);
  169. GraphicsResources {
  170. surface,
  171. device,
  172. pipeline,
  173. queue,
  174. config,
  175. }
  176. }
  177. fn render(&self) {
  178. let GraphicsResources {
  179. surface,
  180. device,
  181. pipeline,
  182. queue,
  183. ..
  184. } = self;
  185. let frame = surface
  186. .get_current_texture()
  187. .expect("Failed to acquire next swap chain texture");
  188. let view = frame
  189. .texture
  190. .create_view(&wgpu::TextureViewDescriptor::default());
  191. let mut encoder =
  192. device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
  193. {
  194. let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
  195. label: None,
  196. color_attachments: &[Some(wgpu::RenderPassColorAttachment {
  197. view: &view,
  198. resolve_target: None,
  199. ops: wgpu::Operations {
  200. load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
  201. store: wgpu::StoreOp::Store,
  202. },
  203. })],
  204. depth_stencil_attachment: None,
  205. timestamp_writes: None,
  206. occlusion_query_set: None,
  207. });
  208. rpass.set_pipeline(pipeline);
  209. rpass.draw(0..3, 0..1);
  210. }
  211. queue.submit(Some(encoder.finish()));
  212. frame.present();
  213. }
  214. }