feat: add Vulkan instance probe to smoke runner
This commit is contained in:
@@ -77,7 +77,7 @@ jobs:
|
|||||||
--platform "${{ matrix.smoke_platform }}"
|
--platform "${{ matrix.smoke_platform }}"
|
||||||
--out "target/fparkan/native-smoke/${{ runner.os }}.json"
|
--out "target/fparkan/native-smoke/${{ runner.os }}.json"
|
||||||
--status blocked
|
--status blocked
|
||||||
--probe-loader
|
--probe-instance
|
||||||
--reason "native Vulkan smoke runner is not enabled on this CI lane yet"
|
--reason "native Vulkan smoke runner is not enabled on this CI lane yet"
|
||||||
- name: Upload acceptance evidence
|
- name: Upload acceptance evidence
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
//! Native Vulkan smoke runner entrypoint.
|
//! Native Vulkan smoke runner entrypoint.
|
||||||
|
|
||||||
use fparkan_render_vulkan::{
|
use fparkan_render_vulkan::{
|
||||||
probe_vulkan_loader, triangle_shader_manifest, validate_shader_manifest,
|
create_vulkan_instance_probe, probe_vulkan_loader, triangle_shader_manifest,
|
||||||
|
validate_shader_manifest, VulkanInstanceConfig,
|
||||||
};
|
};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -57,6 +58,7 @@ struct SmokeOptions {
|
|||||||
resize_count: u32,
|
resize_count: u32,
|
||||||
validation_error_count: Option<u32>,
|
validation_error_count: Option<u32>,
|
||||||
probe_loader: bool,
|
probe_loader: bool,
|
||||||
|
probe_instance: bool,
|
||||||
reason: Option<String>,
|
reason: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +71,7 @@ impl SmokeOptions {
|
|||||||
let mut resize_count = 0;
|
let mut resize_count = 0;
|
||||||
let mut validation_error_count = None;
|
let mut validation_error_count = None;
|
||||||
let mut probe_loader = false;
|
let mut probe_loader = false;
|
||||||
|
let mut probe_instance = false;
|
||||||
let mut reason = None;
|
let mut reason = None;
|
||||||
let mut iter = args.iter();
|
let mut iter = args.iter();
|
||||||
while let Some(arg) = iter.next() {
|
while let Some(arg) = iter.next() {
|
||||||
@@ -112,6 +115,10 @@ impl SmokeOptions {
|
|||||||
"--probe-loader" => {
|
"--probe-loader" => {
|
||||||
probe_loader = true;
|
probe_loader = true;
|
||||||
}
|
}
|
||||||
|
"--probe-instance" => {
|
||||||
|
probe_loader = true;
|
||||||
|
probe_instance = true;
|
||||||
|
}
|
||||||
"--reason" => {
|
"--reason" => {
|
||||||
let value = iter
|
let value = iter
|
||||||
.next()
|
.next()
|
||||||
@@ -129,6 +136,7 @@ impl SmokeOptions {
|
|||||||
resize_count,
|
resize_count,
|
||||||
validation_error_count,
|
validation_error_count,
|
||||||
probe_loader,
|
probe_loader,
|
||||||
|
probe_instance,
|
||||||
reason,
|
reason,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -144,7 +152,10 @@ fn parse_u32(name: &str, value: &str) -> Result<u32, String> {
|
|||||||
struct VulkanBootstrapProbe {
|
struct VulkanBootstrapProbe {
|
||||||
loader_status: VulkanLoaderStatus,
|
loader_status: VulkanLoaderStatus,
|
||||||
instance_api: Option<String>,
|
instance_api: Option<String>,
|
||||||
error: Option<String>,
|
loader_error: Option<String>,
|
||||||
|
instance_status: VulkanInstanceStatus,
|
||||||
|
instance_error: Option<String>,
|
||||||
|
portability_enumeration: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanBootstrapProbe {
|
impl VulkanBootstrapProbe {
|
||||||
@@ -153,22 +164,47 @@ impl VulkanBootstrapProbe {
|
|||||||
return Self {
|
return Self {
|
||||||
loader_status: VulkanLoaderStatus::Skipped,
|
loader_status: VulkanLoaderStatus::Skipped,
|
||||||
instance_api: None,
|
instance_api: None,
|
||||||
error: None,
|
loader_error: None,
|
||||||
|
instance_status: VulkanInstanceStatus::Skipped,
|
||||||
|
instance_error: None,
|
||||||
|
portability_enumeration: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match probe_vulkan_loader() {
|
let mut probe = match probe_vulkan_loader() {
|
||||||
Ok(report) => Self {
|
Ok(report) => Self {
|
||||||
loader_status: VulkanLoaderStatus::Available,
|
loader_status: VulkanLoaderStatus::Available,
|
||||||
instance_api: Some(format_api_version(report.instance_api_version)),
|
instance_api: Some(format_api_version(report.instance_api_version)),
|
||||||
error: None,
|
loader_error: None,
|
||||||
|
instance_status: VulkanInstanceStatus::Skipped,
|
||||||
|
instance_error: None,
|
||||||
|
portability_enumeration: false,
|
||||||
},
|
},
|
||||||
Err(err) => Self {
|
Err(err) => Self {
|
||||||
loader_status: VulkanLoaderStatus::Unavailable,
|
loader_status: VulkanLoaderStatus::Unavailable,
|
||||||
instance_api: None,
|
instance_api: None,
|
||||||
error: Some(err.to_string()),
|
loader_error: Some(err.to_string()),
|
||||||
|
instance_status: VulkanInstanceStatus::Skipped,
|
||||||
|
instance_error: None,
|
||||||
|
portability_enumeration: false,
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if options.probe_instance && probe.loader_status == VulkanLoaderStatus::Available {
|
||||||
|
let config = VulkanInstanceConfig::smoke("fparkan-vulkan-smoke");
|
||||||
|
probe.portability_enumeration = config.enable_portability_enumeration;
|
||||||
|
match create_vulkan_instance_probe(&config) {
|
||||||
|
Ok(instance) => {
|
||||||
|
probe.instance_status = VulkanInstanceStatus::Created;
|
||||||
|
probe.portability_enumeration = instance.report.create_flags != 0;
|
||||||
}
|
}
|
||||||
|
Err(err) => {
|
||||||
|
probe.instance_status = VulkanInstanceStatus::Failed;
|
||||||
|
probe.instance_error = Some(err.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
probe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,6 +225,23 @@ impl VulkanLoaderStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
enum VulkanInstanceStatus {
|
||||||
|
Skipped,
|
||||||
|
Created,
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VulkanInstanceStatus {
|
||||||
|
const fn as_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Skipped => "skipped",
|
||||||
|
Self::Created => "created",
|
||||||
|
Self::Failed => "failed",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
enum SmokePlatform {
|
enum SmokePlatform {
|
||||||
Windows,
|
Windows,
|
||||||
@@ -271,6 +324,11 @@ fn validate_smoke_options(
|
|||||||
"passed native smoke report requires successful --probe-loader".to_string(),
|
"passed native smoke report requires successful --probe-loader".to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if bootstrap.instance_status != VulkanInstanceStatus::Created {
|
||||||
|
return Err(
|
||||||
|
"passed native smoke report requires successful --probe-instance".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -293,8 +351,12 @@ fn render_smoke_report_json(
|
|||||||
.instance_api
|
.instance_api
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| "null".to_string(), |value| json_string(value));
|
.map_or_else(|| "null".to_string(), |value| json_string(value));
|
||||||
let bootstrap_error = bootstrap
|
let loader_error = bootstrap
|
||||||
.error
|
.loader_error
|
||||||
|
.as_ref()
|
||||||
|
.map_or_else(|| "null".to_string(), |value| json_string(value));
|
||||||
|
let instance_error = bootstrap
|
||||||
|
.instance_error
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| "null".to_string(), |value| json_string(value));
|
.map_or_else(|| "null".to_string(), |value| json_string(value));
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
@@ -311,7 +373,10 @@ fn render_smoke_report_json(
|
|||||||
" \"shader_manifest_hash\": \"{}\",\n",
|
" \"shader_manifest_hash\": \"{}\",\n",
|
||||||
" \"vulkan_loader_status\": \"{}\",\n",
|
" \"vulkan_loader_status\": \"{}\",\n",
|
||||||
" \"vulkan_instance_api\": {},\n",
|
" \"vulkan_instance_api\": {},\n",
|
||||||
" \"vulkan_bootstrap_error\": {},\n",
|
" \"vulkan_loader_error\": {},\n",
|
||||||
|
" \"vulkan_instance_status\": \"{}\",\n",
|
||||||
|
" \"vulkan_instance_error\": {},\n",
|
||||||
|
" \"vulkan_portability_enumeration\": {},\n",
|
||||||
" \"reason\": {}\n",
|
" \"reason\": {}\n",
|
||||||
"}}\n"
|
"}}\n"
|
||||||
),
|
),
|
||||||
@@ -326,7 +391,14 @@ fn render_smoke_report_json(
|
|||||||
json_escape(&shader_manifest.manifest_hash),
|
json_escape(&shader_manifest.manifest_hash),
|
||||||
bootstrap.loader_status.as_str(),
|
bootstrap.loader_status.as_str(),
|
||||||
instance_api,
|
instance_api,
|
||||||
bootstrap_error,
|
loader_error,
|
||||||
|
bootstrap.instance_status.as_str(),
|
||||||
|
instance_error,
|
||||||
|
if bootstrap.portability_enumeration {
|
||||||
|
"true"
|
||||||
|
} else {
|
||||||
|
"false"
|
||||||
|
},
|
||||||
reason
|
reason
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -404,7 +476,10 @@ mod tests {
|
|||||||
&VulkanBootstrapProbe {
|
&VulkanBootstrapProbe {
|
||||||
loader_status: VulkanLoaderStatus::Unavailable,
|
loader_status: VulkanLoaderStatus::Unavailable,
|
||||||
instance_api: None,
|
instance_api: None,
|
||||||
error: Some("Vulkan loader is unavailable".to_string()),
|
loader_error: Some("Vulkan loader is unavailable".to_string()),
|
||||||
|
instance_status: VulkanInstanceStatus::Skipped,
|
||||||
|
instance_error: None,
|
||||||
|
portability_enumeration: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -433,7 +508,10 @@ mod tests {
|
|||||||
&VulkanBootstrapProbe {
|
&VulkanBootstrapProbe {
|
||||||
loader_status: VulkanLoaderStatus::Available,
|
loader_status: VulkanLoaderStatus::Available,
|
||||||
instance_api: Some("1.3.0".to_string()),
|
instance_api: Some("1.3.0".to_string()),
|
||||||
error: None,
|
loader_error: None,
|
||||||
|
instance_status: VulkanInstanceStatus::Created,
|
||||||
|
instance_error: None,
|
||||||
|
portability_enumeration: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Err("passed native smoke report requires --frames >= 300".to_string())
|
Err("passed native smoke report requires --frames >= 300".to_string())
|
||||||
@@ -464,13 +542,51 @@ mod tests {
|
|||||||
&VulkanBootstrapProbe {
|
&VulkanBootstrapProbe {
|
||||||
loader_status: VulkanLoaderStatus::Skipped,
|
loader_status: VulkanLoaderStatus::Skipped,
|
||||||
instance_api: None,
|
instance_api: None,
|
||||||
error: None,
|
loader_error: None,
|
||||||
|
instance_status: VulkanInstanceStatus::Skipped,
|
||||||
|
instance_error: None,
|
||||||
|
portability_enumeration: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Err("passed native smoke report requires successful --probe-loader".to_string())
|
Err("passed native smoke report requires successful --probe-loader".to_string())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejects_passed_without_instance_probe() {
|
||||||
|
let options = SmokeOptions::parse(&strings(&[
|
||||||
|
"--platform",
|
||||||
|
"linux",
|
||||||
|
"--out",
|
||||||
|
"target/native.json",
|
||||||
|
"--status",
|
||||||
|
"passed",
|
||||||
|
"--frames",
|
||||||
|
"300",
|
||||||
|
"--resize-count",
|
||||||
|
"1",
|
||||||
|
"--validation-error-count",
|
||||||
|
"0",
|
||||||
|
"--probe-loader",
|
||||||
|
]))
|
||||||
|
.expect("options");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
validate_smoke_options(
|
||||||
|
&options,
|
||||||
|
&VulkanBootstrapProbe {
|
||||||
|
loader_status: VulkanLoaderStatus::Available,
|
||||||
|
instance_api: Some("1.3.0".to_string()),
|
||||||
|
loader_error: None,
|
||||||
|
instance_status: VulkanInstanceStatus::Skipped,
|
||||||
|
instance_error: None,
|
||||||
|
portability_enumeration: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Err("passed native smoke report requires successful --probe-instance".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn blocked_report_includes_shader_manifest_and_bootstrap_status() -> Result<(), String> {
|
fn blocked_report_includes_shader_manifest_and_bootstrap_status() -> Result<(), String> {
|
||||||
let options = SmokeOptions::parse(&strings(&[
|
let options = SmokeOptions::parse(&strings(&[
|
||||||
@@ -489,7 +605,10 @@ mod tests {
|
|||||||
&VulkanBootstrapProbe {
|
&VulkanBootstrapProbe {
|
||||||
loader_status: VulkanLoaderStatus::Unavailable,
|
loader_status: VulkanLoaderStatus::Unavailable,
|
||||||
instance_api: None,
|
instance_api: None,
|
||||||
error: Some("Vulkan loader is unavailable: dlopen failed".to_string()),
|
loader_error: Some("Vulkan loader is unavailable: dlopen failed".to_string()),
|
||||||
|
instance_status: VulkanInstanceStatus::Skipped,
|
||||||
|
instance_error: None,
|
||||||
|
portability_enumeration: true,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -499,13 +618,32 @@ mod tests {
|
|||||||
assert!(json.contains("\"shader_manifest_hash\": \""));
|
assert!(json.contains("\"shader_manifest_hash\": \""));
|
||||||
assert!(json.contains("\"vulkan_loader_status\": \"unavailable\""));
|
assert!(json.contains("\"vulkan_loader_status\": \"unavailable\""));
|
||||||
assert!(json.contains("\"vulkan_instance_api\": null"));
|
assert!(json.contains("\"vulkan_instance_api\": null"));
|
||||||
assert!(json.contains(
|
assert!(json
|
||||||
"\"vulkan_bootstrap_error\": \"Vulkan loader is unavailable: dlopen failed\""
|
.contains("\"vulkan_loader_error\": \"Vulkan loader is unavailable: dlopen failed\""));
|
||||||
));
|
assert!(json.contains("\"vulkan_instance_status\": \"skipped\""));
|
||||||
|
assert!(json.contains("\"vulkan_instance_error\": null"));
|
||||||
|
assert!(json.contains("\"vulkan_portability_enumeration\": true"));
|
||||||
assert!(json.contains("\"reason\": \"runner unavailable\""));
|
assert!(json.contains("\"reason\": \"runner unavailable\""));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_instance_probe_as_loader_probe() -> Result<(), String> {
|
||||||
|
let options = SmokeOptions::parse(&strings(&[
|
||||||
|
"--platform",
|
||||||
|
"linux",
|
||||||
|
"--out",
|
||||||
|
"target/native.json",
|
||||||
|
"--probe-instance",
|
||||||
|
"--reason",
|
||||||
|
"runner unavailable",
|
||||||
|
]))?;
|
||||||
|
|
||||||
|
assert!(options.probe_loader);
|
||||||
|
assert!(options.probe_instance);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn formats_vulkan_api_version() {
|
fn formats_vulkan_api_version() {
|
||||||
assert_eq!(format_api_version((1 << 22) | (3 << 12) | 280), "1.3.280");
|
assert_eq!(format_api_version((1 << 22) | (3 << 12) | 280), "1.3.280");
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ S0-VK-021 covered cargo test -p fparkan-render-vulkan --offline frame_submission
|
|||||||
S0-VK-022 covered cargo test -p fparkan-render-vulkan --offline backend_tracks_render_request_and_presents
|
S0-VK-022 covered cargo test -p fparkan-render-vulkan --offline backend_tracks_render_request_and_presents
|
||||||
S0-VK-023 covered cargo test -p fparkan-vulkan-smoke --offline rejects_false_pass_without_full_evidence blocked_report_includes_shader_manifest_and_bootstrap_status
|
S0-VK-023 covered cargo test -p fparkan-vulkan-smoke --offline rejects_false_pass_without_full_evidence blocked_report_includes_shader_manifest_and_bootstrap_status
|
||||||
S0-VK-024 covered cargo test -p fparkan-vulkan-smoke --offline rejects_passed_without_loader_probe formats_vulkan_api_version
|
S0-VK-024 covered cargo test -p fparkan-vulkan-smoke --offline rejects_passed_without_loader_probe formats_vulkan_api_version
|
||||||
|
S0-VK-025 covered cargo test -p fparkan-vulkan-smoke --offline rejects_passed_without_instance_probe parses_instance_probe_as_loader_probe
|
||||||
S0-LIMIT-001 covered cargo test -p fparkan-binary --offline rejects_count_stride_overflow
|
S0-LIMIT-001 covered cargo test -p fparkan-binary --offline rejects_count_stride_overflow
|
||||||
S0-LIMIT-002 covered cargo test -p fparkan-binary --offline rejects_oversized_declared_allocation_before_read
|
S0-LIMIT-002 covered cargo test -p fparkan-binary --offline rejects_oversized_declared_allocation_before_read
|
||||||
L1-P1-NRES-001 covered cargo test -p fparkan-nres --offline licensed_corpora_nres_roundtrip_gates
|
L1-P1-NRES-001 covered cargo test -p fparkan-nres --offline licensed_corpora_nres_roundtrip_gates
|
||||||
|
|||||||
|
@@ -52,6 +52,7 @@
|
|||||||
`S0-VK-022`
|
`S0-VK-022`
|
||||||
`S0-VK-023`
|
`S0-VK-023`
|
||||||
`S0-VK-024`
|
`S0-VK-024`
|
||||||
|
`S0-VK-025`
|
||||||
`S0-LIMIT-001`
|
`S0-LIMIT-001`
|
||||||
`S0-LIMIT-002`
|
`S0-LIMIT-002`
|
||||||
`L1-P1-NRES-001`
|
`L1-P1-NRES-001`
|
||||||
|
|||||||
Reference in New Issue
Block a user