chore: simplify project to engine docs and tests

Remove planning and acceptance scaffolding while retaining the native Vulkan mission preview, format readers, runtime algorithms, and ordinary Rust tests. Keep the book aligned with the runnable project and validate checked-in shaders without generated tool metadata.
This commit is contained in:
Valentin Popov committed 2026-09-06 05:22:12 +04:00
1 parent da03e77f53
commit aa51f3574d
167 files changed
+667 -23705

No files matched your search

@@ -1,7 +1,6 @@
#![allow(unsafe_code)]
use ash::vk;
use serde::Serialize;
use std::collections::BTreeSet;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
@@ -10,7 +9,6 @@ use super::{
EXT_DEBUG_UTILS_EXTENSION, KHR_PORTABILITY_ENUMERATION_EXTENSION, MIN_VULKAN_API_VERSION,
VALIDATION_LAYER_NAME,
};
use crate::policy::{format_api_version, serialize_json_or_fallback};
/// Vulkan instance bootstrap configuration.
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -236,28 +234,6 @@ pub fn create_vulkan_instance_probe(
})
}
/// Renders a deterministic JSON Vulkan instance plan.
#[must_use]
pub fn render_instance_plan_json(plan: &VulkanInstancePlan) -> String {
#[derive(Serialize)]
struct InstancePlanJson<'a> {
schema: u32,
create_flags: u32,
validation_requested: bool,
enabled_extensions: &'a [String],
}
serialize_json_or_fallback(
&InstancePlanJson {
schema: plan.schema,
create_flags: plan.create_flags,
validation_requested: plan.validation_requested,
enabled_extensions: &plan.enabled_extensions,
},
"{\"schema\":0,\"create_flags\":0,\"validation_requested\":false,\"enabled_extensions\":[]}",
)
}
/// Opens the Vulkan loader and reports the supported instance API version.
///
/// # Errors
@@ -288,26 +264,6 @@ pub fn vulkan_entry_symbol_name() -> &'static CStr {
c"vkGetInstanceProcAddr"
}
/// Renders a deterministic JSON Vulkan loader report.
#[must_use]
pub fn render_loader_probe_report_json(report: &VulkanLoaderProbeReport) -> String {
#[derive(Serialize)]
struct LoaderProbeReportJson {
schema: u32,
loader_available: bool,
instance_api: String,
}
serialize_json_or_fallback(
&LoaderProbeReportJson {
schema: report.schema,
loader_available: report.loader_available,
instance_api: format_api_version(report.instance_api_version),
},
"{\"schema\":0,\"loader_available\":false,\"instance_api\":\"0.0.0\"}",
)
}
fn available_instance_extensions(entry: &ash::Entry) -> Result<Vec<String>, VulkanInstanceError> {
let available_extensions =
// SAFETY: Enumerating instance extensions reads loader-owned immutable metadata.
@@ -26,7 +26,6 @@ pub(super) struct VulkanDepthAttachment {
pub(super) format: vk::Format,
}
#[allow(clippy::too_many_lines)]
pub(super) fn create_depth_attachment(
instance: &VulkanInstanceProbe,
device: &VulkanLogicalDeviceProbe,
@@ -249,7 +248,6 @@ pub(super) fn create_static_mesh_index_buffer(
)
}
#[allow(clippy::too_many_lines)]
pub(super) fn create_static_texture_image(
instance: &VulkanInstanceProbe,
device: &VulkanLogicalDeviceProbe,
@@ -376,7 +374,6 @@ pub(super) fn create_static_texture_image(
})
}
#[allow(clippy::too_many_lines)]
fn upload_static_texture(
device: &VulkanLogicalDeviceProbe,
command_pool: vk::CommandPool,
@@ -17,7 +17,7 @@ use super::{
VulkanValidationReport,
};
use crate::policy::KHR_PORTABILITY_SUBSET_EXTENSION;
use crate::shader_manifest::{triangle_shader_manifest, validate_shader_manifest};
use crate::shader::validate_triangle_shaders;
#[cfg(test)]
fn take_runtime_owners_in_dependency_order<Instance, Validation, Surface, Device, Swapchain>(
@@ -103,7 +103,7 @@ impl VulkanSmokeRenderer {
///
/// Returns [`VulkanSmokeRendererError`] when Vulkan bootstrap, pipeline creation,
/// memory allocation, or synchronization resource creation fails.
#[allow(clippy::too_many_lines)]
pub fn new(
create_info: &VulkanSmokeRendererCreateInfo,
) -> Result<Self, VulkanSmokeRendererError> {
@@ -120,8 +120,7 @@ impl VulkanSmokeRenderer {
resolve_draw_texture_indices(&create_info.mesh.draw_ranges, &create_info.materials)
.map_err(|context| VulkanSmokeRendererError::InvalidStaticMesh { context })?;
let bootstrap_progress = create_info.bootstrap_progress.as_ref();
let shader_manifest = validate_shader_manifest(&triangle_shader_manifest())
.map_err(VulkanSmokeRendererError::ShaderManifest)?;
validate_triangle_shaders().map_err(VulkanSmokeRendererError::Shader)?;
let surface_plan = plan_vulkan_surface(Some(create_info.native_handles))
.map_err(VulkanSmokeRendererError::Surface)?;
let mut instance_config = VulkanInstanceConfig::smoke(&create_info.application_name);
@@ -240,7 +239,6 @@ impl VulkanSmokeRenderer {
pending_extent: None,
swapchain_recreate_count: 0,
report: VulkanSmokeRendererReport {
shader_manifest_hash: shader_manifest.manifest_hash.clone(),
portability_enumeration: instance_config.enable_portability_enumeration,
portability_subset_enabled: false,
device_name: String::new(),
@@ -260,7 +258,6 @@ impl VulkanSmokeRenderer {
let device_ref = renderer.device_ref()?;
let swapchain_ref = renderer.swapchain_ref()?;
renderer.report = VulkanSmokeRendererReport {
shader_manifest_hash: shader_manifest.manifest_hash,
portability_enumeration: renderer
.instance
.as_ref()
@@ -426,7 +423,7 @@ impl VulkanSmokeRenderer {
///
/// Returns [`VulkanSmokeRendererError`] when synchronization, command recording,
/// submission, or presentation fails.
#[allow(clippy::too_many_lines)]
pub fn draw_frame(&mut self) -> Result<VulkanSmokeFrameOutcome, VulkanSmokeRendererError> {
if let Some(extent) = self.pending_extent.take() {
if extent.0 == 0 || extent.1 == 0 {
@@ -632,7 +629,6 @@ impl VulkanSmokeRenderer {
Ok(())
}
#[allow(clippy::too_many_lines)]
fn record_command_buffer(
&mut self,
image_index: usize,
@@ -10,7 +10,7 @@ use super::{
VulkanSurfaceProbe, VulkanSwapchainProbe, VulkanSwapchainProbeError, VulkanSwapchainResources,
VulkanValidationMessenger,
};
use crate::shader_manifest::VulkanShaderManifestError;
use crate::shader::VulkanShaderError;
/// Creates a live native Vulkan renderer for the Stage 0 smoke loop.
#[derive(Clone, Debug)]
@@ -536,7 +536,7 @@ impl VulkanSmokeBootstrapProgress {
/// Stable snapshot of measured bootstrap progress.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[allow(clippy::struct_excessive_bools)]
pub struct VulkanSmokeBootstrapSnapshot {
/// Whether the Vulkan loader was resolved.
pub loader_available: bool,
@@ -562,8 +562,6 @@ const BOOTSTRAP_SWAPCHAIN_CREATED: u8 = 1 << 5;
/// Stable smoke renderer bootstrap report.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VulkanSmokeRendererReport {
/// Checked-in shader manifest hash used by the renderer.
pub shader_manifest_hash: String,
/// Whether portability enumeration was enabled at instance creation.
pub portability_enumeration: bool,
/// Whether the logical device enabled `VK_KHR_portability_subset`.
@@ -652,8 +650,8 @@ pub enum VulkanSmokeRendererError {
LogicalDevice(VulkanLogicalDeviceError),
/// Swapchain bootstrap failed.
Swapchain(VulkanSwapchainProbeError),
/// Shader manifest validation failed.
ShaderManifest(VulkanShaderManifestError),
/// Checked-in shader validation failed.
Shader(VulkanShaderError),
/// Vulkan operation failed.
VulkanOperation {
/// Operation context.
@@ -695,7 +693,7 @@ impl std::fmt::Display for VulkanSmokeRendererError {
Self::Surface(error) => write!(f, "{error}"),
Self::LogicalDevice(error) => write!(f, "{error}"),
Self::Swapchain(error) => write!(f, "{error}"),
Self::ShaderManifest(error) => write!(f, "{error}"),
Self::Shader(error) => write!(f, "{error}"),
Self::VulkanOperation { context, result } => {
write!(f, "{context}: {result:?}")
}
@@ -2,12 +2,10 @@
use ash::{khr::surface, vk};
use fparkan_platform::NativeWindowHandles;
use serde::Serialize;
use std::ffi::CStr;
use std::os::raw::c_char;
use super::VulkanInstanceProbe;
use crate::policy::serialize_json_or_fallback;
/// Deterministic Vulkan surface creation plan.
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -132,24 +130,6 @@ pub fn create_vulkan_surface_probe(
})
}
/// Renders a deterministic JSON Vulkan surface plan.
#[must_use]
pub fn render_surface_plan_json(plan: &VulkanSurfacePlan) -> String {
#[derive(Serialize)]
struct SurfacePlanJson<'a> {
schema: u32,
required_instance_extensions: &'a [String],
}
serialize_json_or_fallback(
&SurfacePlanJson {
schema: plan.schema,
required_instance_extensions: &plan.required_instance_extensions,
},
"{\"schema\":0,\"required_instance_extensions\":[]}",
)
}
pub(super) fn extension_name(extension: *const c_char) -> Result<String, VulkanSurfaceError> {
// SAFETY: `ash-window` returns extension pointers to static NUL-terminated Vulkan names.
let name = unsafe { CStr::from_ptr(extension) };
@@ -45,7 +45,6 @@ struct PartialSwapchainResources {
command_buffers: Vec<vk::CommandBuffer>,
}
#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
pub(super) fn create_swapchain_resources(
instance: &VulkanInstanceProbe,
device: &VulkanLogicalDeviceProbe,
@@ -205,7 +204,6 @@ fn create_swapchain_image_views(
Ok(image_views)
}
#[allow(clippy::type_complexity)]
fn create_swapchain_pipeline_bundle(
instance: &VulkanInstanceProbe,
device: &VulkanLogicalDeviceProbe,
@@ -433,7 +431,6 @@ fn create_pipeline_layout(
})
}
#[allow(clippy::too_many_lines)]
fn create_texture_descriptor_bundle(
device: &VulkanLogicalDeviceProbe,
textures: &[VulkanAllocatedImage],
@@ -554,12 +551,10 @@ fn create_texture_descriptor_bundle(
Ok((layout, pool, descriptor_sets, sampler))
}
#[allow(clippy::cast_precision_loss)]
fn extent_component_to_f32(value: u32) -> f32 {
value as f32
}
#[allow(clippy::too_many_lines)]
fn create_graphics_pipeline_cache(
device: &VulkanLogicalDeviceProbe,
render_pass: vk::RenderPass,
@@ -596,7 +591,6 @@ fn create_graphics_pipeline_cache(
Ok(pipelines)
}
#[allow(clippy::too_many_lines)]
fn create_graphics_pipeline(
device: &VulkanLogicalDeviceProbe,
render_pass: vk::RenderPass,
+19 -261
View File
@@ -1,114 +1,9 @@
use super::*;
use crate::ffi::{SPIRV_MAGIC, SPIRV_VERSION_1_0};
use crate::policy::{KHR_PORTABILITY_SUBSET_EXTENSION, KHR_SWAPCHAIN_EXTENSION};
use crate::shader_manifest::{
SHADER_COMPILER_BINARY_SHA256, SHADER_COMPILER_NAME, SHADER_COMPILER_VERSION,
SHADER_MANIFEST_SCHEMA, SHADER_TARGET_ENV, SPIRV_MAGIC, SPIRV_VALIDATOR_BINARY_SHA256,
SPIRV_VALIDATOR_NAME, SPIRV_VALIDATOR_VERSION, SPIRV_VERSION_1_0,
TRIANGLE_VERTEX_COMPILE_COMMAND, TRIANGLE_VERTEX_SOURCE_PATH, TRIANGLE_VERTEX_SOURCE_SHA256,
TRIANGLE_VERTEX_SPIRV_PATH, TRIANGLE_VERTEX_VALIDATE_COMMAND,
};
use crate::shader::{validate_spirv_container, validate_triangle_shaders, VulkanShaderError};
use crate::*;
use fparkan_platform::{DepthStencilSupport, RenderRequest};
use fparkan_render::{
DrawCommand, DrawId, GpuMaterialId, GpuMeshId, IndexRange, LegacyPipelineState, RenderCommand,
RenderPhase,
};
use fparkan_render::{RenderBackend, RenderError};
#[test]
fn planning_backend_tracks_render_request_and_simulated_present() -> Result<(), RenderError> {
let mut backend = VulkanPlanningBackend::new();
let request = RenderRequest {
presentation: fparkan_platform::PresentationMode::Immediate,
..RenderRequest::conservative()
};
backend.set_render_request(request);
assert_eq!(backend.render_request(), request);
assert_eq!(backend.report().request.current_request, request);
assert_eq!(backend.report().request.request_updates, 1);
let commands = fparkan_render::RenderCommandList {
commands: vec![
RenderCommand::BeginFrame,
RenderCommand::Draw(DrawCommand {
id: DrawId(11),
phase: RenderPhase::Opaque,
object_id: None,
mesh: GpuMeshId(1),
material: GpuMaterialId(2),
pipeline_key: LegacyPipelineState::default().into(),
transform: [1.0; 16],
range: IndexRange { start: 0, count: 3 },
stable_order: 7,
}),
RenderCommand::EndFrame,
],
};
backend.execute(&commands)?;
assert_eq!(backend.state(), VulkanPlanningBackendState::Configured);
assert_eq!(backend.report().execution.planned_frames, 1);
assert_eq!(backend.report().execution.submission_plans, 1);
assert_eq!(backend.report().execution.simulated_presents, 1);
assert!(backend.report().execution.last_capture_size > 0);
assert_eq!(
backend.report().last_frame_submission,
Some(VulkanFrameSubmissionPlan {
schema: 1,
frames_in_flight: 2,
command_buffers: 2,
semaphores_per_frame: 2,
fences_per_frame: 1,
draw_count: 1,
indexed_vertex_count: 3,
})
);
Ok(())
}
#[test]
fn frame_submission_plan_json_is_stable() -> Result<(), RenderError> {
let commands = fparkan_render::RenderCommandList {
commands: vec![
RenderCommand::BeginFrame,
RenderCommand::Draw(DrawCommand {
id: DrawId(11),
phase: RenderPhase::Opaque,
object_id: None,
mesh: GpuMeshId(1),
material: GpuMaterialId(2),
pipeline_key: LegacyPipelineState::default().into(),
transform: [1.0; 16],
range: IndexRange { start: 0, count: 3 },
stable_order: 7,
}),
RenderCommand::EndFrame,
],
};
let swapchain = VulkanSwapchainPlan {
schema: 1,
extent: (1, 1),
format: VulkanSurfaceFormat {
format: vk::Format::B8G8R8A8_SRGB.as_raw(),
color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR.as_raw(),
},
present_mode: vk::PresentModeKHR::FIFO.as_raw(),
image_count: 3,
image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT.as_raw(),
};
let plan = plan_vulkan_frame_submission(&swapchain, &commands)?;
assert_eq!(plan.frames_in_flight, 2);
assert_eq!(plan.command_buffers, 3);
assert_eq!(plan.draw_count, 1);
assert_eq!(plan.indexed_vertex_count, 3);
assert_eq!(
render_frame_submission_plan_json(&plan),
"{\"schema\":1,\"frames_in_flight\":2,\"command_buffers\":3,\"semaphores_per_frame\":2,\"fences_per_frame\":1,\"draw_count\":1,\"indexed_vertex_count\":3}"
);
Ok(())
}
#[test]
fn device_scoring_is_deterministic_and_prefers_discrete_unified_queue() {
@@ -390,38 +285,6 @@ fn capability_report_preserves_informational_sampled_formats_and_limits() {
);
}
#[test]
fn capability_report_json_is_stable() {
let mut rejected = device("Rejected", VulkanDeviceType::IntegratedGpu, 0, true, false);
rejected.present_modes.clear();
let report = select_physical_device(&[
rejected,
device("GPU \"A\"", VulkanDeviceType::DiscreteGpu, 3, true, false),
])
.expect("selected device");
assert_eq!(
render_capability_report_json(&report),
"{\"schema\":1,\"vulkan_api\":\"1.1.0\",\"device_name\":\"GPU \\\"A\\\"\",\"score\":1101,\"graphics_queue_family\":3,\"present_queue_family\":3,\"portability_subset\":false,\"enabled_extensions\":[\"VK_KHR_swapchain\"],\"informational_capabilities\":{\"sampled_color_formats\":[50],\"sampled_depth_formats\":[126],\"limits\":{\"max_image_dimension_2d\":4096,\"max_sampler_allocation_count\":4096,\"max_per_stage_descriptor_samplers\":16,\"max_bound_descriptor_sets\":4}},\"rejected_devices\":[{\"device_name\":\"Rejected\",\"reason_code\":\"missing_present_mode\",\"reason\":\"Vulkan device Rejected has no supported present mode\"}]}"
);
}
#[test]
fn loader_probe_report_json_is_stable() {
assert_eq!(
vulkan_entry_symbol_name().to_bytes(),
b"vkGetInstanceProcAddr"
);
assert_eq!(
render_loader_probe_report_json(&VulkanLoaderProbeReport {
schema: 1,
loader_available: true,
instance_api_version: vk::API_VERSION_1_2,
}),
"{\"schema\":1,\"loader_available\":true,\"instance_api\":\"1.2.0\"}"
);
}
#[test]
fn loader_error_display_is_actionable() {
assert_eq!(
@@ -446,9 +309,15 @@ fn instance_plan_is_sorted_deduplicated_and_portability_aware() {
enable_validation: true,
});
assert_eq!(plan.create_flags, 1);
assert!(plan.validation_requested);
assert_eq!(
render_instance_plan_json(&plan),
"{\"schema\":1,\"create_flags\":1,\"validation_requested\":true,\"enabled_extensions\":[\"VK_EXT_debug_utils\",\"VK_KHR_portability_enumeration\",\"VK_KHR_surface\"]}"
plan.enabled_extensions,
vec![
"VK_EXT_debug_utils".to_string(),
KHR_PORTABILITY_ENUMERATION_EXTENSION.to_string(),
"VK_KHR_surface".to_string(),
]
);
}
@@ -509,20 +378,6 @@ fn surface_plan_requires_native_handles() {
);
}
#[test]
fn surface_plan_json_is_stable() {
assert_eq!(
render_surface_plan_json(&VulkanSurfacePlan {
schema: 1,
required_instance_extensions: vec![
"VK_KHR_surface".to_string(),
"VK_EXT_metal_surface".to_string(),
],
}),
"{\"schema\":1,\"required_instance_extensions\":[\"VK_KHR_surface\",\"VK_EXT_metal_surface\"]}"
);
}
#[test]
fn static_surface_extension_name_is_decoded() {
let name = extension_name(ash::khr::surface::NAME.as_ptr()).expect("extension name");
@@ -620,128 +475,31 @@ fn swapchain_plan_rejects_missing_surface_data_and_empty_extent() {
}
#[test]
fn swapchain_plan_json_and_recreation_reports_are_stable() {
let plan = plan_vulkan_swapchain(&swapchain_request()).expect("swapchain plan");
assert_eq!(
render_swapchain_plan_json(&plan),
"{\"schema\":1,\"extent\":[1024,720],\"format\":50,\"color_space\":0,\"present_mode\":1,\"image_count\":3,\"image_usage\":16}"
);
let report = swapchain_recreation_report(
VulkanSwapchainRecreationReason::OutOfDate,
(1024, 720),
(1280, 720),
);
assert_eq!(
render_swapchain_recreation_report_json(&report),
"{\"schema\":1,\"reason\":\"out_of_date\",\"previous_extent\":[1024,720],\"next_extent\":[1280,720]}"
);
fn checked_in_shaders_have_valid_spirv_containers() {
assert_eq!(validate_triangle_shaders(), Ok(()));
}
#[test]
fn triangle_shader_manifest_hashes_are_stable() {
let report = validate_shader_manifest(&triangle_shader_manifest()).expect("shader manifest");
assert_eq!(report.schema, SHADER_MANIFEST_SCHEMA);
assert_eq!(report.target_env, SHADER_TARGET_ENV);
fn shader_container_rejects_invalid_spirv() {
assert_eq!(
report.compiler,
VulkanShaderToolManifest {
name: SHADER_COMPILER_NAME,
version: SHADER_COMPILER_VERSION,
binary_sha256: SHADER_COMPILER_BINARY_SHA256,
}
);
assert_eq!(
report.validator,
VulkanShaderToolManifest {
name: SPIRV_VALIDATOR_NAME,
version: SPIRV_VALIDATOR_VERSION,
binary_sha256: SPIRV_VALIDATOR_BINARY_SHA256,
}
);
assert_eq!(report.modules.len(), 2);
assert_eq!(report.modules[0].name, "triangle.vert");
assert_eq!(report.modules[0].stage, VulkanShaderStage::Vertex);
assert_eq!(report.modules[0].source_path, TRIANGLE_VERTEX_SOURCE_PATH);
assert_eq!(
report.modules[0].source_sha256,
TRIANGLE_VERTEX_SOURCE_SHA256
);
assert_eq!(report.modules[0].spirv_path, TRIANGLE_VERTEX_SPIRV_PATH);
assert_eq!(report.modules[0].word_count, 358);
assert_eq!(
report.modules[0].sha256,
"4e2051ac43b933a57e5557e596bcc95952b4efbfcb45315180f89581e094398d"
);
assert_eq!(report.modules[0].descriptor_sets, 0);
assert_eq!(report.modules[0].push_constant_bytes, 64);
assert_eq!(
report.modules[0].compile_command,
TRIANGLE_VERTEX_COMPILE_COMMAND
);
assert_eq!(
report.modules[0].validate_command,
TRIANGLE_VERTEX_VALIDATE_COMMAND
);
assert!(!report.modules[0].interface_hash.is_empty());
assert_eq!(
report.modules[1].sha256,
"8ab7bf835e166892b04b10fa1100258daf355d2f693ad311d014dbee22de5c7b"
);
assert_eq!(
report.manifest_hash,
"cf471972978ddb5c8919711ad64c93d0d64e18b1f590a89ce014f1e2a743cfb5"
);
}
#[test]
fn shader_manifest_report_json_is_stable() {
let report = validate_shader_manifest(&triangle_shader_manifest()).expect("shader manifest");
let json = render_shader_manifest_report_json(&report);
assert!(json.contains(SHADER_COMPILER_NAME));
assert!(json.contains(SPIRV_VALIDATOR_NAME));
assert!(json.contains(TRIANGLE_VERTEX_SOURCE_PATH));
assert!(json.contains(TRIANGLE_VERTEX_COMPILE_COMMAND));
}
#[test]
fn checked_in_shader_manifest_matches_generated_report() {
let report = validate_shader_manifest(&triangle_shader_manifest()).expect("shader manifest");
assert_eq!(
render_shader_manifest_report_json(&report),
include_str!("../../shaders/manifest.json").trim()
);
}
#[test]
fn shader_manifest_rejects_invalid_spirv_containers() {
let mut module = triangle_shader_manifest().remove(0);
module.words = &[0xFFFF_FFFF, SPIRV_VERSION_1_0, 0, 1, 0];
assert_eq!(
validate_shader_manifest(&[module]),
Err(VulkanShaderManifestError::InvalidMagic {
validate_spirv_container("triangle.vert", &[0xFFFF_FFFF, SPIRV_VERSION_1_0, 0, 1, 0]),
Err(VulkanShaderError::InvalidMagic {
name: "triangle.vert",
found: 0xFFFF_FFFF,
})
);
let mut module = triangle_shader_manifest().remove(0);
module.words = &[SPIRV_MAGIC, 0, 0, 1, 0];
assert_eq!(
validate_shader_manifest(&[module]),
Err(VulkanShaderManifestError::UnsupportedVersion {
validate_spirv_container("triangle.vert", &[SPIRV_MAGIC, 0, 0, 1, 0]),
Err(VulkanShaderError::UnsupportedVersion {
name: "triangle.vert",
found: 0,
})
);
let mut module = triangle_shader_manifest().remove(0);
module.words = &[SPIRV_MAGIC, SPIRV_VERSION_1_0, 0, 0, 0];
assert_eq!(
validate_shader_manifest(&[module]),
Err(VulkanShaderManifestError::InvalidBound {
validate_spirv_container("triangle.vert", &[SPIRV_MAGIC, SPIRV_VERSION_1_0, 0, 0, 0]),
Err(VulkanShaderError::InvalidBound {
name: "triangle.vert",
})
);