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
// 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::PassId;
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: PassId, rect: Rect, param: CW::Param);
}
/// Builder for a [`CustomPipe`]
pub trait CustomPipeBuilder {
type Pipe: CustomPipe;
/// Request a device supporting these features and limits
///
/// See [`wgpu::Adapter::request_device`] and [`wgpu::DeviceDescriptor`] doc.
fn device_descriptor() -> wgpu::DeviceDescriptor<'static> {
Default::default()
}
/// Build a pipe
///
/// A "common" bind group with layout `bgl_common` is available, supplying
/// window sizing and theme lighting information (refer to existing pipes
/// and shaders for usage). Usage is optional.
///
/// The given texture format should be used to construct a
/// compatible [`wgpu::RenderPipeline`].
fn build(
&mut self,
device: &wgpu::Device,
bgl_common: &wgpu::BindGroupLayout,
tex_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 custom graphics pipeline, you must implement your own multiplexer.
pub trait CustomPipe: 'static {
/// Associated per-window state for the custom pipe
type Window: CustomWindow;
/// Construct a window associated with this pipeline
///
/// Note: [`Self::resize`] will be called before usage.
fn new_window(&self, device: &wgpu::Device) -> Self::Window;
/// Called whenever the window is resized
fn resize(
&self,
window: &mut Self::Window,
device: &wgpu::Device,
queue: &wgpu::Queue,
size: Size,
) {
let _ = (window, device, queue, size);
}
/// Per-frame updates
///
/// This is called once per frame before rendering operations, and may for
/// example be used to prepare uniform and buffers.
///
/// This method is optional; by default it does nothing.
fn prepare(
&self,
window: &mut Self::Window,
device: &wgpu::Device,
staging_belt: &mut wgpu::util::StagingBelt,
encoder: &mut wgpu::CommandEncoder,
) {
let _ = (window, device, staging_belt, encoder);
}
/// 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.
///
/// The "common" bind group supplies window scaling and theme lighting
/// information may optionally be set (see [`CustomPipeBuilder::build`]).
///
/// 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>,
bg_common: &'a wgpu::BindGroup,
) {
}
/// 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.
///
/// 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,
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: 'static {
/// 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: PassId, rect: Rect, param: Self::Param);
}
/// A dummy implementation (does nothing)
impl CustomPipeBuilder for () {
type Pipe = ();
fn build(
&mut self,
_: &wgpu::Device,
_: &wgpu::BindGroupLayout,
_: wgpu::TextureFormat,
) -> Self::Pipe {
}
}
pub enum Void {}
/// A dummy implementation (does nothing)
impl CustomPipe for () {
type Window = ();
fn new_window(&self, _: &wgpu::Device) -> Self::Window {}
fn resize(&self, _: &mut Self::Window, _: &wgpu::Device, _: &wgpu::Queue, _: Size) {}
}
/// A dummy implementation (does nothing)
impl CustomWindow for () {
type Param = Void;
fn invoke(&mut self, _: PassId, _: Rect, _: Self::Param) {}
}
impl<CW: CustomWindow> DrawCustom<CW> for DrawWindow<CW> {
fn custom(&mut self, pass: PassId, rect: Rect, param: CW::Param) {
self.custom.invoke(pass, rect, param);
}
}