Skip to main content

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