kas_wgpu/draw/
custom.rs

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