1use std::borrow::Cow;
4
5use rend3::{
6 graph::{
7 DataHandle, DepthHandle, RenderGraph, RenderPassDepthTarget, RenderPassTarget, RenderPassTargets,
8 RenderTargetHandle,
9 },
10 types::{SampleCount, TextureHandle},
11 util::bind_merge::{BindGroupBuilder, BindGroupLayoutBuilder},
12 Renderer,
13};
14use wgpu::{
15 BindGroup, BindGroupLayout, BindingType, Color, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState,
16 DepthStencilState, Face, FragmentState, FrontFace, MultisampleState, PipelineLayoutDescriptor, PolygonMode,
17 PrimitiveState, PrimitiveTopology, RenderPipeline, RenderPipelineDescriptor, ShaderModuleDescriptor, ShaderSource,
18 ShaderStages, StencilState, TextureFormat, TextureSampleType, TextureViewDimension, VertexState,
19};
20
21use crate::{common::WholeFrameInterfaces, shaders::WGSL_SHADERS};
22
23struct StoredSkybox {
24 bg: Option<BindGroup>,
25 handle: Option<TextureHandle>,
26}
27
28pub struct SkyboxRoutine {
32 pipelines: SkyboxPipelines,
33 bgl: BindGroupLayout,
34 current_skybox: StoredSkybox,
35}
36
37impl SkyboxRoutine {
38 pub fn new(renderer: &Renderer, interfaces: &WholeFrameInterfaces) -> Self {
40 let bgl = BindGroupLayoutBuilder::new()
41 .append(
42 ShaderStages::FRAGMENT,
43 BindingType::Texture {
44 sample_type: TextureSampleType::Float { filterable: true },
45 view_dimension: TextureViewDimension::Cube,
46 multisampled: false,
47 },
48 None,
49 )
50 .build(&renderer.device, Some("skybox bgl"));
51
52 let pipelines = SkyboxPipelines::new(renderer, interfaces, &bgl);
53
54 Self {
55 current_skybox: StoredSkybox { bg: None, handle: None },
56 bgl,
57 pipelines,
58 }
59 }
60
61 pub fn set_background_texture(&mut self, texture: Option<TextureHandle>) {
64 self.current_skybox.handle = texture;
65 self.current_skybox.bg = None;
66 }
67
68 pub fn ready(&mut self, renderer: &Renderer) {
70 let data_core = renderer.data_core.lock();
71 let d2c_texture_manager = &data_core.d2c_texture_manager;
72
73 profiling::scope!("Update Skybox");
74
75 if let Some(ref handle) = self.current_skybox.handle {
76 if self.current_skybox.bg.is_none() {
77 let bg = BindGroupBuilder::new()
78 .append_texture_view(d2c_texture_manager.get_view(handle.get_raw()))
79 .build(&renderer.device, Some("skybox"), &self.bgl);
80
81 self.current_skybox.bg = Some(bg)
82 }
83 }
84 }
85
86 pub fn add_to_graph<'node>(
88 &'node self,
89 graph: &mut RenderGraph<'node>,
90 color: RenderTargetHandle,
91 resolve: Option<RenderTargetHandle>,
92 depth: RenderTargetHandle,
93 forward_uniform_bg: DataHandle<BindGroup>,
94 samples: SampleCount,
95 ) {
96 let mut builder = graph.add_node("Skybox");
97
98 let hdr_color_handle = builder.add_render_target_output(color);
99 let hdr_resolve = builder.add_optional_render_target_output(resolve);
100 let hdr_depth_handle = builder.add_render_target_input(depth);
101
102 let rpass_handle = builder.add_renderpass(RenderPassTargets {
103 targets: vec![RenderPassTarget {
104 color: hdr_color_handle,
105 clear: Color::BLACK,
106 resolve: hdr_resolve,
107 }],
108 depth_stencil: Some(RenderPassDepthTarget {
109 target: DepthHandle::RenderTarget(hdr_depth_handle),
110 depth_clear: Some(0.0),
111 stencil_clear: None,
112 }),
113 });
114
115 let forward_uniform_handle = builder.add_data_input(forward_uniform_bg);
116 let pt_handle = builder.passthrough_ref(self);
117
118 builder.build(move |pt, _renderer, encoder_or_pass, temps, _ready, graph_data| {
119 let this = pt.get(pt_handle);
120 let rpass = encoder_or_pass.get_rpass(rpass_handle);
121
122 let forward_uniform_bg = graph_data.get_data(temps, forward_uniform_handle).unwrap();
123
124 if let Some(ref bg) = this.current_skybox.bg {
125 let pipeline = match samples {
126 SampleCount::One => &this.pipelines.pipeline_s1,
127 SampleCount::Four => &this.pipelines.pipeline_s4,
128 };
129
130 rpass.set_pipeline(pipeline);
131 rpass.set_bind_group(0, forward_uniform_bg, &[]);
132 rpass.set_bind_group(1, bg, &[]);
133 rpass.draw(0..3, 0..1);
134 }
135 });
136 }
137}
138
139pub struct SkyboxPipelines {
141 pub pipeline_s1: RenderPipeline,
142 pub pipeline_s4: RenderPipeline,
143}
144impl SkyboxPipelines {
145 pub fn new(renderer: &Renderer, interfaces: &WholeFrameInterfaces, bgl: &BindGroupLayout) -> Self {
146 profiling::scope!("build skybox pipeline");
147 let skybox_pass_vert = renderer.device.create_shader_module(&ShaderModuleDescriptor {
148 label: Some("skybox vert"),
149 source: ShaderSource::Wgsl(Cow::Borrowed(
150 WGSL_SHADERS
151 .get_file("skybox.vert.wgsl")
152 .unwrap()
153 .contents_utf8()
154 .unwrap(),
155 )),
156 });
157 let skybox_pass_frag = renderer.device.create_shader_module(&ShaderModuleDescriptor {
158 label: Some("skybox frag"),
159 source: ShaderSource::Wgsl(Cow::Borrowed(
160 WGSL_SHADERS
161 .get_file("skybox.frag.wgsl")
162 .unwrap()
163 .contents_utf8()
164 .unwrap(),
165 )),
166 });
167
168 let pll = renderer.device.create_pipeline_layout(&PipelineLayoutDescriptor {
169 label: Some("skybox pass"),
170 bind_group_layouts: &[&interfaces.forward_uniform_bgl, bgl],
171 push_constant_ranges: &[],
172 });
173
174 let inner = |samples| {
175 renderer.device.create_render_pipeline(&RenderPipelineDescriptor {
176 label: Some("skybox pass"),
177 layout: Some(&pll),
178 vertex: VertexState {
179 module: &skybox_pass_vert,
180 entry_point: "main",
181 buffers: &[],
182 },
183 primitive: PrimitiveState {
184 topology: PrimitiveTopology::TriangleList,
185 strip_index_format: None,
186 front_face: FrontFace::Cw,
187 cull_mode: Some(Face::Back),
188 unclipped_depth: false,
189 polygon_mode: PolygonMode::Fill,
190 conservative: false,
191 },
192 depth_stencil: Some(DepthStencilState {
193 format: TextureFormat::Depth32Float,
194 depth_write_enabled: true,
195 depth_compare: CompareFunction::GreaterEqual,
196 stencil: StencilState::default(),
197 bias: DepthBiasState::default(),
198 }),
199 multisample: MultisampleState {
200 count: samples as u32,
201 ..Default::default()
202 },
203 fragment: Some(FragmentState {
204 module: &skybox_pass_frag,
205 entry_point: "main",
206 targets: &[ColorTargetState {
207 format: TextureFormat::Rgba16Float,
208 blend: None,
209 write_mask: ColorWrites::all(),
210 }],
211 }),
212 multiview: None,
213 })
214 };
215
216 Self {
217 pipeline_s1: inner(SampleCount::One),
218 pipeline_s4: inner(SampleCount::Four),
219 }
220 }
221}