1use crate::{
2 format_sso,
3 graph::{GraphTextureStore, ReadyData},
4 instruction::{InstructionKind, InstructionStreamPair},
5 managers::{
6 CameraManager, DirectionalLightManager, InternalTexture, MaterialManager, MeshManager, ObjectManager,
7 SkeletonManager, TextureManager,
8 },
9 types::{
10 Camera, DirectionalLight, DirectionalLightChange, DirectionalLightHandle, MaterialHandle, Mesh, MeshHandle,
11 Object, ObjectHandle, Texture, TextureHandle,
12 },
13 util::mipmap::MipmapGenerator,
14 ExtendedAdapterInfo, InstanceAdapterDevice, RendererInitializationError, RendererProfile,
15};
16use glam::Mat4;
17use parking_lot::Mutex;
18use rend3_types::{
19 Handedness, Material, MipmapCount, MipmapSource, ObjectChange, Skeleton, SkeletonHandle, TextureFormat,
20 TextureFromTexture, TextureUsages,
21};
22use std::{
23 num::NonZeroU32,
24 panic::Location,
25 sync::{atomic::AtomicUsize, Arc},
26};
27use wgpu::{
28 util::DeviceExt, CommandBuffer, CommandEncoderDescriptor, Device, DownlevelCapabilities, Extent3d, Features,
29 ImageCopyTexture, ImageDataLayout, Limits, Origin3d, Queue, TextureAspect, TextureDescriptor, TextureDimension,
30 TextureSampleType, TextureViewDescriptor, TextureViewDimension,
31};
32use wgpu_profiler::GpuProfiler;
33
34pub mod error;
35mod ready;
36mod setup;
37
38pub struct Renderer {
41 instructions: InstructionStreamPair,
42
43 pub profile: RendererProfile,
45 pub adapter_info: ExtendedAdapterInfo,
47 pub queue: Arc<Queue>,
49 pub device: Arc<Device>,
51
52 pub features: Features,
54 pub limits: Limits,
56 pub downlevel: DownlevelCapabilities,
58 pub handedness: Handedness,
60
61 current_ident: AtomicUsize,
63 pub data_core: Mutex<RendererDataCore>,
65
66 pub mipmap_generator: MipmapGenerator,
68}
69
70pub struct RendererDataCore {
72 pub camera_manager: CameraManager,
74 pub mesh_manager: MeshManager,
76 pub d2_texture_manager: TextureManager,
78 pub d2c_texture_manager: TextureManager,
80 pub material_manager: MaterialManager,
82 pub object_manager: ObjectManager,
84 pub directional_light_manager: DirectionalLightManager,
86 pub skeleton_manager: SkeletonManager,
88
89 pub profiler: GpuProfiler,
91
92 pub(crate) graph_texture_store: GraphTextureStore,
94}
95
96impl Renderer {
97 pub fn new(
104 iad: InstanceAdapterDevice,
105 handedness: Handedness,
106 aspect_ratio: Option<f32>,
107 ) -> Result<Arc<Self>, RendererInitializationError> {
108 setup::create_renderer(iad, handedness, aspect_ratio)
109 }
110
111 #[track_caller]
118 pub fn add_mesh(&self, mesh: Mesh) -> MeshHandle {
119 let handle = MeshManager::allocate(&self.current_ident);
120
121 self.instructions.push(
122 InstructionKind::AddMesh {
123 handle: handle.clone(),
124 mesh,
125 },
126 *Location::caller(),
127 );
128
129 handle
130 }
131
132 #[track_caller]
139 pub fn add_skeleton(&self, skeleton: Skeleton) -> SkeletonHandle {
140 let handle = SkeletonManager::allocate(&self.current_ident);
141 self.instructions.push(
142 InstructionKind::AddSkeleton {
143 handle: handle.clone(),
144 skeleton,
145 },
146 *Location::caller(),
147 );
148 handle
149 }
150
151 #[track_caller]
156 pub fn add_texture_2d(&self, texture: Texture) -> TextureHandle {
157 profiling::scope!("Add Texture 2D");
158
159 Self::validation_texture_format(texture.format);
160
161 let handle = TextureManager::allocate(&self.current_ident);
162 let size = Extent3d {
163 width: texture.size.x,
164 height: texture.size.y,
165 depth_or_array_layers: 1,
166 };
167
168 let mip_level_count = match texture.mip_count {
169 MipmapCount::Specific(v) => v.get(),
170 MipmapCount::Maximum => size.max_mips(),
171 };
172
173 let desc = TextureDescriptor {
174 label: None,
175 size,
176 mip_level_count,
177 sample_count: 1,
178 dimension: TextureDimension::D2,
179 format: texture.format,
180 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_SRC | TextureUsages::COPY_DST,
181 };
182
183 let (buffer, tex) = match texture.mip_source {
184 MipmapSource::Uploaded => (
185 None,
186 self.device.create_texture_with_data(&self.queue, &desc, &texture.data),
187 ),
188 MipmapSource::Generated => {
189 let desc = TextureDescriptor {
190 usage: desc.usage | TextureUsages::RENDER_ATTACHMENT,
191 ..desc
192 };
193 let tex = self.device.create_texture(&desc);
194
195 let format_desc = texture.format.describe();
196
197 self.queue.write_texture(
199 ImageCopyTexture {
200 texture: &tex,
201 mip_level: 0,
202 origin: Origin3d::ZERO,
203 aspect: TextureAspect::All,
204 },
205 &texture.data,
206 ImageDataLayout {
207 offset: 0,
208 bytes_per_row: NonZeroU32::new(
209 format_desc.block_size as u32 * (size.width / format_desc.block_dimensions.0 as u32),
210 ),
211 rows_per_image: None,
212 },
213 size,
214 );
215
216 let mut encoder = self.device.create_command_encoder(&CommandEncoderDescriptor::default());
217
218 self.mipmap_generator
220 .generate_mipmaps(&self.device, &mut encoder, &tex, &desc);
221
222 (Some(encoder.finish()), tex)
223 }
224 };
225
226 let view = tex.create_view(&TextureViewDescriptor::default());
227 self.instructions.push(
228 InstructionKind::AddTexture {
229 handle: handle.clone(),
230 desc,
231 texture: tex,
232 view,
233 buffer,
234 cube: false,
235 },
236 *Location::caller(),
237 );
238 handle
239 }
240
241 #[track_caller]
247 pub fn add_texture_2d_from_texture(&self, texture: TextureFromTexture) -> TextureHandle {
248 profiling::scope!("Add Texture 2D From Texture");
249
250 let mut encoder = self.device.create_command_encoder(&CommandEncoderDescriptor::default());
251
252 let handle = TextureManager::allocate(&self.current_ident);
253
254 let data_core = self.data_core.lock();
255
256 let InternalTexture {
257 texture: old_texture,
258 desc: old_texture_desc,
259 } = data_core.d2_texture_manager.get_internal(texture.src.get_raw());
260
261 let new_size = old_texture_desc.mip_level_size(texture.start_mip).unwrap();
262
263 let mip_level_count = texture
264 .mip_count
265 .map_or_else(|| old_texture_desc.mip_level_count - texture.start_mip, |c| c.get());
266
267 let desc = TextureDescriptor {
268 size: new_size,
269 mip_level_count,
270 ..old_texture_desc.clone()
271 };
272
273 let tex = self.device.create_texture(&desc);
274
275 let view = tex.create_view(&TextureViewDescriptor::default());
276
277 for new_mip in 0..mip_level_count {
278 let old_mip = new_mip + texture.start_mip;
279
280 let _label = format_sso!("mip {} to {}", old_mip, new_mip);
281 profiling::scope!(&_label);
282
283 encoder.copy_texture_to_texture(
284 ImageCopyTexture {
285 texture: old_texture,
286 mip_level: old_mip,
287 origin: Origin3d::ZERO,
288 aspect: TextureAspect::All,
289 },
290 ImageCopyTexture {
291 texture: &tex,
292 mip_level: new_mip,
293 origin: Origin3d::ZERO,
294 aspect: TextureAspect::All,
295 },
296 old_texture_desc.mip_level_size(old_mip).unwrap(),
297 );
298 }
299 self.instructions.push(
300 InstructionKind::AddTexture {
301 handle: handle.clone(),
302 texture: tex,
303 desc,
304 view,
305 buffer: Some(encoder.finish()),
306 cube: false,
307 },
308 *Location::caller(),
309 );
310 handle
311 }
312
313 #[track_caller]
318 pub fn add_texture_cube(&self, texture: Texture) -> TextureHandle {
319 profiling::scope!("Add Texture Cube");
320
321 Self::validation_texture_format(texture.format);
322
323 let handle = TextureManager::allocate(&self.current_ident);
324 let size = Extent3d {
325 width: texture.size.x,
326 height: texture.size.y,
327 depth_or_array_layers: 6,
328 };
329
330 let mip_level_count = match texture.mip_count {
331 MipmapCount::Specific(v) => v.get(),
332 MipmapCount::Maximum => size.max_mips(),
333 };
334
335 let desc = TextureDescriptor {
336 label: None,
337 size,
338 mip_level_count,
339 sample_count: 1,
340 dimension: TextureDimension::D2,
341 format: texture.format,
342 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
343 };
344
345 let tex = self.device.create_texture_with_data(&self.queue, &desc, &texture.data);
346
347 let view = tex.create_view(&TextureViewDescriptor {
348 dimension: Some(TextureViewDimension::Cube),
349 ..TextureViewDescriptor::default()
350 });
351 self.instructions.push(
352 InstructionKind::AddTexture {
353 handle: handle.clone(),
354 texture: tex,
355 desc,
356 view,
357 buffer: None,
358 cube: true,
359 },
360 *Location::caller(),
361 );
362 handle
363 }
364
365 fn validation_texture_format(format: TextureFormat) {
366 let sample_type = format.describe().sample_type;
367 if let TextureSampleType::Float { filterable } = sample_type {
368 if !filterable {
369 panic!(
370 "Textures formats must allow filtering with a linear filter. {:?} has sample type {:?} which does not.",
371 format, sample_type
372 )
373 }
374 } else {
375 panic!(
376 "Textures formats must be sample-able as floating point. {:?} has sample type {:?}.",
377 format, sample_type
378 )
379 }
380 }
381
382 #[track_caller]
389 pub fn add_material<M: Material>(&self, material: M) -> MaterialHandle {
390 let handle = MaterialManager::allocate(&self.current_ident);
391 self.instructions.push(
392 InstructionKind::AddMaterial {
393 handle: handle.clone(),
394 fill_invoke: Box::new(move |material_manager, device, profile, d2_manager, mat_handle| {
395 material_manager.fill(device, profile, d2_manager, mat_handle, material)
396 }),
397 },
398 *Location::caller(),
399 );
400 handle
401 }
402
403 #[track_caller]
405 pub fn update_material<M: Material>(&self, handle: &MaterialHandle, material: M) {
406 self.instructions.push(
407 InstructionKind::ChangeMaterial {
408 handle: handle.clone(),
409 change_invoke: Box::new(
410 move |material_manager, device, profile, d2_manager, object_manager, mat_handle| {
411 material_manager.update(device, profile, d2_manager, object_manager, mat_handle, material)
412 },
413 ),
414 },
415 *Location::caller(),
416 )
417 }
418
419 #[track_caller]
426 pub fn add_object(&self, object: Object) -> ObjectHandle {
427 let handle = ObjectManager::allocate(&self.current_ident);
428 self.instructions.push(
429 InstructionKind::AddObject {
430 handle: handle.clone(),
431 object,
432 },
433 *Location::caller(),
434 );
435 handle
436 }
437
438 #[track_caller]
443 pub fn duplicate_object(&self, object_handle: &ObjectHandle, change: ObjectChange) -> ObjectHandle {
444 let dst_handle = ObjectManager::allocate(&self.current_ident);
445 self.instructions.push(
446 InstructionKind::DuplicateObject {
447 src_handle: object_handle.clone(),
448 dst_handle: dst_handle.clone(),
449 change,
450 },
451 *Location::caller(),
452 );
453 dst_handle
454 }
455
456 #[track_caller]
458 pub fn set_object_transform(&self, handle: &ObjectHandle, transform: Mat4) {
459 self.instructions.push(
460 InstructionKind::SetObjectTransform {
461 handle: handle.get_raw(),
462 transform,
463 },
464 *Location::caller(),
465 );
466 }
467
468 #[track_caller]
480 pub fn set_skeleton_joint_transforms(
481 &self,
482 handle: &SkeletonHandle,
483 joint_global_transforms: &[Mat4],
484 inverse_bind_transforms: &[Mat4],
485 ) {
486 self.set_skeleton_joint_matrices(
487 handle,
488 Skeleton::compute_joint_matrices(joint_global_transforms, inverse_bind_transforms),
489 );
490 }
491
492 #[track_caller]
498 pub fn set_skeleton_joint_matrices(&self, handle: &SkeletonHandle, joint_matrices: Vec<Mat4>) {
499 self.instructions.push(
500 InstructionKind::SetSkeletonJointDeltas {
501 handle: handle.get_raw(),
502 joint_matrices,
503 },
504 *Location::caller(),
505 )
506 }
507
508 #[track_caller]
512 pub fn add_directional_light(&self, light: DirectionalLight) -> DirectionalLightHandle {
513 let handle = DirectionalLightManager::allocate(&self.current_ident);
514
515 self.instructions.push(
516 InstructionKind::AddDirectionalLight {
517 handle: handle.clone(),
518 light,
519 },
520 *Location::caller(),
521 );
522
523 handle
524 }
525
526 #[track_caller]
528 pub fn update_directional_light(&self, handle: &DirectionalLightHandle, change: DirectionalLightChange) {
529 self.instructions.push(
530 InstructionKind::ChangeDirectionalLight {
531 handle: handle.get_raw(),
532 change,
533 },
534 *Location::caller(),
535 )
536 }
537
538 #[track_caller]
541 pub fn set_aspect_ratio(&self, ratio: f32) {
542 self.instructions
543 .push(InstructionKind::SetAspectRatio { ratio }, *Location::caller())
544 }
545
546 #[track_caller]
548 pub fn set_camera_data(&self, data: Camera) {
549 self.instructions
550 .push(InstructionKind::SetCameraData { data }, *Location::caller())
551 }
552
553 pub fn ready(&self) -> (Vec<CommandBuffer>, ReadyData) {
559 ready::ready(self)
560 }
561}