Render live preview shows depth as written (no translation)
@@ -51,7 +51,7 @@ Vanuit **Body** of de bodyweergave opent **2D-preview** de gerenderde compositor
51
51
- Kies **Man / Vrouw**. Alleen exportsets met dezelfde project-/rig-/exportcontract (`CompatKey`) zijn combineerbaar; equipment verschijnt als multiselect wanneer die contracten overeenkomen.
52
52
- Kies een clip uit de body-exportinventaris. **Speel / Pauze**, snelheid en export-fps sturen een looping frame-tijd (elapsed × fps); ontbrekende maps/frames tonen een diagnose in plaats van een stille vervanging.
53
53
- **Yaw − / Yaw +** wrappen over 0…330° (stap 30). **Pitch − / Pitch +** kiezen −60 / −30 / 0 / +30 / +60 met klemmen (start op 0). Hoekwijzigingen behouden de animatietijd.
54
- Kies weergavestand **Resultaat** (standaard; belichte compositie), **Alleen diffuse**, **Alleen normal map**, **Alleen depth map** of **Alleen specular map**. Mapstanden tonen per pixel de winnende laag van de dieptecompositie, onbelicht en precies zoals in de atlas opgeslagen, zonder decodering: diffuse, normal en specular als hun RGBA, depth als zijn opgeslagen kanalen (`gray` als grijs, `rgb_bands` en `rgb_packed` als ruwe RGB). 16-bit depth wordt op gewone weergaveprecisie (8 bit per kanaal, afgerond) getoond. Alleen Resultaat decodeert de diepte voor compositie en belichting. Playback, hoek en equipment werken in elke stand; licht alleen in Resultaat.
54
- Kies weergavestand **Resultaat** (standaard; belichte compositie), **Alleen diffuse**, **Alleen normal map**, **Alleen depth map** of **Alleen specular map**. Mapstanden tonen per pixel de winnende laag van de dieptecompositie, onbelicht en precies zoals in de atlas opgeslagen, zonder decodering: diffuse, normal en specular als hun RGBA, depth als zijn opgeslagen kanalen (`gray` als grijs, `rgb_bands` en `rgb_packed` als ruwe RGB). 16-bit depth wordt op gewone weergaveprecisie (8 bit per kanaal, afgerond) getoond. Alleen Resultaat decodeert de diepte voor compositie en belichting. Ook het live passbeeld tijdens het renderen toont de depth-map precies zoals hij geschreven wordt (zelfde weergaveregel). Playback, hoek en equipment werken in elke stand; licht alleen in Resultaat.
55
55
- Houd **Azimut / Elevatie** ingedrukt voor continue lichtrotatie in °/s (delta-time); azimut wrapt over 360° zonder klem, elevatie blijft −80…+80. De knoppen volgen de echte muisknopstatus, dus ze blijven draaien zolang je vasthoudt (ook langer dan een seconde). Loslaten, van de knop af bewegen, focusverlies of de pointer die het venster verlaat stopt de beweging. Belichting is alleen voor Resultaat; opgeslagen diffuse blijft unlit albedo.
56
56
- Playback loopt over exact `frames / fps` seconden, zodat ieder geëxporteerd frame (inclusief het laatste) even lang duurt (`1/fps`).
57
57
- Het samengestelde frame gebruikt nearest-neighbor weergave met uniforme schaal en gecentreerde letterboxing, gelijk aan de productie-passpreviews.
@@ -51,7 +51,7 @@ Vanuit **Body** of de bodyweergave opent **2D-preview** de gerenderde compositor51
- Kies **Man / Vrouw**. Alleen exportsets met dezelfde project-/rig-/exportcontract (`CompatKey`) zijn combineerbaar; equipment verschijnt als multiselect wanneer die contracten overeenkomen.51
- Kies **Man / Vrouw**. Alleen exportsets met dezelfde project-/rig-/exportcontract (`CompatKey`) zijn combineerbaar; equipment verschijnt als multiselect wanneer die contracten overeenkomen.52
- Kies een clip uit de body-exportinventaris. **Speel / Pauze**, snelheid en export-fps sturen een looping frame-tijd (elapsed × fps); ontbrekende maps/frames tonen een diagnose in plaats van een stille vervanging.52
- Kies een clip uit de body-exportinventaris. **Speel / Pauze**, snelheid en export-fps sturen een looping frame-tijd (elapsed × fps); ontbrekende maps/frames tonen een diagnose in plaats van een stille vervanging.53
- **Yaw − / Yaw +** wrappen over 0…330° (stap 30). **Pitch − / Pitch +** kiezen −60 / −30 / 0 / +30 / +60 met klemmen (start op 0). Hoekwijzigingen behouden de animatietijd.53
- **Yaw − / Yaw +** wrappen over 0…330° (stap 30). **Pitch − / Pitch +** kiezen −60 / −30 / 0 / +30 / +60 met klemmen (start op 0). Hoekwijzigingen behouden de animatietijd.54
- Kies weergavestand **Resultaat** (standaard; belichte compositie), **Alleen diffuse**, **Alleen normal map**, **Alleen depth map** of **Alleen specular map**. Mapstanden tonen per pixel de winnende laag van de dieptecompositie, onbelicht en precies zoals in de atlas opgeslagen, zonder decodering: diffuse, normal en specular als hun RGBA, depth als zijn opgeslagen kanalen (`gray` als grijs, `rgb_bands` en `rgb_packed` als ruwe RGB). 16-bit depth wordt op gewone weergaveprecisie (8 bit per kanaal, afgerond) getoond. Alleen Resultaat decodeert de diepte voor compositie en belichting. Playback, hoek en equipment werken in elke stand; licht alleen in Resultaat.54
- Kies weergavestand **Resultaat** (standaard; belichte compositie), **Alleen diffuse**, **Alleen normal map**, **Alleen depth map** of **Alleen specular map**. Mapstanden tonen per pixel de winnende laag van de dieptecompositie, onbelicht en precies zoals in de atlas opgeslagen, zonder decodering: diffuse, normal en specular als hun RGBA, depth als zijn opgeslagen kanalen (`gray` als grijs, `rgb_bands` en `rgb_packed` als ruwe RGB). 16-bit depth wordt op gewone weergaveprecisie (8 bit per kanaal, afgerond) getoond. Alleen Resultaat decodeert de diepte voor compositie en belichting. Ook het live passbeeld tijdens het renderen toont de depth-map precies zoals hij geschreven wordt (zelfde weergaveregel). Playback, hoek en equipment werken in elke stand; licht alleen in Resultaat.55
- Houd **Azimut / Elevatie** ingedrukt voor continue lichtrotatie in °/s (delta-time); azimut wrapt over 360° zonder klem, elevatie blijft −80…+80. De knoppen volgen de echte muisknopstatus, dus ze blijven draaien zolang je vasthoudt (ook langer dan een seconde). Loslaten, van de knop af bewegen, focusverlies of de pointer die het venster verlaat stopt de beweging. Belichting is alleen voor Resultaat; opgeslagen diffuse blijft unlit albedo.55
- Houd **Azimut / Elevatie** ingedrukt voor continue lichtrotatie in °/s (delta-time); azimut wrapt over 360° zonder klem, elevatie blijft −80…+80. De knoppen volgen de echte muisknopstatus, dus ze blijven draaien zolang je vasthoudt (ook langer dan een seconde). Loslaten, van de knop af bewegen, focusverlies of de pointer die het venster verlaat stopt de beweging. Belichting is alleen voor Resultaat; opgeslagen diffuse blijft unlit albedo.56
- Playback loopt over exact `frames / fps` seconden, zodat ieder geëxporteerd frame (inclusief het laatste) even lang duurt (`1/fps`).56
- Playback loopt over exact `frames / fps` seconden, zodat ieder geëxporteerd frame (inclusief het laatste) even lang duurt (`1/fps`).57
- Het samengestelde frame gebruikt nearest-neighbor weergave met uniforme schaal en gecentreerde letterboxing, gelijk aan de productie-passpreviews.57
- Het samengestelde frame gebruikt nearest-neighbor weergave met uniforme schaal en gecentreerde letterboxing, gelijk aan de productie-passpreviews.@@ -66,7 +66,6 @@ pub struct Target {
66
66
pub depth: Vec<u16>,
67
67
/// Unquantized normalized depth t (0..1) per pixel, for the export encoding.
68
68
pub depth_t: Vec<f32>,
69
pub depth_preview: ImageBuf,
70
69
}
71
70
/// Content fingerprint of a source file used for a production job.
72
71
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -851,6 +850,24 @@ vec3 encode_srgb(vec3 x){return mix(x*12.92,1.055*pow(max(x,vec3(0.0)),vec3(1.0/
851
850
void main(){vec3 n=normalize(v_normal);if(u_flat){n=normalize(cross(dFdx(v_view_position),dFdy(v_view_position)));if(!gl_FrontFacing)n=-n;}vec4 c=u_color*v_color;if(u_albedo_enabled)c*=texture(u_albedo,v_uv);if(c.a<u_cutoff)discard;float s=u_specular;if(u_shiny_enabled)s*=dot(texture(u_shiny,v_uv).rgb,vec3(0.2126,0.7152,0.0722));diffuse=vec4(encode_srgb(clamp(c.rgb,0.0,1.0)),c.a);normal=vec4(n*0.5+0.5,c.a);specular=vec4(vec3(clamp(s,0.0,1.0)),c.a);}
852
851
"#;
853
852
853
/// Live-preview image of a depth PNG exactly as written (Tim t1005u): the
854
/// stored channels in display form, no depth decoding.
855
pub fn stored_depth_preview(
856
png_bytes: &[u8],
857
w: u32,
858
h: u32,
859
fmt: crate::camera::DepthFormat,
860
) -> Result<ImageBuf, String> {
861
let native =
862
crate::export_set::decode_depth_native(png_bytes, w, h, crate::export_set::MAX_TARGET, fmt)
863
.map_err(|e| e.to_string())?;
864
Ok(image(
865
w,
866
h,
867
crate::export_set::depth_display_rgba(&native, fmt),
868
))
869
}
870
854
871
fn image(w: u32, h: u32, rgba: Vec<u8>) -> ImageBuf {
855
872
ImageBuf {
856
873
width: w,
@@ -964,7 +981,6 @@ pub fn resolve(raw: &Buffers, detail: &[u8], w: u32, h: u32) -> Result<Target, S
964
981
let mut specular = diffuse.clone();
965
982
let mut depth = vec![0u16; count * 2];
966
983
let mut depth_t = vec![0f32; count];
967
let mut depth_preview = diffuse.clone();
968
984
for y in 0..h {
969
985
for x in 0..w {
970
986
let i = (y * w + x) as usize;
@@ -1017,14 +1033,8 @@ pub fn resolve(raw: &Buffers, detail: &[u8], w: u32, h: u32) -> Result<Target, S
1017
1033
diffuse[i * 4 + c] = srgb(color[c] / sum);
1018
1034
normals[i * 4 + c] = ((n[c] * 0.5 + 0.5) * 255.).round().clamp(0., 255.) as u8;
1019
1035
specular[i * 4 + c] = (shiny / sum).round().clamp(0., 255.) as u8;
1020
depth_preview[i * 4 + c] = (d >> 8) as u8;
1021
1036
}
1022
for map in [
1023
&mut diffuse,
1024
&mut normals,
1025
&mut specular,
1026
&mut depth_preview,
1027
] {
1037
for map in [&mut diffuse, &mut normals, &mut specular] {
1028
1038
map[i * 4 + 3] = alpha;
1029
1039
}
1030
1040
depth[i * 2] = d;
@@ -1038,7 +1048,6 @@ pub fn resolve(raw: &Buffers, detail: &[u8], w: u32, h: u32) -> Result<Target, S
1038
1048
specular: image(w, h, specular),
1039
1049
depth,
1040
1050
depth_t,
1041
depth_preview: image(w, h, depth_preview),
1042
1051
})
1043
1052
}
1044
1053
@@ -1726,11 +1735,12 @@ fn execute_target(
1726
1735
label: format!("{pose} · target-res / diffuse"),
1727
1736
});
1728
1737
let target = resolve(&raw, &detail, job.target_width, job.target_height)?;
1729
for ((map, preview), rel) in [
1730
("diffuse", target.diffuse),
1731
("depth", target.depth_preview),
1732
("normal", target.normal),
1733
("specular", target.specular),
1738
let depth_placeholder = image(job.target_width, job.target_height, Vec::new());
1739
for ((map, mut preview), rel) in [
1740
("diffuse", target.diffuse.clone()),
1741
("depth", depth_placeholder),
1742
("normal", target.normal.clone()),
1743
("specular", target.specular.clone()),
1734
1744
]
1735
1745
.into_iter()
1736
1746
.zip(paths)
@@ -1746,6 +1756,16 @@ fn execute_target(
1746
1756
debug_assert!(rel.ends_with(&format!("_{map}.png")));
1747
1757
let kept = session.verified.contains_key(rel);
1748
1758
let digest = if kept {
1759
if map == "depth" {
1760
let stored = std::fs::read(session.root.join(rel))
1761
.map_err(|e| format!("{rel}: {e}"))?;
1762
preview = stored_depth_preview(
1763
&stored,
1764
job.target_width,
1765
job.target_height,
1766
job.depth_format,
1767
)?;
1768
}
1749
1769
session.verified[rel].clone()
1750
1770
} else {
1751
1771
let bytes = if map == "depth" {
@@ -1757,12 +1777,19 @@ fn execute_target(
1757
1777
(f64::from(*t), f64::from(target.depth[i * 2 + 1]) / 65535.)
1758
1778
})
1759
1779
.collect();
1760
encode_depth_png_fmt(
1780
let bytes = encode_depth_png_fmt(
1761
1781
job.target_width,
1762
1782
job.target_height,
1763
1783
&pixels,
1764
1784
job.depth_format,
1765
)?
1785
)?;
1786
preview = stored_depth_preview(
1787
&bytes,
1788
job.target_width,
1789
job.target_height,
1790
job.depth_format,
1791
)?;
1792
bytes
1766
1793
} else {
1767
1794
encode_rgba_png(&preview, map == "diffuse")?
1768
1795
};
@@ -2828,6 +2855,63 @@ mod tests {
2828
2855
assert!(gone.is_err());
2829
2856
}
2830
2857
2858
#[test]
2859
fn live_depth_preview_shows_the_written_depth_bytes_in_every_format() {
2860
use crate::camera::{DepthEncoding, DepthFormat};
2861
for bits in [8u8, 16] {
2862
for encoding in DepthEncoding::ALL {
2863
let fmt = DepthFormat::new(bits, encoding).unwrap();
2864
let tmp = Temp::new();
2865
let mut job = resume_job(&tmp.0);
2866
job.depth_format = fmt;
2867
let (result, events) = drive(job, RunTarget::Fresh, None);
2868
result.unwrap();
2869
let run = tmp.0.join("male-body-atlas");
2870
let export = crate::atlas::AtlasSet::open_run(&run)
2871
.unwrap()
2872
.ppda
2873
.export
2874
.unwrap();
2875
let written: Vec<Vec<u8>> = export
2876
.png_sha256
2877
.keys()
2878
.filter(|k| k.ends_with("_depth.png"))
2879
.map(|k| {
2880
let bytes = std::fs::read(run.join(k)).unwrap();
2881
let native = crate::export_set::decode_depth_native(
2882
&bytes,
2883
2,
2884
2,
2885
crate::export_set::MAX_TARGET,
2886
fmt,
2887
)
2888
.unwrap();
2889
crate::export_set::depth_display_rgba(&native, fmt)
2890
})
2891
.collect();
2892
let shown: Vec<&ImageBuf> = events
2893
.iter()
2894
.filter_map(|e| match e {
2895
RenderEvent::Pass {
2896
label,
2897
image: Some(img),
2898
..
2899
} if label.ends_with("· depth") => Some(img),
2900
_ => None,
2901
})
2902
.collect();
2903
assert_eq!(shown.len(), written.len(), "{fmt:?}");
2904
for img in shown {
2905
assert_eq!((img.width, img.height), (2, 2));
2906
assert!(
2907
written.contains(&img.rgba),
2908
"{fmt:?}: preview is not the written bytes"
2909
);
2910
}
2911
}
2912
}
2913
}
2914
2831
2915
#[test]
2832
2916
#[cfg(unix)]
2833
2917
fn fresh_render_discards_older_unfinished_builds_of_the_same_target_only() {
@@ -66,7 +66,6 @@ pub struct Target {66
pub depth: Vec<u16>,66
pub depth: Vec<u16>,67
/// Unquantized normalized depth t (0..1) per pixel, for the export encoding.67
/// Unquantized normalized depth t (0..1) per pixel, for the export encoding.68
pub depth_t: Vec<f32>,68
pub depth_t: Vec<f32>,69
pub depth_preview: ImageBuf,70
}69
}71
/// Content fingerprint of a source file used for a production job.70
/// Content fingerprint of a source file used for a production job.72
#[derive(Debug, Clone, PartialEq, Eq)]71
#[derive(Debug, Clone, PartialEq, Eq)]@@ -851,6 +850,24 @@ vec3 encode_srgb(vec3 x){return mix(x*12.92,1.055*pow(max(x,vec3(0.0)),vec3(1.0/851
void main(){vec3 n=normalize(v_normal);if(u_flat){n=normalize(cross(dFdx(v_view_position),dFdy(v_view_position)));if(!gl_FrontFacing)n=-n;}vec4 c=u_color*v_color;if(u_albedo_enabled)c*=texture(u_albedo,v_uv);if(c.a<u_cutoff)discard;float s=u_specular;if(u_shiny_enabled)s*=dot(texture(u_shiny,v_uv).rgb,vec3(0.2126,0.7152,0.0722));diffuse=vec4(encode_srgb(clamp(c.rgb,0.0,1.0)),c.a);normal=vec4(n*0.5+0.5,c.a);specular=vec4(vec3(clamp(s,0.0,1.0)),c.a);}850
void main(){vec3 n=normalize(v_normal);if(u_flat){n=normalize(cross(dFdx(v_view_position),dFdy(v_view_position)));if(!gl_FrontFacing)n=-n;}vec4 c=u_color*v_color;if(u_albedo_enabled)c*=texture(u_albedo,v_uv);if(c.a<u_cutoff)discard;float s=u_specular;if(u_shiny_enabled)s*=dot(texture(u_shiny,v_uv).rgb,vec3(0.2126,0.7152,0.0722));diffuse=vec4(encode_srgb(clamp(c.rgb,0.0,1.0)),c.a);normal=vec4(n*0.5+0.5,c.a);specular=vec4(vec3(clamp(s,0.0,1.0)),c.a);}852
"#;851
"#;853
852
853
/// Live-preview image of a depth PNG exactly as written (Tim t1005u): the854
/// stored channels in display form, no depth decoding.855
pub fn stored_depth_preview(856
png_bytes: &[u8],857
w: u32,858
h: u32,859
fmt: crate::camera::DepthFormat,860
) -> Result<ImageBuf, String> {861
let native =862
crate::export_set::decode_depth_native(png_bytes, w, h, crate::export_set::MAX_TARGET, fmt)863
.map_err(|e| e.to_string())?;864
Ok(image(865
w,866
h,867
crate::export_set::depth_display_rgba(&native, fmt),868
))869
}870
854
fn image(w: u32, h: u32, rgba: Vec<u8>) -> ImageBuf {871
fn image(w: u32, h: u32, rgba: Vec<u8>) -> ImageBuf {855
ImageBuf {872
ImageBuf {856
width: w,873
width: w,@@ -964,7 +981,6 @@ pub fn resolve(raw: &Buffers, detail: &[u8], w: u32, h: u32) -> Result<Target, S964
let mut specular = diffuse.clone();981
let mut specular = diffuse.clone();965
let mut depth = vec![0u16; count * 2];982
let mut depth = vec![0u16; count * 2];966
let mut depth_t = vec![0f32; count];983
let mut depth_t = vec![0f32; count];967
let mut depth_preview = diffuse.clone();968
for y in 0..h {984
for y in 0..h {969
for x in 0..w {985
for x in 0..w {970
let i = (y * w + x) as usize;986
let i = (y * w + x) as usize;@@ -1017,14 +1033,8 @@ pub fn resolve(raw: &Buffers, detail: &[u8], w: u32, h: u32) -> Result<Target, S1017
diffuse[i * 4 + c] = srgb(color[c] / sum);1033
diffuse[i * 4 + c] = srgb(color[c] / sum);1018
normals[i * 4 + c] = ((n[c] * 0.5 + 0.5) * 255.).round().clamp(0., 255.) as u8;1034
normals[i * 4 + c] = ((n[c] * 0.5 + 0.5) * 255.).round().clamp(0., 255.) as u8;1019
specular[i * 4 + c] = (shiny / sum).round().clamp(0., 255.) as u8;1035
specular[i * 4 + c] = (shiny / sum).round().clamp(0., 255.) as u8;1020
depth_preview[i * 4 + c] = (d >> 8) as u8;1021
}1036
}1022
for map in [1037
for map in [&mut diffuse, &mut normals, &mut specular] {1023
&mut diffuse,1024
&mut normals,1025
&mut specular,1026
&mut depth_preview,1027
] {1028
map[i * 4 + 3] = alpha;1038
map[i * 4 + 3] = alpha;1029
}1039
}1030
depth[i * 2] = d;1040
depth[i * 2] = d;@@ -1038,7 +1048,6 @@ pub fn resolve(raw: &Buffers, detail: &[u8], w: u32, h: u32) -> Result<Target, S1038
specular: image(w, h, specular),1048
specular: image(w, h, specular),1039
depth,1049
depth,1040
depth_t,1050
depth_t,1041
depth_preview: image(w, h, depth_preview),1042
})1051
})1043
}1052
}1044
1053
@@ -1726,11 +1735,12 @@ fn execute_target(1726
label: format!("{pose} · target-res / diffuse"),1735
label: format!("{pose} · target-res / diffuse"),1727
});1736
});1728
let target = resolve(&raw, &detail, job.target_width, job.target_height)?;1737
let target = resolve(&raw, &detail, job.target_width, job.target_height)?;1729
for ((map, preview), rel) in [1738
let depth_placeholder = image(job.target_width, job.target_height, Vec::new());1730
("diffuse", target.diffuse),1739
for ((map, mut preview), rel) in [1731
("depth", target.depth_preview),1740
("diffuse", target.diffuse.clone()),1732
("normal", target.normal),1741
("depth", depth_placeholder),1733
("specular", target.specular),1742
("normal", target.normal.clone()),1743
("specular", target.specular.clone()),1734
]1744
]1735
.into_iter()1745
.into_iter()1736
.zip(paths)1746
.zip(paths)@@ -1746,6 +1756,16 @@ fn execute_target(1746
debug_assert!(rel.ends_with(&format!("_{map}.png")));1756
debug_assert!(rel.ends_with(&format!("_{map}.png")));1747
let kept = session.verified.contains_key(rel);1757
let kept = session.verified.contains_key(rel);1748
let digest = if kept {1758
let digest = if kept {1759
if map == "depth" {1760
let stored = std::fs::read(session.root.join(rel))1761
.map_err(|e| format!("{rel}: {e}"))?;1762
preview = stored_depth_preview(1763
&stored,1764
job.target_width,1765
job.target_height,1766
job.depth_format,1767
)?;1768
}1749
session.verified[rel].clone()1769
session.verified[rel].clone()1750
} else {1770
} else {1751
let bytes = if map == "depth" {1771
let bytes = if map == "depth" {@@ -1757,12 +1777,19 @@ fn execute_target(1757
(f64::from(*t), f64::from(target.depth[i * 2 + 1]) / 65535.)1777
(f64::from(*t), f64::from(target.depth[i * 2 + 1]) / 65535.)1758
})1778
})1759
.collect();1779
.collect();1760
encode_depth_png_fmt(1780
let bytes = encode_depth_png_fmt(1761
job.target_width,1781
job.target_width,1762
job.target_height,1782
job.target_height,1763
&pixels,1783
&pixels,1764
job.depth_format,1784
job.depth_format,1765
)?1785
)?;1786
preview = stored_depth_preview(1787
&bytes,1788
job.target_width,1789
job.target_height,1790
job.depth_format,1791
)?;1792
bytes1766
} else {1793
} else {1767
encode_rgba_png(&preview, map == "diffuse")?1794
encode_rgba_png(&preview, map == "diffuse")?1768
};1795
};@@ -2828,6 +2855,63 @@ mod tests {2828
assert!(gone.is_err());2855
assert!(gone.is_err());2829
}2856
}2830
2857
2858
#[test]2859
fn live_depth_preview_shows_the_written_depth_bytes_in_every_format() {2860
use crate::camera::{DepthEncoding, DepthFormat};2861
for bits in [8u8, 16] {2862
for encoding in DepthEncoding::ALL {2863
let fmt = DepthFormat::new(bits, encoding).unwrap();2864
let tmp = Temp::new();2865
let mut job = resume_job(&tmp.0);2866
job.depth_format = fmt;2867
let (result, events) = drive(job, RunTarget::Fresh, None);2868
result.unwrap();2869
let run = tmp.0.join("male-body-atlas");2870
let export = crate::atlas::AtlasSet::open_run(&run)2871
.unwrap()2872
.ppda2873
.export2874
.unwrap();2875
let written: Vec<Vec<u8>> = export2876
.png_sha2562877
.keys()2878
.filter(|k| k.ends_with("_depth.png"))2879
.map(|k| {2880
let bytes = std::fs::read(run.join(k)).unwrap();2881
let native = crate::export_set::decode_depth_native(2882
&bytes,2883
2,2884
2,2885
crate::export_set::MAX_TARGET,2886
fmt,2887
)2888
.unwrap();2889
crate::export_set::depth_display_rgba(&native, fmt)2890
})2891
.collect();2892
let shown: Vec<&ImageBuf> = events2893
.iter()2894
.filter_map(|e| match e {2895
RenderEvent::Pass {2896
label,2897
image: Some(img),2898
..2899
} if label.ends_with("· depth") => Some(img),2900
_ => None,2901
})2902
.collect();2903
assert_eq!(shown.len(), written.len(), "{fmt:?}");2904
for img in shown {2905
assert_eq!((img.width, img.height), (2, 2));2906
assert!(2907
written.contains(&img.rgba),2908
"{fmt:?}: preview is not the written bytes"2909
);2910
}2911
}2912
}2913
}2914
2831
#[test]2915
#[test]2832
#[cfg(unix)]2916
#[cfg(unix)]2833
fn fresh_render_discards_older_unfinished_builds_of_the_same_target_only() {2917
fn fresh_render_discards_older_unfinished_builds_of_the_same_target_only() {