1use crate::player::PPtr;
32use crate::reader::Reader;
33use crate::version::UnityVersion;
34use serde::Serialize;
35use std::collections::BTreeMap;
36
37use crate::{Result, invalid};
38
39#[derive(Clone, Debug, PartialEq, Serialize)]
45pub struct UnityUrpSettings {
46 pub renderers: Vec<PPtr>,
49 pub default_renderer: i64,
52 pub require_depth_texture: bool,
55 pub require_opaque_texture: bool,
57 pub msaa: i64,
59 pub main_light_mode: i64,
61 pub main_light_shadows: bool,
63 pub additional_lights_mode: i64,
65 pub additional_light_shadows: bool,
67 pub reflection_probe_blending: bool,
69 pub reflection_probe_box_projection: bool,
71 pub reflection_probe_atlas: bool,
73 pub shadow_distance: f32,
75 pub shadow_cascades: u8,
77 pub soft_shadows: bool,
79 pub mixed_lighting: bool,
81 pub light_cookies: bool,
83 pub light_layers: bool,
85 pub lod_cross_fade: bool,
87 pub sh_eval_mode: i64,
89 pub light_probe_system: i64,
91}
92
93const ASSET_VERSION: i32 = 13;
95
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
99#[non_exhaustive]
100pub enum UrpAssetLayout {
101 Urp17_3,
103}
104
105const URP_17_3_PLAYERS: [&str; 2] = ["6000.3.12f1", "6000.3.15f1"];
107
108impl UrpAssetLayout {
109 pub fn of(unity: UnityVersion) -> Result<Self> {
112 for v in URP_17_3_PLAYERS {
113 if UnityVersion::parse(v)? == unity {
114 return Ok(UrpAssetLayout::Urp17_3);
115 }
116 }
117 Err(invalid(format!(
118 "a Universal Render Pipeline asset of a Unity {unity} player: its layout is verified only on the players \
119 of Unity {} (the package's fields are not recorded in the asset, and other players store others)",
120 URP_17_3_PLAYERS.join(" and ")
121 )))
122 }
123}
124
125pub fn read_urp_settings(bytes: &[u8], layout: UrpAssetLayout) -> Result<UnityUrpSettings> {
129 match layout {
130 UrpAssetLayout::Urp17_3 => {}
131 }
132 let mut r = Reader::new(bytes, false);
133 let version = r.i32()?;
134 if version != ASSET_VERSION {
135 return Err(invalid(format!(
136 "Universal Render Pipeline asset version {version}: only version {ASSET_VERSION} has a verified layout"
137 )));
138 }
139 r.i32()?; r.i32()?; r.take(12)?; let renderers = pptrs(&mut r, "m_RendererDataList")?;
143 let default_renderer = r.i32()?;
144 let require_depth_texture = flag(&mut r)?;
145 let require_opaque_texture = flag(&mut r)?;
146 r.i32()?; r.take(8)?; r.i32()?; let msaa = r.i32()?;
150 r.f32()?; r.i32()?; r.take(4)?; r.f32()?; let lod_cross_fade = flag(&mut r)?;
155 r.i32()?; let sh_eval_mode = r.i32()?;
157 let light_probe_system = r.i32()?;
158 r.i32()?; r.i32()?; r.take(16)?; r.i32()?; let main_light_mode = r.i32()?;
163 let main_light_shadows = flag(&mut r)?;
164 r.i32()?; let additional_lights_mode = r.i32()?;
166 r.i32()?; let additional_light_shadows = flag(&mut r)?;
168 r.take(16)?; let reflection_probe_blending = flag(&mut r)?;
170 let reflection_probe_box_projection = flag(&mut r)?;
171 let reflection_probe_atlas = flag(&mut r)?;
172 let shadow_distance = r.f32()?;
173 let shadow_cascades = r.i32()?;
174 r.take(4 + 8 + 12 + 4 + 4 + 4)?; let soft_shadows = flag(&mut r)?;
176 r.take(4)?; r.i32()?; r.i32()?; r.i32()?; r.i32()?; r.take(8)?; let mixed_lighting = flag(&mut r)?;
183 let light_cookies = flag(&mut r)?;
184 let light_layers = flag(&mut r)?;
185 r.i32()?; r.i32()?; flag(&mut r)?; r.i32()?; r.i32()?; for _ in 0..4 {
191 flag(&mut r)?;
193 }
194 r.i32()?; r.f32()?; flag(&mut r)?; r.i32()?; flag(&mut r)?; r.take(12)?; r.i32()?; pptr(&mut r)?; strings(&mut r, "apvScenesData.obsoleteSceneBounds.m_Keys")?;
205 let n = list_len(&mut r, "apvScenesData.obsoleteSceneBounds.m_Values", 24)?;
206 for _ in 0..n {
207 r.take(24)?; }
209 strings(&mut r, "apvScenesData.obsoleteHasProbeVolumes.m_Keys")?;
210 let n = list_len(&mut r, "apvScenesData.obsoleteHasProbeVolumes.m_Values", 1)?;
211 for _ in 0..n {
212 r.bool()?;
213 }
214 r.align(4)?;
215 r.i32()?; r.i32()?; r.take(24)?; used_up(&r, "UniversalRenderPipelineAsset")?;
219 let in_range = |what: &str, ok: bool| -> Result<()> {
221 ok.then_some(()).ok_or_else(|| {
222 invalid(format!(
223 "Universal Render Pipeline asset: {what} out of range (an unverified layout)"
224 ))
225 })
226 };
227 in_range("m_ShadowCascadeCount", (1..=4).contains(&shadow_cascades))?;
228 in_range(
229 "m_ShadowDistance",
230 shadow_distance.is_finite() && shadow_distance >= 0.0,
231 )?;
232 in_range("m_MainLightRenderingMode", (0..=2).contains(&main_light_mode))?;
233 in_range(
234 "m_AdditionalLightsRenderingMode",
235 (0..=2).contains(&additional_lights_mode),
236 )?;
237 in_range("m_ShEvalMode", (0..=3).contains(&sh_eval_mode))?;
238 in_range("m_MSAA", [1, 2, 4, 8].contains(&msaa))?;
239 in_range(
240 "m_DefaultRendererIndex",
241 usize::try_from(default_renderer).is_ok_and(|i| i < renderers.len()),
242 )?;
243 Ok(UnityUrpSettings {
244 renderers,
245 default_renderer: i64::from(default_renderer),
246 require_depth_texture,
247 require_opaque_texture,
248 msaa: i64::from(msaa),
249 main_light_mode: i64::from(main_light_mode),
250 main_light_shadows,
251 additional_lights_mode: i64::from(additional_lights_mode),
252 additional_light_shadows,
253 reflection_probe_blending,
254 reflection_probe_box_projection,
255 reflection_probe_atlas,
256 shadow_distance,
257 shadow_cascades: u8::try_from(shadow_cascades).map_err(|_| {
258 invalid(format!(
259 "Universal Render Pipeline asset: m_ShadowCascadeCount {shadow_cascades} out of range"
260 ))
261 })?,
262 soft_shadows,
263 mixed_lighting,
264 light_cookies,
265 light_layers,
266 lod_cross_fade,
267 sh_eval_mode: i64::from(sh_eval_mode),
268 light_probe_system: i64::from(light_probe_system),
269 })
270}
271
272fn flag(r: &mut Reader) -> Result<bool> {
274 let b = r.bool()?;
275 r.align(4)?;
276 Ok(b)
277}
278
279fn strings(r: &mut Reader, what: &str) -> Result<Vec<String>> {
281 let n = list_len(r, what, 4)?;
282 (0..n)
283 .map(|_| {
284 let len = list_len(r, what, 1)?;
285 let s = std::str::from_utf8(r.take(len)?)
286 .map_err(|_| invalid(format!("{what}: a string that is not UTF-8")))?
287 .to_owned();
288 r.align(4)?;
289 Ok(s)
290 })
291 .collect()
292}
293
294fn pptr(r: &mut Reader) -> Result<PPtr> {
296 Ok(PPtr {
297 file_id: r.i32()?,
298 path_id: r.i64()?,
299 })
300}
301
302fn list_len(r: &mut Reader, what: &str, size: usize) -> Result<usize> {
304 let n = r.u32()?;
305 usize::try_from(n)
306 .ok()
307 .filter(|&n| n.checked_mul(size).is_some_and(|len| len <= r.remaining()))
308 .ok_or_else(|| {
309 invalid(format!(
310 "{what}: {n} entries of {size} bytes, {} bytes left",
311 r.remaining()
312 ))
313 })
314}
315
316fn pptrs(r: &mut Reader, what: &str) -> Result<Vec<PPtr>> {
318 let n = list_len(r, what, 12)?;
319 (0..n).map(|_| pptr(r)).collect()
320}
321
322fn choice(r: &mut Reader, what: &str, values: &[i32]) -> Result<i64> {
324 let v = r.i32()?;
325 if values.contains(&v) {
326 Ok(i64::from(v))
327 } else {
328 Err(invalid(format!(
329 "{what} {v} (the values: {values:?}; an unverified layout)"
330 )))
331 }
332}
333
334fn first(n: i32) -> Vec<i32> {
336 (0..n).collect()
337}
338
339fn used_up(r: &Reader, what: &str) -> Result<()> {
341 match r.remaining() {
342 0 => Ok(()),
343 n => Err(invalid(format!(
344 "{what}: {n} bytes past the verified layout's {} (another layout)",
345 r.pos
346 ))),
347 }
348}
349
350const RENDERER_ASSET_VERSION: i32 = 3;
352
353const RENDERER_TAIL: usize = 96;
358
359#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
364pub struct UnityStencilState {
365 pub override_stencil: bool,
367 pub reference: i64,
369 pub compare: i64,
371 pub pass: i64,
373 pub fail: i64,
375 pub z_fail: i64,
377}
378
379#[derive(Clone, Debug, PartialEq, Serialize)]
388pub struct UnityUrpRenderer {
389 pub features: Vec<PPtr>,
392 pub feature_map: Vec<i64>,
395 pub native_render_pass: bool,
397 pub xr_system_data: PPtr,
399 pub post_process_data: PPtr,
401 pub prepass_layer_mask: u32,
403 pub opaque_layer_mask: u32,
405 pub transparent_layer_mask: u32,
407 pub stencil: UnityStencilState,
409 pub shadow_transparent_receive: bool,
411 pub rendering_mode: i64,
413 pub depth_priming_mode: i64,
415 pub copy_depth_mode: i64,
417 pub depth_attachment_format: i64,
419 pub depth_texture_format: i64,
421 pub accurate_gbuffer_normals: bool,
423 pub intermediate_texture_mode: i64,
425}
426
427pub fn read_urp_renderer(bytes: &[u8]) -> Result<UnityUrpRenderer> {
429 const WHAT: &str = "Universal Render Pipeline renderer";
430 let mut r = Reader::new(bytes, false);
431 r.take(12 * 9)?;
433 let features = pptrs(&mut r, "m_RendererFeatures")?;
434 let n = list_len(&mut r, "m_RendererFeatureMap", 8)?;
435 let feature_map = (0..n).map(|_| r.i64()).collect::<Result<Vec<_>>>()?;
436 let native_render_pass = flag(&mut r)?;
437 if r.remaining() != RENDERER_TAIL {
438 return Err(invalid(format!(
439 "{WHAT}: {} bytes after m_UseNativeRenderPass, the verified layout stores {RENDERER_TAIL} (84 is the layout \
440 without xrSystemData, which is not verified)",
441 r.remaining()
442 )));
443 }
444 let xr_system_data = pptr(&mut r)?;
445 let post_process_data = pptr(&mut r)?;
446 let version = r.i32()?;
447 if version != RENDERER_ASSET_VERSION {
448 return Err(invalid(format!(
449 "{WHAT} asset version {version}: only version {RENDERER_ASSET_VERSION} has a verified layout"
450 )));
451 }
452 let prepass_layer_mask = r.u32()?;
453 let opaque_layer_mask = r.u32()?;
454 let transparent_layer_mask = r.u32()?;
455 let stencil = UnityStencilState {
456 override_stencil: flag(&mut r)?,
457 reference: i64::from(r.i32()?),
458 compare: choice(&mut r, "stencilCompareFunction", &first(9))?,
459 pass: choice(&mut r, "passOperation", &first(8))?,
460 fail: choice(&mut r, "failOperation", &first(8))?,
461 z_fail: choice(&mut r, "zFailOperation", &first(8))?,
462 };
463 let depth_formats = [0, 90, 91, 92, 93, 94, 151];
464 let renderer = UnityUrpRenderer {
465 features,
466 feature_map,
467 native_render_pass,
468 xr_system_data,
469 post_process_data,
470 prepass_layer_mask,
471 opaque_layer_mask,
472 transparent_layer_mask,
473 stencil,
474 shadow_transparent_receive: flag(&mut r)?,
475 rendering_mode: choice(&mut r, "m_RenderingMode", &first(4))?,
476 depth_priming_mode: choice(&mut r, "m_DepthPrimingMode", &first(3))?,
477 copy_depth_mode: choice(&mut r, "m_CopyDepthMode", &first(3))?,
478 depth_attachment_format: choice(&mut r, "m_DepthAttachmentFormat", &depth_formats)?,
479 depth_texture_format: choice(&mut r, "m_DepthTextureFormat", &depth_formats)?,
480 accurate_gbuffer_normals: flag(&mut r)?,
481 intermediate_texture_mode: choice(&mut r, "m_IntermediateTextureMode", &first(2))?,
482 };
483 used_up(&r, WHAT)?;
484 Ok(renderer)
485}
486
487pub fn read_urp_feature_active(bytes: &[u8]) -> Result<bool> {
490 flag(&mut Reader::new(bytes, false))
491}
492
493pub const URP_DECAL_FEATURE: &str = "UnityEngine.Rendering.Universal.DecalRendererFeature";
495
496pub const URP_FULL_SCREEN_FEATURE: &str = "UnityEngine.Rendering.Universal.FullScreenPassRendererFeature";
498
499#[derive(Clone, Debug, PartialEq, Serialize)]
502pub struct UnityUrpDecalFeature {
503 pub active: bool,
505 pub technique: i64,
507 pub max_draw_distance: f32,
509 pub decal_layers: bool,
511 pub dbuffer_surface_data: i64,
514 pub screen_space_normal_blend: i64,
516}
517
518pub fn read_urp_decal_feature(bytes: &[u8]) -> Result<UnityUrpDecalFeature> {
520 let mut r = Reader::new(bytes, false);
521 let feature = UnityUrpDecalFeature {
522 active: flag(&mut r)?,
523 technique: choice(&mut r, "DecalSettings.technique", &first(3))?,
524 max_draw_distance: r.f32()?,
525 decal_layers: flag(&mut r)?,
526 dbuffer_surface_data: choice(&mut r, "DBufferSettings.surfaceData", &first(3))?,
527 screen_space_normal_blend: choice(&mut r, "DecalScreenSpaceSettings.normalBlend", &first(3))?,
528 };
529 used_up(&r, "Decal renderer feature")?;
530 Ok(feature)
531}
532
533const FULL_SCREEN_VERSION: i32 = 1;
535
536#[derive(Clone, Debug, PartialEq, Serialize)]
538pub struct UnityUrpFullScreenFeature {
539 pub active: bool,
541 pub injection_point: i64,
544 pub fetch_color_buffer: bool,
546 pub requirements: i64,
549 pub pass_material: PPtr,
551 pub pass_index: i64,
553 pub bind_depth_stencil_attachment: bool,
555}
556
557pub fn read_urp_full_screen_feature(bytes: &[u8]) -> Result<UnityUrpFullScreenFeature> {
559 const WHAT: &str = "Full Screen Pass renderer feature";
560 let mut r = Reader::new(bytes, false);
561 let active = flag(&mut r)?;
562 let injection_point = choice(&mut r, "injectionPoint", &[450, 550, 600])?;
563 let fetch_color_buffer = flag(&mut r)?;
564 let requirements = i64::from(r.i32()?);
565 let pass_material = pptr(&mut r)?;
566 let pass_index = i64::from(r.i32()?);
567 let bind_depth_stencil_attachment = flag(&mut r)?;
568 let version = r.i32()?;
569 if version != FULL_SCREEN_VERSION {
570 return Err(invalid(format!(
571 "{WHAT} m_Version {version}: only version {FULL_SCREEN_VERSION} is verified"
572 )));
573 }
574 used_up(&r, WHAT)?;
575 Ok(UnityUrpFullScreenFeature {
576 active,
577 injection_point,
578 fetch_color_buffer,
579 requirements,
580 pass_material,
581 pass_index,
582 bind_depth_stencil_attachment,
583 })
584}
585
586pub const URP_CAMERA_DATA: &str = "UnityEngine.Rendering.Universal.UniversalAdditionalCameraData";
588
589const CAMERA_VERSION: i32 = 2;
591
592#[derive(Clone, Debug, PartialEq, Serialize)]
594pub struct UnityTaaSettings {
595 pub quality: i64,
597 pub frame_influence: f32,
599 pub jitter_scale: f32,
601 pub mip_bias: f32,
603 pub variance_clamp_scale: f32,
605 pub contrast_adaptive_sharpening: f32,
607}
608
609#[derive(Clone, Debug, PartialEq, Serialize)]
612pub struct UnityUrpCamera {
613 pub render_shadows: bool,
615 pub depth_texture_option: i64,
617 pub opaque_texture_option: i64,
619 pub camera_type: i64,
621 pub stack: Vec<PPtr>,
623 pub renderer_index: i64,
626 pub volume_layer_mask: u32,
628 pub volume_trigger: PPtr,
630 pub volume_update_mode: i64,
632 pub render_post_processing: bool,
634 pub antialiasing: i64,
636 pub antialiasing_quality: i64,
638 pub stop_nan: bool,
640 pub dithering: bool,
642 pub clear_depth: bool,
644 pub allow_xr_rendering: bool,
646 pub allow_hdr_output: bool,
648 pub use_screen_coord_override: bool,
650 pub screen_size_override: [f32; 4],
652 pub screen_coord_scale_bias: [f32; 4],
654 pub requires_depth_texture: bool,
656 pub requires_color_texture: bool,
658 pub taa: UnityTaaSettings,
660}
661
662pub fn read_urp_camera(bytes: &[u8]) -> Result<UnityUrpCamera> {
664 const WHAT: &str = "Universal Render Pipeline camera data";
665 let mut r = Reader::new(bytes, false);
666 let vec4 = |r: &mut Reader| -> Result<[f32; 4]> { Ok([r.f32()?, r.f32()?, r.f32()?, r.f32()?]) };
667 let camera = UnityUrpCamera {
668 render_shadows: flag(&mut r)?,
669 depth_texture_option: choice(&mut r, "m_RequiresDepthTextureOption", &first(3))?,
670 opaque_texture_option: choice(&mut r, "m_RequiresOpaqueTextureOption", &first(3))?,
671 camera_type: choice(&mut r, "m_CameraType", &first(2))?,
672 stack: pptrs(&mut r, "m_Cameras")?,
673 renderer_index: i64::from(r.i32()?),
674 volume_layer_mask: r.u32()?,
675 volume_trigger: pptr(&mut r)?,
676 volume_update_mode: choice(&mut r, "m_VolumeFrameworkUpdateModeOption", &first(3))?,
677 render_post_processing: flag(&mut r)?,
678 antialiasing: choice(&mut r, "m_Antialiasing", &first(4))?,
679 antialiasing_quality: choice(&mut r, "m_AntialiasingQuality", &first(3))?,
680 stop_nan: flag(&mut r)?,
681 dithering: flag(&mut r)?,
682 clear_depth: flag(&mut r)?,
683 allow_xr_rendering: flag(&mut r)?,
684 allow_hdr_output: flag(&mut r)?,
685 use_screen_coord_override: flag(&mut r)?,
686 screen_size_override: vec4(&mut r)?,
687 screen_coord_scale_bias: vec4(&mut r)?,
688 requires_depth_texture: flag(&mut r)?,
689 requires_color_texture: flag(&mut r)?,
690 taa: UnityTaaSettings {
691 quality: choice(&mut r, "TemporalAA.Settings.m_Quality", &first(5))?,
692 frame_influence: r.f32()?,
693 jitter_scale: r.f32()?,
694 mip_bias: r.f32()?,
695 variance_clamp_scale: r.f32()?,
696 contrast_adaptive_sharpening: r.f32()?,
697 },
698 };
699 let version = r.i32()?;
700 if version != CAMERA_VERSION {
701 return Err(invalid(format!(
702 "{WHAT} m_Version {version}: only version {CAMERA_VERSION} is verified"
703 )));
704 }
705 used_up(&r, WHAT)?;
706 Ok(camera)
707}
708
709pub const URP_GLOBAL_SETTINGS: &str = "UnityEngine.Rendering.Universal.UniversalRenderPipelineGlobalSettings";
712
713const GLOBAL_SETTINGS_VERSION: i32 = 10;
715
716const REFERENCES_VERSION: i32 = 2;
718
719#[derive(Clone, Debug, PartialEq, Serialize)]
725pub struct UnityUrpGlobalSettings {
726 pub rendering_layer_names: Vec<String>,
728 pub valid_rendering_layers: u32,
730 pub settings: Vec<UnityGraphicsSettings>,
732}
733
734#[derive(Clone, Debug, PartialEq, Serialize)]
736pub struct UnityGraphicsSettings {
737 pub rid: i64,
739 pub class: String,
741 pub assembly: String,
743 pub fields: Vec<(&'static str, UnityField)>,
745}
746
747#[derive(Clone, Debug, PartialEq, Serialize)]
749#[serde(rename_all = "snake_case")]
750pub enum UnityField {
751 Int(i64),
753 Bool(bool),
755 Ref(PPtr),
757 Refs(Vec<PPtr>),
759 Colours(Vec<[f32; 4]>),
761}
762
763#[derive(Clone, Copy, Debug)]
765enum FieldKind {
766 Version(i32),
768 Int,
769 Bool,
770 Ref,
771 Refs,
772 Colours,
773}
774
775use FieldKind as F;
776
777const SHADER_STRIPPING_FIELDS: &[(&str, FieldKind)] = &[
779 ("m_Version", F::Version(0)),
780 ("m_ExportShaderVariants", F::Bool),
781 ("m_ShaderVariantLogLevel", F::Int),
782 ("m_StripRuntimeDebugShaders", F::Bool),
783];
784
785const GRAPHICS_SETTINGS: [(&str, &[(&str, FieldKind)]); 18] = [
789 (
790 "UnityEngine.Rendering.Universal.UniversalRenderPipelineRuntimeTerrainShaders",
791 &[
792 ("m_Version", F::Version(0)),
793 ("m_TerrainDetailLit", F::Ref),
794 ("m_TerrainDetailGrassBillboard", F::Ref),
795 ("m_TerrainDetailGrass", F::Ref),
796 ],
797 ),
798 (
799 "UnityEngine.Rendering.URPReflectionProbeSettings",
800 &[
801 ("version", F::Version(1)),
802 ("useReflectionProbeRotation", F::Bool),
803 ],
804 ),
805 (
806 "UnityEngine.Rendering.VrsRenderPipelineRuntimeResources",
807 &[
808 ("m_TextureComputeShader", F::Ref),
809 ("m_VisualizationShader", F::Ref),
810 ("m_VisualizationLookupTable", F::Colours),
811 ("m_ConversionLookupTable", F::Colours),
812 ],
813 ),
814 (
815 "UnityEngine.Rendering.LightmapSamplingSettings",
816 &[
817 ("m_Version", F::Version(1)),
818 ("m_UseBicubicLightmapSampling", F::Bool),
819 ],
820 ),
821 (
822 URP_RENDER_GRAPH_SETTINGS,
823 &[
824 ("m_Version", F::Version(0)),
825 ("m_EnableRenderCompatibilityMode", F::Bool),
826 ],
827 ),
828 (
829 URP_RENDERER_RESOURCES,
830 &[
831 ("m_Version", F::Version(0)),
832 ("m_CopyDepthPS", F::Ref),
833 ("m_CameraMotionVector", F::Ref),
834 ("m_StencilDeferredPS", F::Ref),
835 ("m_ClusterDeferred", F::Ref),
836 ("m_StencilDitherMaskSeedPS", F::Ref),
837 ("m_DBufferClear", F::Ref),
838 ],
839 ),
840 (
841 "UnityEngine.Rendering.Universal.URPDefaultVolumeProfileSettings",
842 &[("m_Version", F::Version(0)), ("m_VolumeProfile", F::Ref)],
843 ),
844 (
845 "UnityEngine.Rendering.Universal.UniversalRenderPipelineRuntimeShaders",
846 &[
847 ("m_Version", F::Version(0)),
848 ("m_FallbackErrorShader", F::Ref),
849 ("m_BlitHDROverlay", F::Ref),
850 ("m_CoreBlitPS", F::Ref),
851 ("m_CoreBlitColorAndDepthPS", F::Ref),
852 ("m_SamplingPS", F::Ref),
853 ("m_TerrainDetailLit", F::Ref),
854 ("m_TerrainDetailGrassBillboard", F::Ref),
855 ("m_TerrainDetailGrass", F::Ref),
856 ],
857 ),
858 (
859 "UnityEngine.Rendering.Universal.UniversalRenderPipelineRuntimeXRResources",
860 &[
861 ("m_xrOcclusionMeshPS", F::Ref),
862 ("m_xrMirrorViewPS", F::Ref),
863 ("m_xrMotionVector", F::Ref),
864 ],
865 ),
866 (
867 "UnityEngine.Rendering.Universal.UniversalRenderPipelineDebugShaders",
868 &[
869 ("m_DebugReplacementPS", F::Ref),
870 ("m_HdrDebugViewPS", F::Ref),
871 ("m_ProbeVolumeSamplingDebugComputeShader", F::Ref),
872 ],
873 ),
874 (
875 "UnityEngine.Rendering.Universal.UniversalRenderPipelineRuntimeTextures",
876 &[
877 ("m_Version", F::Version(1)),
878 ("m_BlueNoise64LTex", F::Ref),
879 ("m_BayerMatrixTex", F::Ref),
880 ("m_DebugFontTex", F::Ref),
881 ],
882 ),
883 (
884 "UnityEngine.Rendering.RenderGraphGlobalSettings",
885 &[
886 ("m_version", F::Version(0)),
887 ("m_EnableCompilationCaching", F::Bool),
888 ("m_EnableValidityChecks", F::Bool),
889 ],
890 ),
891 (
892 "UnityEngine.Rendering.RenderGraphModule.Util.RenderGraphUtilsResources",
893 &[("m_Version", F::Version(0)), ("m_CoreCopyPS", F::Ref)],
894 ),
895 (
896 "UnityEngine.Rendering.ShaderStrippingSetting",
897 SHADER_STRIPPING_FIELDS,
898 ),
899 (
900 "UnityEngine.Rendering.GPUResidentDrawerResources",
901 &[
902 ("m_Version", F::Version(0)),
903 ("m_InstanceDataBufferCopyKernels", F::Ref),
904 ("m_InstanceDataBufferUploadKernels", F::Ref),
905 ("m_TransformUpdaterKernels", F::Ref),
906 ("m_WindDataUpdaterKernels", F::Ref),
907 ("m_OccluderDepthPyramidKernels", F::Ref),
908 ("m_InstanceOcclusionCullingKernels", F::Ref),
909 ("m_OcclusionCullingDebugKernels", F::Ref),
910 ("m_DebugOcclusionTestPS", F::Ref),
911 ("m_DebugOccluderPS", F::Ref),
912 ],
913 ),
914 (
915 "UnityEngine.Rendering.STP/RuntimeResources",
916 &[("m_setupCS", F::Ref), ("m_preTaaCS", F::Ref), ("m_taaCS", F::Ref)],
917 ),
918 (
919 "UnityEngine.Rendering.Universal.ScreenSpaceAmbientOcclusionPersistentResources",
920 &[("m_Shader", F::Ref), ("m_Version", F::Version(0))],
921 ),
922 (
923 "UnityEngine.Rendering.Universal.ScreenSpaceAmbientOcclusionDynamicResources",
924 &[("m_BlueNoise256Textures", F::Refs), ("m_Version", F::Version(0))],
925 ),
926];
927
928pub fn verified_graphics_settings() -> impl Iterator<Item = &'static str> {
930 GRAPHICS_SETTINGS.iter().map(|(c, _)| *c)
931}
932
933pub const URP_RENDER_GRAPH_SETTINGS: &str = "UnityEngine.Rendering.Universal.RenderGraphSettings";
935
936pub const URP_RENDERER_RESOURCES: &str = "UnityEngine.Rendering.Universal.UniversalRendererResources";
938
939#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
941pub struct UnityUrpRendererResources {
942 pub copy_depth: PPtr,
944 pub camera_motion_vector: PPtr,
946 pub stencil_deferred: PPtr,
948 pub cluster_deferred: PPtr,
950 pub stencil_dither_mask_seed: PPtr,
952 pub dbuffer_clear: PPtr,
954}
955
956impl UnityGraphicsSettings {
957 pub fn field(&self, name: &str) -> Option<&UnityField> {
959 self.fields.iter().find(|(n, _)| *n == name).map(|(_, v)| v)
960 }
961}
962
963impl UnityUrpGlobalSettings {
964 pub fn entry(&self, class: &str) -> Option<&UnityGraphicsSettings> {
966 self.settings.iter().find(|s| s.class == class)
967 }
968
969 pub fn renderer_resources(&self) -> Option<UnityUrpRendererResources> {
971 let e = self.entry(URP_RENDERER_RESOURCES)?;
972 let r = |name: &str| match e.field(name) {
973 Some(UnityField::Ref(p)) => Some(*p),
974 _ => None,
975 };
976 Some(UnityUrpRendererResources {
977 copy_depth: r("m_CopyDepthPS")?,
978 camera_motion_vector: r("m_CameraMotionVector")?,
979 stencil_deferred: r("m_StencilDeferredPS")?,
980 cluster_deferred: r("m_ClusterDeferred")?,
981 stencil_dither_mask_seed: r("m_StencilDitherMaskSeedPS")?,
982 dbuffer_clear: r("m_DBufferClear")?,
983 })
984 }
985
986 pub fn render_compatibility_mode(&self) -> Option<bool> {
991 match self
992 .entry(URP_RENDER_GRAPH_SETTINGS)?
993 .field("m_EnableRenderCompatibilityMode")
994 {
995 Some(UnityField::Bool(b)) => Some(*b),
996 _ => None,
997 }
998 }
999}
1000
1001fn string(r: &mut Reader, what: &str) -> Result<String> {
1003 let len = list_len(r, what, 1)?;
1004 let s = std::str::from_utf8(r.take(len)?)
1005 .map_err(|_| invalid(format!("{what}: a string that is not UTF-8")))?
1006 .to_owned();
1007 r.align(4)?;
1008 Ok(s)
1009}
1010
1011fn settings_fields(
1013 r: &mut Reader,
1014 class: &str,
1015 fields: &[(&'static str, FieldKind)],
1016) -> Result<Vec<(&'static str, UnityField)>> {
1017 fields
1018 .iter()
1019 .map(|&(name, kind)| {
1020 let what = format!("{class}.{name}");
1021 let value = match kind {
1022 FieldKind::Version(want) => {
1023 let v = r.i32()?;
1024 if v != want {
1025 return Err(invalid(format!(
1026 "{what} {v}: only version {want} has a verified layout"
1027 )));
1028 }
1029 UnityField::Int(i64::from(v))
1030 }
1031 FieldKind::Int => UnityField::Int(i64::from(r.i32()?)),
1032 FieldKind::Bool => UnityField::Bool(flag(r)?),
1033 FieldKind::Ref => UnityField::Ref(pptr(r)?),
1034 FieldKind::Refs => UnityField::Refs(pptrs(r, &what)?),
1035 FieldKind::Colours => {
1036 let n = list_len(r, &what, 16)?;
1037 UnityField::Colours(
1038 (0..n)
1039 .map(|_| Ok([r.f32()?, r.f32()?, r.f32()?, r.f32()?]))
1040 .collect::<Result<_>>()?,
1041 )
1042 }
1043 };
1044 Ok((name, value))
1045 })
1046 .collect()
1047}
1048
1049fn empty_list(r: &mut Reader, what: &str) -> Result<()> {
1051 match r.u32()? {
1052 0 => Ok(()),
1053 n => Err(invalid(format!(
1054 "{what}: {n} entries, whose layout is not verified (only empty lists are read)"
1055 ))),
1056 }
1057}
1058
1059pub fn read_urp_global_settings(bytes: &[u8]) -> Result<UnityUrpGlobalSettings> {
1063 const WHAT: &str = "Universal Render Pipeline global settings";
1064 let mut r = Reader::new(bytes, false);
1065 settings_fields(&mut r, "m_ShaderStrippingSetting", SHADER_STRIPPING_FIELDS)?;
1067 settings_fields(
1068 &mut r,
1069 "m_URPShaderStrippingSetting",
1070 &[
1071 ("m_Version", F::Version(0)),
1072 ("m_StripUnusedPostProcessingVariants", F::Bool),
1073 ("m_StripUnusedVariants", F::Bool),
1074 ("m_StripScreenCoordOverrideVariants", F::Bool),
1075 ],
1076 )?;
1077 settings_fields(
1078 &mut r,
1079 WHAT,
1080 &[
1081 ("m_ShaderVariantLogLevel", F::Int),
1082 ("m_ExportShaderVariants", F::Bool),
1083 ("m_StripDebugVariants", F::Bool),
1084 ("m_StripUnusedPostProcessingVariants", F::Bool),
1085 ("m_StripUnusedVariants", F::Bool),
1086 ("m_StripScreenCoordOverrideVariants", F::Bool),
1087 ("supportRuntimeDebugDisplay", F::Bool),
1088 ("m_EnableRenderGraph", F::Bool),
1089 ],
1090 )?;
1091 let n = list_len(&mut r, "m_Settings.m_RuntimeSettings.m_List", 8)?;
1093 let list: Vec<i64> = (0..n).map(|_| r.i64()).collect::<Result<_>>()?;
1094 let version = r.i32()?;
1095 if version != GLOBAL_SETTINGS_VERSION {
1096 return Err(invalid(format!(
1097 "{WHAT} m_AssetVersion {version}: only version {GLOBAL_SETTINGS_VERSION} has a verified layout"
1098 )));
1099 }
1100 pptr(&mut r)?; let rendering_layer_names = strings(&mut r, "m_RenderingLayerNames")?;
1102 let valid_rendering_layers = r.u32()?;
1103 for i in 0..8 {
1104 string(&mut r, &format!("lightLayerName{i}"))?;
1105 }
1106 for list in [
1108 "obsoleteSceneBounds keys",
1109 "obsoleteSceneBounds values",
1110 "obsoleteHasProbeVolumes keys",
1111 "obsoleteHasProbeVolumes values",
1112 ] {
1113 empty_list(&mut r, &format!("apvScenesData.{list}"))?;
1114 }
1115 let registry = r.i32()?;
1117 if registry != REFERENCES_VERSION {
1118 return Err(invalid(format!(
1119 "{WHAT}: managed-reference registry version {registry} (read: {REFERENCES_VERSION})"
1120 )));
1121 }
1122 let n = list_len(&mut r, "the managed-reference registry", 20)?;
1123 let mut entries: BTreeMap<i64, UnityGraphicsSettings> = BTreeMap::new();
1124 for _ in 0..n {
1125 let rid = r.i64()?;
1126 let class = string(&mut r, "a managed reference's class")?;
1127 let namespace = string(&mut r, "a managed reference's namespace")?;
1128 let assembly = string(&mut r, "a managed reference's assembly")?;
1129 let class = match namespace.as_str() {
1130 "" => class,
1131 ns => format!("{ns}.{class}"),
1132 };
1133 let (_, layout) = GRAPHICS_SETTINGS.iter().find(|(c, _)| *c == class).ok_or_else(|| {
1134 invalid(format!(
1135 "{WHAT}: a graphics settings entry of class {class} ({assembly}), whose layout is not verified"
1136 ))
1137 })?;
1138 let fields = settings_fields(&mut r, &class, layout)?;
1139 let entry = UnityGraphicsSettings {
1140 rid,
1141 class,
1142 assembly,
1143 fields,
1144 };
1145 if let Some(first) = entries.insert(rid, entry) {
1146 return Err(invalid(format!(
1147 "{WHAT}: managed reference {rid} stored twice ({})",
1148 first.class
1149 )));
1150 }
1151 }
1152 used_up(&r, WHAT)?;
1153 let settings = list
1154 .iter()
1155 .map(|rid| {
1156 entries.remove(rid).ok_or_else(|| {
1157 invalid(format!(
1158 "{WHAT}: the settings list names managed reference {rid}, which the registry does not hold"
1159 ))
1160 })
1161 })
1162 .collect::<Result<Vec<_>>>()?;
1163 if let Some(e) = entries.values().next() {
1164 return Err(invalid(format!(
1165 "{WHAT}: managed reference {} ({}) that the settings list does not name",
1166 e.rid, e.class
1167 )));
1168 }
1169 let mut classes = std::collections::BTreeSet::new();
1170 for s in &settings {
1171 if !classes.insert(s.class.as_str()) {
1172 return Err(invalid(format!("{WHAT}: the class {} listed twice", s.class)));
1173 }
1174 }
1175 Ok(UnityUrpGlobalSettings {
1176 rendering_layer_names,
1177 valid_rendering_layers,
1178 settings,
1179 })
1180}
1181
1182#[cfg(test)]
1183mod tests {
1184 use super::*;
1185
1186 fn asset(cascades: i32, default_renderer: i32) -> Vec<u8> {
1188 let mut b: Vec<u8> = Vec::new();
1189 let int = |b: &mut Vec<u8>, v: i32| b.extend(v.to_le_bytes());
1190 let float = |b: &mut Vec<u8>, v: f32| b.extend(v.to_le_bytes());
1191 let bool4 = |b: &mut Vec<u8>, v: bool| b.extend(i32::from(v).to_le_bytes());
1192 int(&mut b, 13); int(&mut b, 13);
1194 int(&mut b, 1); b.extend([0; 12]); int(&mut b, 1); int(&mut b, 0);
1198 b.extend(6168i64.to_le_bytes());
1199 int(&mut b, default_renderer);
1200 bool4(&mut b, true); bool4(&mut b, false); int(&mut b, 1);
1203 bool4(&mut b, false); bool4(&mut b, true); int(&mut b, 0);
1206 int(&mut b, 4); float(&mut b, 1.0);
1208 int(&mut b, 0); bool4(&mut b, false);
1210 float(&mut b, 0.92);
1211 bool4(&mut b, true); int(&mut b, 1);
1213 int(&mut b, 2); int(&mut b, 0); int(&mut b, 1024);
1216 int(&mut b, 256);
1217 for _ in 0..4 {
1218 bool4(&mut b, false);
1219 }
1220 int(&mut b, 1); int(&mut b, 1); bool4(&mut b, true); int(&mut b, 4096);
1224 int(&mut b, 1); int(&mut b, 8);
1226 bool4(&mut b, true); for v in [1024, 256, 512, 1024] {
1228 int(&mut b, v);
1229 }
1230 bool4(&mut b, true); bool4(&mut b, false); bool4(&mut b, true); float(&mut b, 400.0);
1234 int(&mut b, cascades);
1235 float(&mut b, 0.25);
1236 for v in [0.1, 0.3, 0.123, 0.29, 0.54, 0.11, 0.1, 0.5] {
1237 float(&mut b, v);
1238 }
1239 bool4(&mut b, true); bool4(&mut b, true); int(&mut b, 64);
1242 int(&mut b, 3);
1243 int(&mut b, 2048);
1244 int(&mut b, 3);
1245 bool4(&mut b, true);
1246 bool4(&mut b, false);
1247 bool4(&mut b, true); bool4(&mut b, false); bool4(&mut b, true); int(&mut b, 0);
1252 int(&mut b, 0);
1253 bool4(&mut b, true);
1254 int(&mut b, 0);
1255 int(&mut b, 32);
1256 for _ in 0..4 {
1257 bool4(&mut b, true);
1258 }
1259 int(&mut b, 0);
1260 float(&mut b, 0.0);
1261 bool4(&mut b, false);
1262 int(&mut b, 1);
1263 bool4(&mut b, false);
1264 for v in [256, 0, 256, 2] {
1265 int(&mut b, v);
1266 }
1267 b.extend([0; 12]); int(&mut b, 1);
1270 int(&mut b, 5);
1271 b.extend(b"scene\0\0\0");
1272 int(&mut b, 1);
1273 b.extend([0; 24]);
1274 int(&mut b, 1);
1275 int(&mut b, 5);
1276 b.extend(b"scene\0\0\0");
1277 int(&mut b, 1);
1278 b.extend([1, 0, 0, 0]);
1279 int(&mut b, 0); int(&mut b, 1); b.extend([0; 24]); b
1283 }
1284
1285 #[test]
1288 fn a_urp_assets_settings_are_read_by_the_verified_layout() {
1289 let read = |b: &[u8]| read_urp_settings(b, UrpAssetLayout::Urp17_3);
1290 let s = read(&asset(4, 0)).unwrap();
1291 let renderer = PPtr {
1292 file_id: 0,
1293 path_id: 6168,
1294 };
1295 assert_eq!((s.renderers.as_slice(), s.default_renderer), (&[renderer][..], 0));
1296 assert!(s.require_depth_texture && !s.require_opaque_texture);
1297 assert_eq!(s.msaa, 4);
1298 assert_eq!((s.shadow_cascades, s.shadow_distance), (4, 400.0));
1299 assert!(s.main_light_shadows && s.soft_shadows && s.additional_light_shadows);
1300 assert!(
1301 s.reflection_probe_blending && !s.reflection_probe_box_projection && s.reflection_probe_atlas
1302 );
1303 assert!(s.mixed_lighting && !s.light_cookies && s.light_layers && s.lod_cross_fade);
1304 assert_eq!(
1305 (s.main_light_mode, s.additional_lights_mode, s.sh_eval_mode),
1306 (1, 1, 2)
1307 );
1308 let mut other = asset(4, 0);
1310 other[0] = 12;
1311 assert!(read(&other).is_err());
1312 assert!(read(&asset(7, 0)).is_err());
1313 let past = read(&asset(4, 1)).unwrap_err().to_string();
1314 assert!(past.contains("m_DefaultRendererIndex"), "{past}");
1315 let whole = asset(4, 0);
1317 assert!(read(&whole[..whole.len() - 4]).is_err());
1318 let mut longer = whole.clone();
1319 longer.extend([0; 4]);
1320 let longer = read(&longer).unwrap_err().to_string();
1321 assert!(longer.contains("past the verified layout"), "{longer}");
1322 }
1323
1324 #[test]
1327 fn a_urp_assets_layout_is_identified_by_the_players_unity_version() {
1328 let of = |v: &str| UrpAssetLayout::of(UnityVersion::parse(v).unwrap());
1329 assert_eq!(of("6000.3.12f1").unwrap(), UrpAssetLayout::Urp17_3);
1330 assert_eq!(of("6000.3.15f1").unwrap(), UrpAssetLayout::Urp17_3);
1331 let refused = of("6000.3.17f1").unwrap_err().to_string();
1332 assert!(refused.contains("Unity 6000.3.17f1 player"), "{refused}");
1333 assert!(of("6000.6.4f1").is_err());
1334 }
1335
1336 fn int(b: &mut Vec<u8>, v: i32) {
1337 b.extend(v.to_le_bytes());
1338 }
1339
1340 fn reference(b: &mut Vec<u8>, path_id: i64) {
1341 int(b, 0);
1342 b.extend(path_id.to_le_bytes());
1343 }
1344
1345 fn renderer(version: i32, mode: i32) -> Vec<u8> {
1347 let mut b = vec![0; 12 * 9]; int(&mut b, 2);
1349 reference(&mut b, 6001);
1350 reference(&mut b, 6002);
1351 int(&mut b, 2);
1352 b.extend(7i64.to_le_bytes());
1353 b.extend((-8i64).to_le_bytes());
1354 int(&mut b, 1); reference(&mut b, 0); reference(&mut b, 6170); int(&mut b, version);
1358 for mask in [0x8a30_9437u32, 0x8a31_d437, u32::MAX] {
1359 b.extend(mask.to_le_bytes());
1360 }
1361 for v in [1, 3, 8, 2, 0, 0] {
1362 int(&mut b, v); }
1364 int(&mut b, 1); int(&mut b, mode);
1366 int(&mut b, 0); int(&mut b, 1); int(&mut b, 92);
1369 int(&mut b, 0);
1370 int(&mut b, 0);
1371 int(&mut b, 1); b
1373 }
1374
1375 #[test]
1379 fn a_urp_renderer_is_read_by_the_verified_layout() {
1380 let r = read_urp_renderer(&renderer(3, 2)).unwrap();
1381 let ids: Vec<i64> = r.features.iter().map(|f| f.path_id).collect();
1382 assert_eq!((ids, r.feature_map.clone()), (vec![6001, 6002], vec![7, -8]));
1383 assert!(r.native_render_pass && r.shadow_transparent_receive && !r.accurate_gbuffer_normals);
1384 assert_eq!((r.xr_system_data.path_id, r.post_process_data.path_id), (0, 6170));
1385 assert_eq!(
1386 (
1387 r.prepass_layer_mask,
1388 r.opaque_layer_mask,
1389 r.transparent_layer_mask
1390 ),
1391 (0x8a30_9437, 0x8a31_d437, u32::MAX)
1392 );
1393 assert_eq!(
1394 r.stencil,
1395 UnityStencilState {
1396 override_stencil: true,
1397 reference: 3,
1398 compare: 8,
1399 pass: 2,
1400 fail: 0,
1401 z_fail: 0
1402 }
1403 );
1404 assert_eq!(
1405 (r.rendering_mode, r.depth_priming_mode, r.copy_depth_mode),
1406 (2, 0, 1)
1407 );
1408 assert_eq!(
1409 (
1410 r.depth_attachment_format,
1411 r.depth_texture_format,
1412 r.intermediate_texture_mode
1413 ),
1414 (92, 0, 1)
1415 );
1416 let refusal = |b: &[u8]| read_urp_renderer(b).unwrap_err().to_string();
1417 assert!(refusal(&renderer(2, 2)).contains("asset version 2"));
1418 assert!(refusal(&renderer(3, 4)).contains("m_RenderingMode 4"));
1419 let mut longer = renderer(3, 2);
1420 longer.extend([0; 4]);
1421 assert!(refusal(&longer).contains("100 bytes after m_UseNativeRenderPass"));
1422 let mut no_xr = renderer(3, 2);
1424 no_xr.drain(160..172);
1425 assert!(refusal(&no_xr).contains("84 bytes after"), "{}", refusal(&no_xr));
1426 let mut map = renderer(3, 2);
1428 map[136] = 1; map.drain(148..156);
1430 let short = read_urp_renderer(&map).unwrap();
1431 assert_eq!((short.features.len(), short.feature_map), (2, vec![7]));
1432 assert_eq!(short.post_process_data.path_id, 6170);
1433 assert!(read_urp_renderer(&renderer(3, 2)[..150]).is_err());
1434 }
1435
1436 #[test]
1439 fn urp_features_are_read_by_the_verified_layout() {
1440 assert!(read_urp_feature_active(&[1, 0, 0, 0, 9, 9]).unwrap());
1441 assert!(!read_urp_feature_active(&[0, 0, 0, 0]).unwrap());
1442 assert!(read_urp_feature_active(&[2, 0, 0, 0]).is_err());
1443 let decal = |technique: i32| {
1444 let mut b = Vec::new();
1445 int(&mut b, 1);
1446 int(&mut b, technique);
1447 b.extend(500f32.to_le_bytes());
1448 int(&mut b, 0);
1449 int(&mut b, 2);
1450 int(&mut b, 0);
1451 b
1452 };
1453 assert_eq!(
1454 read_urp_decal_feature(&decal(1)).unwrap(),
1455 UnityUrpDecalFeature {
1456 active: true,
1457 technique: 1,
1458 max_draw_distance: 500.0,
1459 decal_layers: false,
1460 dbuffer_surface_data: 2,
1461 screen_space_normal_blend: 0
1462 }
1463 );
1464 assert!(read_urp_decal_feature(&decal(3)).is_err());
1465 let mut longer = decal(1);
1466 longer.extend([0; 4]);
1467 assert!(read_urp_decal_feature(&longer).is_err());
1468 let full_screen = |injection: i32, version: i32| {
1469 let mut b = Vec::new();
1470 int(&mut b, 1);
1471 int(&mut b, injection);
1472 int(&mut b, 1);
1473 int(&mut b, -1); reference(&mut b, 7);
1475 int(&mut b, 0);
1476 int(&mut b, 0);
1477 int(&mut b, version);
1478 b
1479 };
1480 let f = read_urp_full_screen_feature(&full_screen(600, 1)).unwrap();
1481 assert!(f.active && f.fetch_color_buffer && !f.bind_depth_stencil_attachment);
1482 assert_eq!(
1483 (
1484 f.injection_point,
1485 f.requirements,
1486 f.pass_material.path_id,
1487 f.pass_index
1488 ),
1489 (600, -1, 7, 0)
1490 );
1491 let refusal = |b: &[u8]| read_urp_full_screen_feature(b).unwrap_err().to_string();
1492 assert!(refusal(&full_screen(300, 1)).contains("injectionPoint 300"));
1493 assert!(refusal(&full_screen(600, 0)).contains("m_Version 0"));
1494 }
1495
1496 fn camera(stack: i32, version: i32) -> Vec<u8> {
1498 let mut b = Vec::new();
1499 int(&mut b, 1); int(&mut b, 2);
1501 int(&mut b, 2);
1502 int(&mut b, 0); int(&mut b, stack);
1504 for i in 0..stack {
1505 reference(&mut b, i64::from(i) + 100);
1506 }
1507 int(&mut b, -1); b.extend(0x8000_0000u32.to_le_bytes());
1509 reference(&mut b, 0);
1510 int(&mut b, 2);
1511 int(&mut b, 1); int(&mut b, 3); int(&mut b, 2);
1514 for v in [0, 1, 1, 1, 1, 0] {
1515 int(&mut b, v);
1516 }
1517 for v in [0f32, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0] {
1518 b.extend(v.to_le_bytes());
1519 }
1520 int(&mut b, 0);
1521 int(&mut b, 0);
1522 int(&mut b, 3); for v in [0.1f32, 1.0, 0.0, 0.9, 0.0] {
1524 b.extend(v.to_le_bytes());
1525 }
1526 int(&mut b, version);
1527 b
1528 }
1529
1530 #[test]
1533 fn a_urp_camera_is_read_by_the_verified_layout() {
1534 assert_eq!(camera(0, 2).len(), 148);
1535 let c = read_urp_camera(&camera(2, 2)).unwrap();
1536 let stack: Vec<i64> = c.stack.iter().map(|p| p.path_id).collect();
1537 assert_eq!(stack, vec![100, 101]);
1538 assert_eq!(
1539 (
1540 c.renderer_index,
1541 c.depth_texture_option,
1542 c.antialiasing,
1543 c.taa.quality
1544 ),
1545 (-1, 2, 3, 3)
1546 );
1547 assert!(c.render_shadows && c.render_post_processing && c.clear_depth && !c.stop_nan && c.dithering);
1548 assert_eq!(
1549 (c.volume_layer_mask, c.screen_coord_scale_bias),
1550 (0x8000_0000, [1.0, 1.0, 0.0, 0.0])
1551 );
1552 assert_eq!(c.taa.variance_clamp_scale, 0.9);
1553 assert!(
1554 read_urp_camera(&camera(0, 1))
1555 .unwrap_err()
1556 .to_string()
1557 .contains("m_Version 1")
1558 );
1559 let mut longer = camera(0, 2);
1560 longer.extend([0; 4]);
1561 assert!(read_urp_camera(&longer).is_err());
1562 }
1563}