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
//! Render pipelines the renderer builds from its own fixed sources: blurs,
//! blits, images and glyphs. A launch records, by label, the ones it draws
//! with, and later launches queue those on the warm-up lane before their
//! frames ask for them.
use std::cell::Cell;
use crate::{
lazy_resource::{LazyGpuResource, Pending},
pipeline_compiler::{CompileLane, CompilerSend, PipelineCompiler},
pipeline_recorder::PipelineRecorder,
};
/// One fixed render pipeline, named by a label no other fixed pipeline
/// shares.
pub(crate) struct FixedPipeline {
resource: LazyGpuResource<wgpu::RenderPipeline>,
/// Whether its build is queued, so it is queued once.
queued: Cell<bool>,
/// Whether the loaded driver cache holds it: a draw builds it in its
/// frame as a cache hit.
cached: Cell<bool>,
drawn: Cell<bool>,
}
impl FixedPipeline {
pub(crate) fn new(label: &'static str) -> Self {
Self {
resource: LazyGpuResource::new(label),
queued: Cell::new(false),
cached: Cell::new(false),
drawn: Cell::new(false),
}
}
/// The pipeline for a draw, built here if nothing has built it yet. The
/// first draw is noted with `recorder`.
pub(crate) fn for_draw(
&self,
recorder: &PipelineRecorder,
backend: wgpu::Backend,
create: impl FnOnce() -> wgpu::RenderPipeline,
) -> &wgpu::RenderPipeline {
if !self.drawn.replace(true) {
recorder.note_fixed(self.resource.label(), recorder.in_first_screen());
}
self.resource.for_draw(recorder, backend, create)
}
/// Queues the build on `lane` unless it is already built or queued.
pub(crate) fn queue(
&self,
compiler: &PipelineCompiler,
lane: CompileLane,
backend: wgpu::Backend,
create: impl FnOnce() -> wgpu::RenderPipeline + CompilerSend + 'static,
) {
if self.resource.get().is_none() && !self.queued.replace(true) {
self.resource.queue(compiler, lane, backend, create);
}
}
/// Builds the pipeline, which `job` makes, on `lane` ahead of its first
/// draw unless it is built, queued or in the loaded driver cache, or the
/// compiler builds nothing ahead (see [`PipelineCompiler::builds_ahead`]).
/// A pass asks for every pipeline it is about to draw with, so the
/// missing ones compile side by side. The pipeline while it is still to
/// land, for a caller that waits for it.
pub(crate) fn prefetch<J>(
&self,
lane: CompileLane,
compiler: &PipelineCompiler,
backend: wgpu::Backend,
job: impl FnOnce() -> J,
) -> Option<Pending<wgpu::RenderPipeline>>
where
J: FnOnce() -> wgpu::RenderPipeline + CompilerSend + 'static,
{
if !compiler.builds_ahead() || self.resource.get().is_some() || self.cached.get() {
return None;
}
if !self.queued.get() {
self.queue(compiler, lane, backend, job());
}
Some(self.resource.pending())
}
/// Whether the pipeline is built; when it is not, its build, which
/// `job` makes, is queued on the demand lane.
pub(crate) fn ready_or_queue<J>(
&self,
compiler: &PipelineCompiler,
backend: wgpu::Backend,
job: impl FnOnce() -> J,
) -> bool
where
J: FnOnce() -> wgpu::RenderPipeline + CompilerSend + 'static,
{
if self.resource.get().is_some() || self.cached.get() {
return true;
}
if compiler.is_active() {
self.queue(compiler, CompileLane::Demanded, backend, job());
return false;
}
true
}
/// Trusts the loaded driver cache with this pipeline.
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn trust_cached(&self) {
self.cached.set(true);
}
pub(crate) fn label(&self) -> &'static str {
self.resource.label()
}
/// Builds the pipeline here unless it is built, ahead of its first draw,
/// which is still the one noted as drawn.
pub(crate) fn build_ahead(
&self,
recorder: &PipelineRecorder,
backend: wgpu::Backend,
create: FixedPipelineJob,
) {
self.resource.for_draw(recorder, backend, create);
}
}
/// The creation of one fixed pipeline, owning clones of everything it reads
/// so the background compiler can run it as well as the frame.
#[cfg(not(target_arch = "wasm32"))]
pub(crate) type FixedPipelineJob = Box<dyn FnOnce() -> wgpu::RenderPipeline + Send + 'static>;
#[cfg(target_arch = "wasm32")]
pub(crate) type FixedPipelineJob = Box<dyn FnOnce() -> wgpu::RenderPipeline + 'static>;