Compare commits
2
Commits
7c7e91c857
...
6cd23bf02a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cd23bf02a
|
||
|
|
cbc8ef36f8
|
@@ -61,7 +61,7 @@ pub use self::runtime::{
|
|||||||
pub use self::smoke_types::{
|
pub use self::smoke_types::{
|
||||||
VulkanSmokeBootstrapProgress, VulkanSmokeBootstrapSnapshot, VulkanSmokeFrameOutcome,
|
VulkanSmokeBootstrapProgress, VulkanSmokeBootstrapSnapshot, VulkanSmokeFrameOutcome,
|
||||||
VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, VulkanSmokeRendererError,
|
VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, VulkanSmokeRendererError,
|
||||||
VulkanSmokeRendererReport, VulkanValidationReport,
|
VulkanSmokeRendererReport, VulkanSmokeShutdownReport, VulkanValidationReport,
|
||||||
};
|
};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use self::surface::extension_name;
|
use self::surface::extension_name;
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ use super::{
|
|||||||
destroy_allocated_buffer, destroy_swapchain_resources, plan_vulkan_surface,
|
destroy_allocated_buffer, destroy_swapchain_resources, plan_vulkan_surface,
|
||||||
VulkanAllocatedBuffer, VulkanInstanceConfig, VulkanInstanceProbe, VulkanLogicalDeviceProbe,
|
VulkanAllocatedBuffer, VulkanInstanceConfig, VulkanInstanceProbe, VulkanLogicalDeviceProbe,
|
||||||
VulkanSmokeFrameOutcome, VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo,
|
VulkanSmokeFrameOutcome, VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo,
|
||||||
VulkanSmokeRendererError, VulkanSmokeRendererReport, VulkanSurfaceProbe, VulkanSwapchainProbe,
|
VulkanSmokeRendererError, VulkanSmokeRendererReport, VulkanSmokeShutdownReport,
|
||||||
VulkanSwapchainResources, VulkanValidationMessenger, VulkanValidationReport,
|
VulkanSurfaceProbe, VulkanSwapchainProbe, VulkanSwapchainResources, VulkanValidationMessenger,
|
||||||
|
VulkanValidationReport,
|
||||||
};
|
};
|
||||||
use crate::policy::KHR_PORTABILITY_SUBSET_EXTENSION;
|
use crate::policy::KHR_PORTABILITY_SUBSET_EXTENSION;
|
||||||
use crate::shader_manifest::{triangle_shader_manifest, validate_shader_manifest};
|
use crate::shader_manifest::{triangle_shader_manifest, validate_shader_manifest};
|
||||||
@@ -30,6 +31,30 @@ fn take_runtime_owners_in_dependency_order<Instance, Validation, Surface, Device
|
|||||||
instance.take();
|
instance.take();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_runtime_owners_with_validation_snapshot<
|
||||||
|
Instance,
|
||||||
|
Validation,
|
||||||
|
Surface,
|
||||||
|
Device,
|
||||||
|
Swapchain,
|
||||||
|
Snapshot,
|
||||||
|
Capture,
|
||||||
|
>(
|
||||||
|
instance: &mut Option<Instance>,
|
||||||
|
validation: &mut Option<Validation>,
|
||||||
|
surface: &mut Option<Surface>,
|
||||||
|
device: &mut Option<Device>,
|
||||||
|
swapchain: &mut Option<Swapchain>,
|
||||||
|
capture: Capture,
|
||||||
|
) -> Option<Snapshot>
|
||||||
|
where
|
||||||
|
Capture: FnOnce(&Validation) -> Snapshot,
|
||||||
|
{
|
||||||
|
let snapshot = validation.as_ref().map(capture);
|
||||||
|
take_runtime_owners_in_dependency_order(instance, validation, surface, device, swapchain);
|
||||||
|
snapshot
|
||||||
|
}
|
||||||
|
|
||||||
struct RollbackOnDrop<T, F>
|
struct RollbackOnDrop<T, F>
|
||||||
where
|
where
|
||||||
F: FnOnce(T),
|
F: FnOnce(T),
|
||||||
@@ -226,6 +251,16 @@ impl VulkanSmokeRenderer {
|
|||||||
self.swapchain_recreate_count
|
self.swapchain_recreate_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Explicitly idles and tears down the renderer while the native window is still alive.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`VulkanSmokeRendererError`] when the renderer cannot reach a
|
||||||
|
/// stable idle point before teardown begins.
|
||||||
|
pub fn shutdown(mut self) -> Result<VulkanSmokeShutdownReport, VulkanSmokeRendererError> {
|
||||||
|
self.shutdown_inner()
|
||||||
|
}
|
||||||
|
|
||||||
/// Requests swapchain recreation for a new drawable extent.
|
/// Requests swapchain recreation for a new drawable extent.
|
||||||
pub fn request_resize(&mut self, extent: (u32, u32)) {
|
pub fn request_resize(&mut self, extent: (u32, u32)) {
|
||||||
self.pending_extent = Some(extent);
|
self.pending_extent = Some(extent);
|
||||||
@@ -595,11 +630,7 @@ impl VulkanSmokeRenderer {
|
|||||||
self.current_frame = 0;
|
self.current_frame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn teardown(&mut self) {
|
fn destroy_device_owned_resources(&mut self) {
|
||||||
if let Some(device) = self.device.as_ref() {
|
|
||||||
// SAFETY: The logical device remains live until teardown finishes and idling prevents in-flight work from touching swapchain, buffers, sync objects or the command pool after destruction starts.
|
|
||||||
let _ = unsafe { device.device().device_wait_idle() };
|
|
||||||
}
|
|
||||||
self.destroy_swapchain_resources();
|
self.destroy_swapchain_resources();
|
||||||
if let Some(device) = self.device.as_ref() {
|
if let Some(device) = self.device.as_ref() {
|
||||||
if let Some(buffer) = self.index_buffer.take() {
|
if let Some(buffer) = self.index_buffer.take() {
|
||||||
@@ -623,13 +654,49 @@ impl VulkanSmokeRenderer {
|
|||||||
.destroy_command_pool(self.command_pool, None);
|
.destroy_command_pool(self.command_pool, None);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Drop child Vulkan owners explicitly before their parents instead of relying on field order.
|
self.pending_extent = None;
|
||||||
take_runtime_owners_in_dependency_order(
|
}
|
||||||
|
|
||||||
|
fn shutdown_inner(&mut self) -> Result<VulkanSmokeShutdownReport, VulkanSmokeRendererError> {
|
||||||
|
if let Some(device) = self.device.as_ref() {
|
||||||
|
// SAFETY: The logical device remains live until teardown finishes and idling prevents in-flight work from touching swapchain, buffers, sync objects or the command pool after destruction starts.
|
||||||
|
unsafe { device.device().device_wait_idle() }.map_err(|error| {
|
||||||
|
VulkanSmokeRendererError::VulkanOperation {
|
||||||
|
context: "vkDeviceWaitIdle",
|
||||||
|
result: error,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
self.destroy_device_owned_resources();
|
||||||
|
let validation = take_runtime_owners_with_validation_snapshot(
|
||||||
&mut self.instance,
|
&mut self.instance,
|
||||||
&mut self.validation,
|
&mut self.validation,
|
||||||
&mut self.surface,
|
&mut self.surface,
|
||||||
&mut self.device,
|
&mut self.device,
|
||||||
&mut self.swapchain,
|
&mut self.swapchain,
|
||||||
|
VulkanValidationMessenger::report,
|
||||||
|
)
|
||||||
|
.unwrap_or_default();
|
||||||
|
Ok(VulkanSmokeShutdownReport {
|
||||||
|
renderer_report: self.report.clone(),
|
||||||
|
swapchain_recreate_count: self.swapchain_recreate_count,
|
||||||
|
validation,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn teardown(&mut self) {
|
||||||
|
if let Some(device) = self.device.as_ref() {
|
||||||
|
// SAFETY: The logical device remains live until teardown finishes and idling prevents in-flight work from touching swapchain, buffers, sync objects or the command pool after destruction starts.
|
||||||
|
let _ = unsafe { device.device().device_wait_idle() };
|
||||||
|
}
|
||||||
|
self.destroy_device_owned_resources();
|
||||||
|
let _ = take_runtime_owners_with_validation_snapshot(
|
||||||
|
&mut self.instance,
|
||||||
|
&mut self.validation,
|
||||||
|
&mut self.surface,
|
||||||
|
&mut self.device,
|
||||||
|
&mut self.swapchain,
|
||||||
|
VulkanValidationMessenger::report,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -642,12 +709,16 @@ impl Drop for VulkanSmokeRenderer {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{take_runtime_owners_in_dependency_order, RollbackOnDrop};
|
use super::{
|
||||||
|
take_runtime_owners_in_dependency_order, take_runtime_owners_with_validation_snapshot,
|
||||||
|
RollbackOnDrop,
|
||||||
|
};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
enum TeardownStep {
|
enum TeardownStep {
|
||||||
|
Snapshot,
|
||||||
Instance,
|
Instance,
|
||||||
Validation,
|
Validation,
|
||||||
Surface,
|
Surface,
|
||||||
@@ -778,6 +849,43 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn final_validation_snapshot_is_captured_before_validation_drop() {
|
||||||
|
let log = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
let mut instance = Some(tracker(TeardownStep::Instance, &log));
|
||||||
|
let mut validation = Some(tracker(TeardownStep::Validation, &log));
|
||||||
|
let mut surface = Some(tracker(TeardownStep::Surface, &log));
|
||||||
|
let mut device = Some(tracker(TeardownStep::Device, &log));
|
||||||
|
let mut swapchain = Some(tracker(TeardownStep::Swapchain, &log));
|
||||||
|
|
||||||
|
let snapshot = take_runtime_owners_with_validation_snapshot(
|
||||||
|
&mut instance,
|
||||||
|
&mut validation,
|
||||||
|
&mut surface,
|
||||||
|
&mut device,
|
||||||
|
&mut swapchain,
|
||||||
|
|_| {
|
||||||
|
log.borrow_mut().push(TeardownStep::Snapshot);
|
||||||
|
TeardownStep::Validation
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(snapshot, Some(TeardownStep::Validation));
|
||||||
|
assert_eq!(
|
||||||
|
Rc::into_inner(log)
|
||||||
|
.expect("all drop trackers released")
|
||||||
|
.into_inner(),
|
||||||
|
vec![
|
||||||
|
TeardownStep::Snapshot,
|
||||||
|
TeardownStep::Swapchain,
|
||||||
|
TeardownStep::Device,
|
||||||
|
TeardownStep::Surface,
|
||||||
|
TeardownStep::Validation,
|
||||||
|
TeardownStep::Instance,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rollback_guard_runs_cleanup_when_later_step_fails() {
|
fn rollback_guard_runs_cleanup_when_later_step_fails() {
|
||||||
let log = Rc::new(RefCell::new(Vec::new()));
|
let log = Rc::new(RefCell::new(Vec::new()));
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ pub struct VulkanSmokeRendererReport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Measured validation counters from the live smoke loop.
|
/// Measured validation counters from the live smoke loop.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
pub struct VulkanValidationReport {
|
pub struct VulkanValidationReport {
|
||||||
/// Validation warnings observed by the debug messenger.
|
/// Validation warnings observed by the debug messenger.
|
||||||
pub warning_count: u32,
|
pub warning_count: u32,
|
||||||
@@ -138,6 +138,17 @@ pub struct VulkanValidationReport {
|
|||||||
pub vuids: Vec<String>,
|
pub vuids: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Final smoke renderer shutdown evidence captured after explicit teardown.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct VulkanSmokeShutdownReport {
|
||||||
|
/// Stable renderer bootstrap and swapchain report.
|
||||||
|
pub renderer_report: VulkanSmokeRendererReport,
|
||||||
|
/// Measured swapchain recreation count for the completed smoke loop.
|
||||||
|
pub swapchain_recreate_count: u32,
|
||||||
|
/// Final validation snapshot captured before the debug messenger is destroyed.
|
||||||
|
pub validation: VulkanValidationReport,
|
||||||
|
}
|
||||||
|
|
||||||
/// Result of one rendered smoke frame.
|
/// Result of one rendered smoke frame.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum VulkanSmokeFrameOutcome {
|
pub enum VulkanSmokeFrameOutcome {
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ use fparkan_platform::RenderRequest;
|
|||||||
use fparkan_platform_winit::{window_native_handles, WinitWindowPlan};
|
use fparkan_platform_winit::{window_native_handles, WinitWindowPlan};
|
||||||
use fparkan_render_vulkan::{
|
use fparkan_render_vulkan::{
|
||||||
VulkanSmokeBootstrapProgress, VulkanSmokeFrameOutcome, VulkanSmokeRenderer,
|
VulkanSmokeBootstrapProgress, VulkanSmokeFrameOutcome, VulkanSmokeRenderer,
|
||||||
VulkanSmokeRendererCreateInfo,
|
VulkanSmokeRendererCreateInfo, VulkanSmokeRendererReport, VulkanSmokeShutdownReport,
|
||||||
|
VulkanValidationReport,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -176,6 +177,7 @@ struct SmokeApp {
|
|||||||
window_id: Option<WindowId>,
|
window_id: Option<WindowId>,
|
||||||
window: Option<Window>,
|
window: Option<Window>,
|
||||||
renderer: Option<VulkanSmokeRenderer>,
|
renderer: Option<VulkanSmokeRenderer>,
|
||||||
|
final_renderer: Option<RendererSnapshot>,
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
output: Option<String>,
|
output: Option<String>,
|
||||||
frames_presented: u32,
|
frames_presented: u32,
|
||||||
@@ -193,6 +195,23 @@ fn drop_renderer_before_window<Renderer, WindowLike>(
|
|||||||
drop(window.take());
|
drop(window.take());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct RendererSnapshot {
|
||||||
|
report: VulkanSmokeRendererReport,
|
||||||
|
swapchain_recreate_count: u32,
|
||||||
|
validation: VulkanValidationReport,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VulkanSmokeShutdownReport> for RendererSnapshot {
|
||||||
|
fn from(report: VulkanSmokeShutdownReport) -> Self {
|
||||||
|
Self {
|
||||||
|
report: report.renderer_report,
|
||||||
|
swapchain_recreate_count: report.swapchain_recreate_count,
|
||||||
|
validation: report.validation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SmokeApp {
|
impl SmokeApp {
|
||||||
fn new(
|
fn new(
|
||||||
options: SmokeOptions,
|
options: SmokeOptions,
|
||||||
@@ -206,6 +225,7 @@ impl SmokeApp {
|
|||||||
window_id: None,
|
window_id: None,
|
||||||
window: None,
|
window: None,
|
||||||
renderer: None,
|
renderer: None,
|
||||||
|
final_renderer: None,
|
||||||
error: None,
|
error: None,
|
||||||
output: None,
|
output: None,
|
||||||
frames_presented: 0,
|
frames_presented: 0,
|
||||||
@@ -244,20 +264,31 @@ impl SmokeApp {
|
|||||||
.map_err(|err| format!("{}: {err}", self.options.out.display()))
|
.map_err(|err| format!("{}: {err}", self.options.out.display()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn live_renderer_snapshot(&self) -> Option<RendererSnapshot> {
|
||||||
|
self.renderer.as_ref().map(|renderer| RendererSnapshot {
|
||||||
|
report: renderer.report().clone(),
|
||||||
|
swapchain_recreate_count: renderer.swapchain_recreate_count(),
|
||||||
|
validation: renderer.validation_report(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderer_snapshot(&self) -> Option<RendererSnapshot> {
|
||||||
|
self.final_renderer
|
||||||
|
.clone()
|
||||||
|
.or_else(|| self.live_renderer_snapshot())
|
||||||
|
}
|
||||||
|
|
||||||
fn render_report(
|
fn render_report(
|
||||||
&self,
|
&self,
|
||||||
status: &'static str,
|
status: &'static str,
|
||||||
failure_reason: Option<&str>,
|
failure_reason: Option<&str>,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
let renderer = self.renderer.as_ref();
|
let renderer = self.renderer_snapshot();
|
||||||
let validation = renderer.map_or(
|
let validation = renderer
|
||||||
fparkan_render_vulkan::VulkanValidationReport {
|
.as_ref()
|
||||||
warning_count: 0,
|
.map_or_else(VulkanValidationReport::default, |snapshot| {
|
||||||
error_count: 0,
|
snapshot.validation.clone()
|
||||||
vuids: Vec::new(),
|
});
|
||||||
},
|
|
||||||
VulkanSmokeRenderer::validation_report,
|
|
||||||
);
|
|
||||||
let smoke_report = SmokeReport {
|
let smoke_report = SmokeReport {
|
||||||
schema_version: SCHEMA_VERSION,
|
schema_version: SCHEMA_VERSION,
|
||||||
commit_sha: compiled_commit_sha(),
|
commit_sha: compiled_commit_sha(),
|
||||||
@@ -272,14 +303,16 @@ impl SmokeApp {
|
|||||||
frames: self.frames_presented,
|
frames: self.frames_presented,
|
||||||
resize_count: self.resize_count,
|
resize_count: self.resize_count,
|
||||||
swapchain_recreate_count: renderer
|
swapchain_recreate_count: renderer
|
||||||
.map_or(0, VulkanSmokeRenderer::swapchain_recreate_count),
|
.as_ref()
|
||||||
|
.map_or(0, |snapshot| snapshot.swapchain_recreate_count),
|
||||||
validation_warning_count: validation.warning_count,
|
validation_warning_count: validation.warning_count,
|
||||||
validation_error_count: validation.error_count,
|
validation_error_count: validation.error_count,
|
||||||
validation_vuids: &validation.vuids,
|
validation_vuids: &validation.vuids,
|
||||||
requested_frames: self.options.frames,
|
requested_frames: self.options.frames,
|
||||||
timeout_seconds: self.options.timeout_seconds,
|
timeout_seconds: self.options.timeout_seconds,
|
||||||
shader_manifest_hash: renderer
|
shader_manifest_hash: renderer
|
||||||
.map_or("", |value| value.report().shader_manifest_hash.as_str()),
|
.as_ref()
|
||||||
|
.map_or("", |snapshot| snapshot.report.shader_manifest_hash.as_str()),
|
||||||
vulkan_loader_status: if renderer.is_some() {
|
vulkan_loader_status: if renderer.is_some() {
|
||||||
"available"
|
"available"
|
||||||
} else {
|
} else {
|
||||||
@@ -305,31 +338,43 @@ impl SmokeApp {
|
|||||||
} else {
|
} else {
|
||||||
"failed"
|
"failed"
|
||||||
},
|
},
|
||||||
vulkan_device_name: renderer.map_or("", |value| value.report().device_name.as_str()),
|
vulkan_device_name: renderer
|
||||||
|
.as_ref()
|
||||||
|
.map_or("", |snapshot| snapshot.report.device_name.as_str()),
|
||||||
vulkan_logical_device_status: if renderer.is_some() {
|
vulkan_logical_device_status: if renderer.is_some() {
|
||||||
"created"
|
"created"
|
||||||
} else {
|
} else {
|
||||||
"failed"
|
"failed"
|
||||||
},
|
},
|
||||||
vulkan_logical_device_graphics_queue_family: renderer
|
vulkan_logical_device_graphics_queue_family: renderer
|
||||||
.map_or(0, |value| value.report().graphics_queue_family),
|
.as_ref()
|
||||||
|
.map_or(0, |snapshot| snapshot.report.graphics_queue_family),
|
||||||
vulkan_logical_device_present_queue_family: renderer
|
vulkan_logical_device_present_queue_family: renderer
|
||||||
.map_or(0, |value| value.report().present_queue_family),
|
.as_ref()
|
||||||
|
.map_or(0, |snapshot| snapshot.report.present_queue_family),
|
||||||
vulkan_logical_device_enabled_extension_count: renderer
|
vulkan_logical_device_enabled_extension_count: renderer
|
||||||
.map_or(0, |value| value.report().enabled_extension_count),
|
.as_ref()
|
||||||
|
.map_or(0, |snapshot| snapshot.report.enabled_extension_count),
|
||||||
vulkan_swapchain_status: if renderer.is_some() {
|
vulkan_swapchain_status: if renderer.is_some() {
|
||||||
"created"
|
"created"
|
||||||
} else {
|
} else {
|
||||||
"failed"
|
"failed"
|
||||||
},
|
},
|
||||||
vulkan_swapchain_width: renderer.map_or(0, |value| value.report().swapchain_extent.0),
|
vulkan_swapchain_width: renderer
|
||||||
vulkan_swapchain_height: renderer.map_or(0, |value| value.report().swapchain_extent.1),
|
.as_ref()
|
||||||
|
.map_or(0, |snapshot| snapshot.report.swapchain_extent.0),
|
||||||
|
vulkan_swapchain_height: renderer
|
||||||
|
.as_ref()
|
||||||
|
.map_or(0, |snapshot| snapshot.report.swapchain_extent.1),
|
||||||
vulkan_swapchain_image_count: renderer
|
vulkan_swapchain_image_count: renderer
|
||||||
.map_or(0, |value| value.report().swapchain_image_count),
|
.as_ref()
|
||||||
|
.map_or(0, |snapshot| snapshot.report.swapchain_image_count),
|
||||||
vulkan_portability_enumeration: renderer
|
vulkan_portability_enumeration: renderer
|
||||||
.is_some_and(|value| value.report().portability_enumeration),
|
.as_ref()
|
||||||
|
.is_some_and(|snapshot| snapshot.report.portability_enumeration),
|
||||||
vulkan_portability_subset_enabled: renderer
|
vulkan_portability_subset_enabled: renderer
|
||||||
.is_some_and(|value| value.report().portability_subset_enabled),
|
.as_ref()
|
||||||
|
.is_some_and(|snapshot| snapshot.report.portability_subset_enabled),
|
||||||
};
|
};
|
||||||
serde_json::to_string_pretty(&smoke_report)
|
serde_json::to_string_pretty(&smoke_report)
|
||||||
.map(|json| format!("{json}\n"))
|
.map(|json| format!("{json}\n"))
|
||||||
@@ -362,7 +407,6 @@ impl SmokeApp {
|
|||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let validation = renderer.validation_report();
|
|
||||||
if self.frames_presented < self.options.frames {
|
if self.frames_presented < self.options.frames {
|
||||||
self.error = Some("native smoke did not reach the required frame count".to_string());
|
self.error = Some("native smoke did not reach the required frame count".to_string());
|
||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
@@ -376,15 +420,34 @@ impl SmokeApp {
|
|||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if validation.warning_count != 0 || validation.error_count != 0 {
|
let renderer = match self.renderer.take() {
|
||||||
|
Some(renderer) => renderer,
|
||||||
|
None => {
|
||||||
|
self.error = Some("native smoke renderer was not initialized".to_string());
|
||||||
|
event_loop.exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let final_renderer = match renderer.shutdown() {
|
||||||
|
Ok(report) => RendererSnapshot::from(report),
|
||||||
|
Err(err) => {
|
||||||
|
self.error = Some(err.to_string());
|
||||||
|
event_loop.exit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if final_renderer.validation.warning_count != 0
|
||||||
|
|| final_renderer.validation.error_count != 0
|
||||||
|
{
|
||||||
|
self.final_renderer = Some(final_renderer.clone());
|
||||||
self.error = Some(format!(
|
self.error = Some(format!(
|
||||||
"native smoke validation must stay clean (warnings={}, errors={})",
|
"native smoke validation must stay clean (warnings={}, errors={})",
|
||||||
validation.warning_count, validation.error_count
|
final_renderer.validation.warning_count, final_renderer.validation.error_count
|
||||||
));
|
));
|
||||||
event_loop.exit();
|
event_loop.exit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let _ = renderer;
|
self.final_renderer = Some(final_renderer);
|
||||||
let report = match self.render_report("passed", None) {
|
let report = match self.render_report("passed", None) {
|
||||||
Ok(report) => report,
|
Ok(report) => report,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -1077,6 +1140,7 @@ mod tests {
|
|||||||
window_id: None,
|
window_id: None,
|
||||||
window: None,
|
window: None,
|
||||||
renderer: None,
|
renderer: None,
|
||||||
|
final_renderer: None,
|
||||||
error: Some("native smoke timed out after 7 seconds".to_string()),
|
error: Some("native smoke timed out after 7 seconds".to_string()),
|
||||||
output: None,
|
output: None,
|
||||||
frames_presented: 42,
|
frames_presented: 42,
|
||||||
|
|||||||
+208
-6
@@ -133,7 +133,7 @@ fn run(args: &[String]) -> Result<(), String> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(
|
_ => Err(
|
||||||
"usage: cargo xtask ci | policy | shader-provenance | acceptance report --suite synthetic|licensed [--stage 0..5|all] [--manifest corpora.toml] [--out <path>] | acceptance audit [--roadmap <path>] [--coverage <path>] [--out <path>] [--strict] | native-smoke audit --dir <path> | package --target <triple> --app viewer|game|headless|cli | test synthetic|licensed [--stage 0..5|all] [--manifest corpora.toml] | corpus baseline --root <path>"
|
"usage: cargo xtask ci | policy | shader-provenance | acceptance report --suite synthetic|licensed [--stage 0..5|all] [--manifest corpora.toml] [--out <path>] | acceptance audit [--roadmap <path>] [--coverage <path>] [--out <path>] [--strict] | native-smoke audit --dir <path> [--expected-commit <sha>] [--expected-shader-manifest-hash <sha256>] | package --target <triple> --app viewer|game|headless|cli | test synthetic|licensed [--stage 0..5|all] [--manifest corpora.toml] | corpus baseline --root <path>"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -226,6 +226,12 @@ fn load_shader_manifest(path: &Path) -> Result<ShaderManifestJson, String> {
|
|||||||
path.display()
|
path.display()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if manifest.manifest_hash.trim().is_empty() {
|
||||||
|
return Err(format!(
|
||||||
|
"{}: shader manifest must include a non-empty manifest_hash",
|
||||||
|
path.display()
|
||||||
|
));
|
||||||
|
}
|
||||||
Ok(manifest)
|
Ok(manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1655,6 +1661,8 @@ struct AuditOptions {
|
|||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
struct NativeSmokeAuditOptions {
|
struct NativeSmokeAuditOptions {
|
||||||
dir: PathBuf,
|
dir: PathBuf,
|
||||||
|
expected_commit: String,
|
||||||
|
expected_shader_manifest_hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@@ -1663,6 +1671,7 @@ struct ShaderManifestJson {
|
|||||||
compiler: ShaderToolManifestJson,
|
compiler: ShaderToolManifestJson,
|
||||||
validator: ShaderToolManifestJson,
|
validator: ShaderToolManifestJson,
|
||||||
modules: Vec<ShaderModuleManifestJson>,
|
modules: Vec<ShaderModuleManifestJson>,
|
||||||
|
manifest_hash: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@@ -1821,6 +1830,8 @@ fn parse_audit_options(args: &[String]) -> Result<AuditOptions, String> {
|
|||||||
|
|
||||||
fn parse_native_smoke_audit_options(args: &[String]) -> Result<NativeSmokeAuditOptions, String> {
|
fn parse_native_smoke_audit_options(args: &[String]) -> Result<NativeSmokeAuditOptions, String> {
|
||||||
let mut dir = None;
|
let mut dir = None;
|
||||||
|
let mut expected_commit = expected_native_smoke_commit()?;
|
||||||
|
let mut expected_shader_manifest_hash = current_shader_manifest_hash()?;
|
||||||
let mut iter = args.iter();
|
let mut iter = args.iter();
|
||||||
while let Some(arg) = iter.next() {
|
while let Some(arg) = iter.next() {
|
||||||
match arg.as_str() {
|
match arg.as_str() {
|
||||||
@@ -1830,17 +1841,43 @@ fn parse_native_smoke_audit_options(args: &[String]) -> Result<NativeSmokeAuditO
|
|||||||
.ok_or_else(|| "--dir requires a path".to_string())?;
|
.ok_or_else(|| "--dir requires a path".to_string())?;
|
||||||
dir = Some(PathBuf::from(value));
|
dir = Some(PathBuf::from(value));
|
||||||
}
|
}
|
||||||
|
"--expected-commit" => {
|
||||||
|
let value = iter
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| "--expected-commit requires a value".to_string())?;
|
||||||
|
if !is_commit_sha(value) {
|
||||||
|
return Err(format!(
|
||||||
|
"--expected-commit must be a 40-character lowercase or uppercase hex string, found {value:?}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
expected_commit = value.to_string();
|
||||||
|
}
|
||||||
|
"--expected-shader-manifest-hash" => {
|
||||||
|
let value = iter.next().ok_or_else(|| {
|
||||||
|
"--expected-shader-manifest-hash requires a value".to_string()
|
||||||
|
})?;
|
||||||
|
if value.trim().is_empty() {
|
||||||
|
return Err("--expected-shader-manifest-hash must be non-empty".to_string());
|
||||||
|
}
|
||||||
|
expected_shader_manifest_hash = value.to_string();
|
||||||
|
}
|
||||||
_ => return Err(format!("unknown native-smoke audit option: {arg}")),
|
_ => return Err(format!("unknown native-smoke audit option: {arg}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(NativeSmokeAuditOptions {
|
Ok(NativeSmokeAuditOptions {
|
||||||
dir: dir.ok_or_else(|| "native-smoke audit requires --dir".to_string())?,
|
dir: dir.ok_or_else(|| "native-smoke audit requires --dir".to_string())?,
|
||||||
|
expected_commit,
|
||||||
|
expected_shader_manifest_hash,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_native_smoke_audit(options: &NativeSmokeAuditOptions) -> Result<(), String> {
|
fn run_native_smoke_audit(options: &NativeSmokeAuditOptions) -> Result<(), String> {
|
||||||
let reports = read_native_smoke_reports(&options.dir)?;
|
let reports = read_native_smoke_reports(&options.dir)?;
|
||||||
let failures = audit_native_smoke_reports(&reports);
|
let failures = audit_native_smoke_reports(
|
||||||
|
&reports,
|
||||||
|
&options.expected_commit,
|
||||||
|
&options.expected_shader_manifest_hash,
|
||||||
|
);
|
||||||
if failures.is_empty() {
|
if failures.is_empty() {
|
||||||
println!("native smoke artifacts passed: {}", options.dir.display());
|
println!("native smoke artifacts passed: {}", options.dir.display());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1877,7 +1914,11 @@ fn read_native_smoke_reports(dir: &Path) -> Result<BTreeMap<String, serde_json::
|
|||||||
Ok(reports)
|
Ok(reports)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn audit_native_smoke_reports(reports: &BTreeMap<String, serde_json::Value>) -> Vec<String> {
|
fn audit_native_smoke_reports(
|
||||||
|
reports: &BTreeMap<String, serde_json::Value>,
|
||||||
|
expected_commit: &str,
|
||||||
|
expected_shader_manifest_hash: &str,
|
||||||
|
) -> Vec<String> {
|
||||||
let mut failures = Vec::new();
|
let mut failures = Vec::new();
|
||||||
let mut commit_shas = BTreeSet::new();
|
let mut commit_shas = BTreeSet::new();
|
||||||
let mut rust_toolchains = BTreeSet::new();
|
let mut rust_toolchains = BTreeSet::new();
|
||||||
@@ -1887,6 +1928,20 @@ fn audit_native_smoke_reports(reports: &BTreeMap<String, serde_json::Value>) ->
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
validate_native_smoke_report(platform, report, &mut failures);
|
validate_native_smoke_report(platform, report, &mut failures);
|
||||||
|
expect_string_field(
|
||||||
|
platform,
|
||||||
|
report,
|
||||||
|
"commit_sha",
|
||||||
|
expected_commit,
|
||||||
|
&mut failures,
|
||||||
|
);
|
||||||
|
expect_string_field(
|
||||||
|
platform,
|
||||||
|
report,
|
||||||
|
"shader_manifest_hash",
|
||||||
|
expected_shader_manifest_hash,
|
||||||
|
&mut failures,
|
||||||
|
);
|
||||||
if let Ok(commit_sha) = json_string_field(report, "commit_sha") {
|
if let Ok(commit_sha) = json_string_field(report, "commit_sha") {
|
||||||
if commit_sha == "unknown" {
|
if commit_sha == "unknown" {
|
||||||
failures.push(format!("{platform}: commit_sha must not be \"unknown\""));
|
failures.push(format!("{platform}: commit_sha must not be \"unknown\""));
|
||||||
@@ -1918,6 +1973,32 @@ fn audit_native_smoke_reports(reports: &BTreeMap<String, serde_json::Value>) ->
|
|||||||
failures
|
failures
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expected_native_smoke_commit() -> Result<String, String> {
|
||||||
|
if let Ok(commit_sha) = std::env::var("GITHUB_SHA") {
|
||||||
|
if is_commit_sha(&commit_sha) {
|
||||||
|
return Ok(commit_sha);
|
||||||
|
}
|
||||||
|
return Err(format!(
|
||||||
|
"GITHUB_SHA must be a 40-character lowercase or uppercase hex string when set, found {commit_sha:?}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let commit_sha = current_git_commit_sha();
|
||||||
|
if is_commit_sha(&commit_sha) {
|
||||||
|
Ok(commit_sha)
|
||||||
|
} else {
|
||||||
|
Err(
|
||||||
|
"native-smoke audit could not resolve expected commit from GITHUB_SHA or git rev-parse HEAD"
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_shader_manifest_hash() -> Result<String, String> {
|
||||||
|
let manifest_path = workspace_relative_path(SHADER_MANIFEST_REPORT);
|
||||||
|
let manifest = load_shader_manifest(&manifest_path)?;
|
||||||
|
Ok(manifest.manifest_hash)
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_native_smoke_report(
|
fn validate_native_smoke_report(
|
||||||
platform: &str,
|
platform: &str,
|
||||||
report: &serde_json::Value,
|
report: &serde_json::Value,
|
||||||
@@ -2514,6 +2595,10 @@ fn current_git_commit_sha() -> String {
|
|||||||
.unwrap_or_else(|| "unknown".to_string())
|
.unwrap_or_else(|| "unknown".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_commit_sha(value: &str) -> bool {
|
||||||
|
value.len() == 40 && value.chars().all(|ch| ch.is_ascii_hexdigit())
|
||||||
|
}
|
||||||
|
|
||||||
fn current_git_dirty() -> bool {
|
fn current_git_dirty() -> bool {
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["status", "--short"])
|
.args(["status", "--short"])
|
||||||
@@ -2936,6 +3021,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn native_smoke_audit_accepts_complete_required_platform_pass() {
|
fn native_smoke_audit_accepts_complete_required_platform_pass() {
|
||||||
|
let expected_commit = "0123456789abcdef0123456789abcdef01234567";
|
||||||
|
let expected_shader_manifest_hash =
|
||||||
|
"dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c";
|
||||||
let reports = ["macos"]
|
let reports = ["macos"]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|platform| {
|
.map(|platform| {
|
||||||
@@ -2962,7 +3050,7 @@ mod tests {
|
|||||||
"swapchain_recreate_count": 1,
|
"swapchain_recreate_count": 1,
|
||||||
"validation_warning_count": 0,
|
"validation_warning_count": 0,
|
||||||
"validation_error_count": 0,
|
"validation_error_count": 0,
|
||||||
"shader_manifest_hash": "dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c",
|
"shader_manifest_hash": expected_shader_manifest_hash,
|
||||||
"vulkan_loader_status": "available",
|
"vulkan_loader_status": "available",
|
||||||
"vulkan_instance_status": "created",
|
"vulkan_instance_status": "created",
|
||||||
"window_status": "created",
|
"window_status": "created",
|
||||||
@@ -2984,11 +3072,17 @@ mod tests {
|
|||||||
})
|
})
|
||||||
.collect::<BTreeMap<_, _>>();
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
|
||||||
assert_eq!(audit_native_smoke_reports(&reports), Vec::<String>::new());
|
assert_eq!(
|
||||||
|
audit_native_smoke_reports(&reports, expected_commit, expected_shader_manifest_hash,),
|
||||||
|
Vec::<String>::new()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn native_smoke_audit_rejects_blocked_or_incomplete_reports() {
|
fn native_smoke_audit_rejects_blocked_or_incomplete_reports() {
|
||||||
|
let expected_commit = "0123456789abcdef0123456789abcdef01234567";
|
||||||
|
let expected_shader_manifest_hash =
|
||||||
|
"dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c";
|
||||||
let reports = [(
|
let reports = [(
|
||||||
"macos".to_string(),
|
"macos".to_string(),
|
||||||
serde_json::json!({
|
serde_json::json!({
|
||||||
@@ -3018,7 +3112,8 @@ mod tests {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.collect::<BTreeMap<_, _>>();
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
|
||||||
let failures = audit_native_smoke_reports(&reports);
|
let failures =
|
||||||
|
audit_native_smoke_reports(&reports, expected_commit, expected_shader_manifest_hash);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
failures.contains(&"macos: status expected \"passed\", found \"blocked\"".to_string())
|
failures.contains(&"macos: status expected \"passed\", found \"blocked\"".to_string())
|
||||||
@@ -3041,6 +3136,113 @@ mod tests {
|
|||||||
.contains(&"macos: validation_error_count must be an unsigned integer".to_string()));
|
.contains(&"macos: validation_error_count must be an unsigned integer".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn native_smoke_audit_rejects_stale_commit_sha() {
|
||||||
|
let expected_shader_manifest_hash =
|
||||||
|
"dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c";
|
||||||
|
let reports = [(
|
||||||
|
"macos".to_string(),
|
||||||
|
serde_json::json!({
|
||||||
|
"schema_version": "fparkan-native-smoke-v1",
|
||||||
|
"commit_sha": "fedcba98765432100123456789abcdef01234567",
|
||||||
|
"git_dirty": false,
|
||||||
|
"runner_identity": "github-actions/12345/stage0-macos",
|
||||||
|
"runner_architecture": "aarch64",
|
||||||
|
"rust_toolchain": measured_rust_toolchain_version(),
|
||||||
|
"target_triple": "aarch64-apple-darwin",
|
||||||
|
"platform": "macos",
|
||||||
|
"status": "passed",
|
||||||
|
"frames": 300,
|
||||||
|
"resize_count": 1,
|
||||||
|
"swapchain_recreate_count": 1,
|
||||||
|
"validation_warning_count": 0,
|
||||||
|
"validation_error_count": 0,
|
||||||
|
"shader_manifest_hash": expected_shader_manifest_hash,
|
||||||
|
"vulkan_loader_status": "available",
|
||||||
|
"vulkan_instance_status": "created",
|
||||||
|
"window_status": "created",
|
||||||
|
"vulkan_surface_status": "created",
|
||||||
|
"vulkan_device_status": "selected",
|
||||||
|
"vulkan_device_name": "Apple GPU",
|
||||||
|
"vulkan_logical_device_status": "created",
|
||||||
|
"vulkan_logical_device_graphics_queue_family": 0,
|
||||||
|
"vulkan_logical_device_present_queue_family": 0,
|
||||||
|
"vulkan_logical_device_enabled_extension_count": 1,
|
||||||
|
"vulkan_swapchain_status": "created",
|
||||||
|
"vulkan_swapchain_width": 1280,
|
||||||
|
"vulkan_swapchain_height": 720,
|
||||||
|
"vulkan_swapchain_image_count": 3,
|
||||||
|
"vulkan_portability_enumeration": true,
|
||||||
|
"vulkan_portability_subset_enabled": true
|
||||||
|
}),
|
||||||
|
)]
|
||||||
|
.into_iter()
|
||||||
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
|
||||||
|
let failures = audit_native_smoke_reports(
|
||||||
|
&reports,
|
||||||
|
"0123456789abcdef0123456789abcdef01234567",
|
||||||
|
expected_shader_manifest_hash,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(failures.contains(
|
||||||
|
&"macos: commit_sha expected \"0123456789abcdef0123456789abcdef01234567\", found \"fedcba98765432100123456789abcdef01234567\"".to_string()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn native_smoke_audit_rejects_stale_shader_manifest_hash() {
|
||||||
|
let expected_commit = "0123456789abcdef0123456789abcdef01234567";
|
||||||
|
let reports = [(
|
||||||
|
"macos".to_string(),
|
||||||
|
serde_json::json!({
|
||||||
|
"schema_version": "fparkan-native-smoke-v1",
|
||||||
|
"commit_sha": expected_commit,
|
||||||
|
"git_dirty": false,
|
||||||
|
"runner_identity": "github-actions/12345/stage0-macos",
|
||||||
|
"runner_architecture": "aarch64",
|
||||||
|
"rust_toolchain": measured_rust_toolchain_version(),
|
||||||
|
"target_triple": "aarch64-apple-darwin",
|
||||||
|
"platform": "macos",
|
||||||
|
"status": "passed",
|
||||||
|
"frames": 300,
|
||||||
|
"resize_count": 1,
|
||||||
|
"swapchain_recreate_count": 1,
|
||||||
|
"validation_warning_count": 0,
|
||||||
|
"validation_error_count": 0,
|
||||||
|
"shader_manifest_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
|
"vulkan_loader_status": "available",
|
||||||
|
"vulkan_instance_status": "created",
|
||||||
|
"window_status": "created",
|
||||||
|
"vulkan_surface_status": "created",
|
||||||
|
"vulkan_device_status": "selected",
|
||||||
|
"vulkan_device_name": "Apple GPU",
|
||||||
|
"vulkan_logical_device_status": "created",
|
||||||
|
"vulkan_logical_device_graphics_queue_family": 0,
|
||||||
|
"vulkan_logical_device_present_queue_family": 0,
|
||||||
|
"vulkan_logical_device_enabled_extension_count": 1,
|
||||||
|
"vulkan_swapchain_status": "created",
|
||||||
|
"vulkan_swapchain_width": 1280,
|
||||||
|
"vulkan_swapchain_height": 720,
|
||||||
|
"vulkan_swapchain_image_count": 3,
|
||||||
|
"vulkan_portability_enumeration": true,
|
||||||
|
"vulkan_portability_subset_enabled": true
|
||||||
|
}),
|
||||||
|
)]
|
||||||
|
.into_iter()
|
||||||
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
|
||||||
|
let failures = audit_native_smoke_reports(
|
||||||
|
&reports,
|
||||||
|
expected_commit,
|
||||||
|
"dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(failures.contains(
|
||||||
|
&"macos: shader_manifest_hash expected \"dd293e4ff08ffca1c037900d08b0ffd415db39f238b4fcdde46468fa049b679c\", found \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"".to_string()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn defaults_to_all_stage_and_testdata_root() {
|
fn defaults_to_all_stage_and_testdata_root() {
|
||||||
let args = Vec::new();
|
let args = Vec::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user