wgpu/documentation/best_practices/middleware.rs
1/*!
2# Encapsulating Graphics Work: the Middleware Pattern
3
4Whether you are designing a library to be used by others or modularizing
5your own code, it is important to be able to encapsulate graphics code in a
6way that offers the calling code as much flexibility as possible.
7
8The following pattern is called the "middleware pattern" and works well both
9in separate libraries and in regular modules.
10
11## Middleware libraries
12
13Middleware is a piece of software that fits unobtrusively into an existing
14application, giving it some extra functionality. In the case of `wgpu`,
15middleware are libraries that use the `wgpu` context the user provides to do
16their work. If a library creates the `wgpu` adapter, device, etc. for you,
17it isn't middleware — it would more likely be called a framework.
18
19## API design
20
21This does not have to be the extent of the API; you may have more (or
22different) arguments or more functions, but this is the gist of the
23interactions with `wgpu`.
24
25```ignore
26impl MiddlewareRenderer {
27 /// Create all unchanging resources here.
28 pub fn new(device: &Device, format: &TextureFormat, ..) -> Self;
29
30 /// Prepare for rendering this frame; create all resources that will be
31 /// used during the next render that do not already exist.
32 pub fn prepare(&mut self, ..);
33
34 /// Render using a caller-provided render pass.
35 pub fn render(&self, render_pass: &mut RenderPass<'_>);
36}
37```
38
39The goal of this API is to use as few render passes and submissions as
40possible.
41
42- On GPUs that use _tiled rendering_, there is significant cost to ending a
43 render pass. Therefore, the middleware should accept an existing
44 [`RenderPass`] (presuming it is rendering to a surface/texture provided by
45 the user).
46
47- [`Queue::submit`] is expensive for `wgpu` to execute. Therefore, if the
48 middleware generates a [`CommandBuffer`] when preparing, it should hand
49 that buffer back to the caller to become part of a larger submission,
50 instead of submitting it alone.
51
52## Functions
53
54### New
55
56```ignore
57fn new(device: &Device, format: &TextureFormat, ..) -> Self;
58```
59
60This is where you create your renderer and set up all the static resources.
61Things like pipelines, buffers, or textures should be created and uploaded
62here. When the middleware needs to know the parameters of what it is
63rendering to, favor accepting a [`TextureFormat`], `width`, and `height`
64over a [`SurfaceConfiguration`], as the user may not be rendering to the
65surface but to another texture.
66
67### Prepare
68
69```ignore
70fn prepare(&mut self, ..);
71```
72
73Ideally there should be a minimal amount of resources created per frame, but
74that is often hard to avoid. `prepare()` should create those resources and
75do any other computation required to be ready to render.
76
77### Render
78
79```ignore
80fn render(&self, render_pass: &mut RenderPass<'_>);
81```
82
83This is where the magic happens! Using the resources created during `new()`
84and `prepare()`, render everything using the provided render pass.
85
86The split between `prepare()` and `render()` is not critical but improves
87flexibility. By avoiding borrowing the middleware object exclusively
88(`&mut`) during `render()`, the user has more options for organizing their
89code; for example, they might want to perform command encoding — including
90the middleware's `render()` — in parallel into multiple command buffers, and
91this may be easier if the middleware (and therefore whatever owns it) does
92not have to be exclusively borrowed. It also means that the order of calls
93to `prepare()` of different middleware can be independent of the order of
94drawing.
95
96## Multiple render targets
97
98If your piece of middleware has to render to multiple targets, it is pretty
99unavoidable to have multiple render passes. As much as possible, this
100pattern should be used as a guideline for the design of your API, but it
101doesn't work for every possible piece of middleware out there.
102*/
103
104use crate::{CommandBuffer, Queue, RenderPass, SurfaceConfiguration, TextureFormat};