1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License in the LICENSE-APACHE file or at: // https://www.apache.org/licenses/LICENSE-2.0 //! Custom draw pipes use super::DrawWindow; use kas::draw::Pass; use kas::geom::{Rect, Size}; /// Allows use of the low-level graphics API /// /// To use this, write an implementation of [`CustomPipe`], then pass the /// corresponding [`CustomPipeBuilder`] to [`crate::Toolkit::new_custom`]. pub trait DrawCustom<CW: CustomWindow> { /// Call a custom draw pipe fn custom(&mut self, pass: Pass, rect: Rect, param: CW::Param); } /// Builder for a [`CustomPipe`] pub trait CustomPipeBuilder { type Pipe: CustomPipe; /// Build a pipe /// /// The given texture format and depth format should be used to construct a /// compatible [`wgpu::RenderPipeline`]. fn build( &mut self, device: &wgpu::Device, tex_format: wgpu::TextureFormat, depth_format: wgpu::TextureFormat, ) -> Self::Pipe; } /// A custom draw pipe /// /// A "draw pipe" consists of draw primitives (usually triangles), resources /// (textures), shaders, and pipe configuration (e.g. blending mode). /// A custom pipe allows direct use of the WebGPU graphics stack. /// /// To use this, pass the corresponding [`CustomPipeBuilder`] to /// [`crate::Toolkit::new_custom`]. /// /// Note that `kas-wgpu` accepts only a single custom pipe. To use more than /// one, you will have to implement your own multiplexer (presumably using an /// enum for the `Param` type). pub trait CustomPipe { /// Associated per-window state for the custom pipe type Window: CustomWindow + 'static; /// Construct a window associated with this pipeline fn new_window(&self, device: &wgpu::Device, size: Size) -> Self::Window; /// Called whenever the window is resized fn resize( &self, window: &mut Self::Window, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, size: Size, ); /// Per-frame updates /// /// This is called once per frame before rendering operations, and may for /// example be used to update uniform buffers. /// /// This method is optional; by default it does nothing. fn update( &self, _window: &mut Self::Window, _device: &wgpu::Device, _encoder: &mut wgpu::CommandEncoder, ) { } /// Render (pass) /// /// Each item drawn is associated with a clip region, and each of these /// with a pass. This method will be called once for each clip region in use /// (possibly also for other clip regions). Drawing uses an existing texture /// and occurs after most other draw operations, but before text. /// /// Note that the pass in use has a depth stencil attachment, therefore the /// render pipeline must be constructed with a compatible /// [`wgpu::RenderPipelineDescriptor::depth_stencil_state`]. Since a /// scissor rect is already applied to the draw pass, it is safe to use /// `depth_compare: wgpu::CompareFunction::Always` here. /// /// This method is optional; by default it does nothing. #[allow(unused)] fn render_pass<'a>( &'a self, window: &'a mut Self::Window, device: &wgpu::Device, pass: usize, rpass: &mut wgpu::RenderPass<'a>, ) { } /// Render (final) /// /// This method is the last step in drawing a frame except for text /// rendering. Depending on the application, it may make more sense to draw /// in [`CustomPipe::render_pass`] or in this method. /// /// A depth buffer is available and may be used with /// `depth_compare: wgpu::CompareFunction::GreaterEqual` to avoid drawing /// over pop-up elements and outside of scroll regions. /// /// This method is optional; by default it does nothing. #[allow(unused)] fn render_final<'a>( &'a self, window: &'a mut Self::Window, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, frame_view: &wgpu::TextureView, depth_stencil_attachment: wgpu::RenderPassDepthStencilAttachmentDescriptor, size: Size, ) { } } /// Per-window state for a custom draw pipe /// /// One instance is constructed per window. Since the [`CustomPipe`] is not /// accessible during a widget's [`kas::Layout::draw`] calls, this struct must /// batch per-frame draw data. pub trait CustomWindow { /// User parameter type type Param; /// Invoke user-defined custom routine /// /// Custom add-primitives / update function called from user code by /// [`DrawCustom::custom`]. fn invoke(&mut self, pass: Pass, rect: Rect, param: Self::Param); } /// A dummy implementation (does nothing) impl CustomPipeBuilder for () { type Pipe = (); fn build( &mut self, _: &wgpu::Device, _: wgpu::TextureFormat, _: wgpu::TextureFormat, ) -> Self::Pipe { () } } pub enum Void {} /// A dummy implementation (does nothing) impl CustomPipe for () { type Window = (); fn new_window(&self, _: &wgpu::Device, _: Size) -> Self::Window { () } fn resize( &self, _: &mut Self::Window, _: &wgpu::Device, _: &mut wgpu::CommandEncoder, _: Size, ) { } } /// A dummy implementation (does nothing) impl CustomWindow for () { type Param = Void; fn invoke(&mut self, _: Pass, _: Rect, _: Self::Param) {} } impl<CW: CustomWindow> DrawCustom<CW> for DrawWindow<CW> { fn custom(&mut self, pass: Pass, rect: Rect, param: CW::Param) { self.custom.invoke(pass, rect, param); } }