Skip to main content

agg_gui_wgpu/
custom_render.rs

1//! Public hook for user widgets to inject their own wgpu render pass(es)
2//! into the agg-gui frame.
3//!
4//! # Why
5//!
6//! agg-gui paints 2-D widgets via deferred `DrawCommand`s that flush into a
7//! single `wgpu::CommandEncoder` in [`WgpuGfxCtx::end_frame`](crate::WgpuGfxCtx::end_frame). A
8//! 3-D viewport widget (e.g. AtomArtist's `Viewport3dWidget`) needs to run
9//! its own render pass(es) interleaved with that 2-D stream so its output
10//! ends up on the same surface or layer texture, with depth-correct
11//! ordering relative to surrounding 2-D content.
12//!
13//! The demo's own `WgpuCubeWidget` used to push a hard-coded
14//! `DrawCommand::DrawBarGrid` variant directly into the command list — which
15//! worked but was not extensible, and it kept demo content inside the
16//! renderer. This module generalises that pattern so any widget can plug in;
17//! the cube now rides this path like everyone else.
18//!
19//! # How to use
20//!
21//! 1. Define a struct that implements [`WgpuCustomRender`].
22//! 2. Hold it in `Rc<RefCell<dyn WgpuCustomRender>>` so the widget can
23//!    keep a clone (lazy-init / persistent buffers across frames).
24//! 3. From your widget's `paint(ctx)`, downcast `ctx` to
25//!    [`WgpuGfxCtx`](crate::WgpuGfxCtx) via
26//!    [`DrawCtx::as_any_mut`](agg_gui::draw_ctx::DrawCtx::as_any_mut) and call
27//!    [`WgpuGfxCtx::push_custom_render`](crate::WgpuGfxCtx::push_custom_render).
28//!
29//! # Pass semantics
30//!
31//! When the executor reaches a `DrawCommand::Custom` entry, it:
32//!   1. Ends the active 2-D render pass.
33//!   2. Calls [`WgpuCustomRender::render`] with a [`WgpuCustomRenderCtx`]
34//!      pointing at the same encoder + active target view.
35//!   3. Reopens a 2-D pass with `LoadOp::Load` so subsequent 2-D content
36//!      composites cleanly on top.
37//!
38//! The custom renderer may record any number of its own passes against
39//! the encoder, including offscreen passes followed by a blit to
40//! `target_view`. Conventionally the implementor should leave its target
41//! state untouched after returning (no in-progress pass, no orphaned
42//! buffer mappings).
43
44use std::cell::RefCell;
45use std::rc::Rc;
46
47use crate::pipelines::WgpuPipelines;
48
49/// Inputs handed to a [`WgpuCustomRender`] implementor at execution time.
50///
51/// Field invariants:
52///   - `encoder` is borrowed for the duration of `render` only — record
53///     into it but don't take ownership.
54///   - `target_view` is the surface or active layer texture view; the 2-D
55///     pass on either side of this call uses the same view.
56///   - `target_size` matches the active target's full pixel dimensions.
57///   - `screen_rect` is the widget's logical-pixel rect in **bottom-up
58///     Y-up coords** (agg-gui convention). Convert to wgpu's top-down
59///     Y-down convention as needed when computing scissor / viewport.
60///   - `pipelines` is the shared 2-D pipeline collection.  Implementors
61///     that own an [`crate::ssaa::SsaaFramebuffer`] can call
62///     [`crate::ssaa::SsaaFramebuffer::blit_to`] with this to composite
63///     their offscreen output onto `target_view` through the same
64///     textured-quad pipeline the 2-D path uses.
65///
66/// Only the renderer constructs this (from `end_frame`); implementors receive
67/// one and read it. It is `#[non_exhaustive]` so further per-pass state can be
68/// added without a breaking release.
69#[non_exhaustive]
70pub struct WgpuCustomRenderCtx<'a> {
71    pub device: &'a wgpu::Device,
72    pub queue: &'a wgpu::Queue,
73    pub encoder: &'a mut wgpu::CommandEncoder,
74    pub target_view: &'a wgpu::TextureView,
75    pub target_size: (u32, u32),
76    pub surface_format: wgpu::TextureFormat,
77    pub screen_rect: agg_gui::Rect,
78    /// Active scissor inherited from the agg-gui clip stack (or `None` if
79    /// the parent pass had no clip). Format is `[x, y, w, h]` in wgpu
80    /// top-down pixel coords.
81    pub parent_clip: Option<[i32; 4]>,
82    /// Shared 2-D pipeline collection — exposed so an offscreen-buffered
83    /// custom renderer can blit its resolved framebuffer onto the active
84    /// 2-D render target without rebuilding the textured-quad pipeline
85    /// itself.  See [`crate::ssaa::SsaaFramebuffer::blit_to`].
86    pub pipelines: &'a WgpuPipelines,
87}
88
89/// Trait implemented by widgets that want to inject custom wgpu render
90/// commands into the frame.
91///
92/// `render` is called from `end_frame()` once per matching
93/// `DrawCommand::Custom` queued during
94/// `paint()`. Implementations are typically held in
95/// `Rc<RefCell<dyn WgpuCustomRender>>` so the widget can lazy-init GPU
96/// state on first use and persist it across frames.
97pub trait WgpuCustomRender {
98    fn render(&mut self, ctx: WgpuCustomRenderCtx<'_>);
99}
100
101/// Convenience type alias for the boxed-trait pattern most callers use.
102pub type SharedCustomRenderer = Rc<RefCell<dyn WgpuCustomRender>>;