diff options
Diffstat (limited to 'adapters/fparkan-render-vulkan/src/ffi/tests.rs')
| -rw-r--r-- | adapters/fparkan-render-vulkan/src/ffi/tests.rs | 326 |
1 files changed, 72 insertions, 254 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi/tests.rs b/adapters/fparkan-render-vulkan/src/ffi/tests.rs index a842c73..b8a3d71 100644 --- a/adapters/fparkan-render-vulkan/src/ffi/tests.rs +++ b/adapters/fparkan-render-vulkan/src/ffi/tests.rs @@ -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() { @@ -391,38 +286,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!( VulkanLoaderError::Unavailable { @@ -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(), + ] ); } @@ -510,20 +379,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"); @@ -531,13 +386,13 @@ fn static_surface_extension_name_is_decoded() { } #[test] -fn swapchain_plan_prefers_srgb_mailbox_and_clamps_extent() { +fn swapchain_plan_prefers_unorm_mailbox_and_clamps_extent() { let plan = plan_vulkan_swapchain(&swapchain_request()).expect("swapchain plan"); assert_eq!( plan.format, VulkanSurfaceFormat { - format: vk::Format::B8G8R8A8_SRGB.as_raw(), + format: vk::Format::R8G8B8A8_UNORM.as_raw(), color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR.as_raw(), } ); @@ -589,7 +444,7 @@ fn swapchain_plan_accepts_undefined_surface_format_by_picking_stage0_default() { assert_eq!( plan.format, VulkanSurfaceFormat { - format: vk::Format::B8G8R8A8_SRGB.as_raw(), + format: vk::Format::R8G8B8A8_UNORM.as_raw(), color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR.as_raw(), } ); @@ -620,128 +475,91 @@ 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"); +fn fragment_shader_dispatches_all_four_material_modes() { + let fragment = include_str!("../../shaders/triangle.frag"); + assert!(fragment.contains("int mode = int(round(material.material_mode));")); + assert!(fragment.contains("mode == 0")); + assert!(fragment.contains("mode == 1")); + assert!(fragment.contains("mode == 2")); + assert!(fragment.contains("mode == 3")); + assert!(!fragment.contains("material_mode > 1.5")); + assert!(!fragment.contains("material_mode > 2.5")); +} - assert_eq!(report.schema, SHADER_MANIFEST_SCHEMA); - assert_eq!(report.target_env, SHADER_TARGET_ENV); - assert_eq!( - report.compiler, - VulkanShaderToolManifest { - name: SHADER_COMPILER_NAME, - version: SHADER_COMPILER_VERSION, - binary_sha256: SHADER_COMPILER_BINARY_SHA256, +#[test] +fn native_specular_reference_uses_squared_power_and_total_overflow_curve() { + fn reference_power(cosine: f32, power: u8) -> f32 { + if power == 0 { + return 0.0; } - ); - assert_eq!( - report.validator, - VulkanShaderToolManifest { - name: SPIRV_VALIDATOR_NAME, - version: SPIRV_VALIDATOR_VERSION, - binary_sha256: SPIRV_VALIDATOR_BINARY_SHA256, + let mut result = cosine; + for _ in 0..power.saturating_sub(1) { + result *= result; } - ); - 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" - ); -} + result + } + fn reference_curve(value: f32) -> f32 { + if value <= 1.0 { + 0.8 * value + } else if value <= 3.0 { + 0.1 * value + 0.7 + } else { + 1.0 + } + } -#[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)); + assert_eq!(reference_power(0.5, 0), 0.0); + assert!((reference_power(0.5, 1) - 0.5).abs() < 1e-6); + assert!((reference_power(0.5, 2) - 0.25).abs() < 1e-6); + assert!((reference_power(0.5, 3) - 0.0625).abs() < 1e-6); + let compressed_diffuse = 1.5; + let material_specular = 0.75; + assert!((reference_curve(material_specular + (compressed_diffuse - 1.0)) - 0.825).abs() < 1e-6); + + let vertex = include_str!("../../shaders/triangle.vert"); + assert!(vertex.contains("result *= result;")); + assert!( + vertex.contains("vec3 total_specular = specular + max(compressed - vec3(1.0), vec3(0.0));") + ); + assert!(vertex.contains("if (power <= 0.0 || cosine <= 0.0)")); } #[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() - ); +fn shader_computes_radial_fog_per_vertex_and_interpolates_it() { + let vertex = include_str!("../../shaders/triangle.vert"); + let fragment = include_str!("../../shaders/triangle.frag"); + assert!(vertex.contains("out_fog_factor = native_fog_factor(in_position);")); + assert!(fragment.contains("layout(location = 4) in float in_fog_factor;")); + assert!(!fragment.contains("distance(in_world_position")); + assert!(!fragment.contains("float fog_factor()")); } #[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]; +fn shader_container_rejects_invalid_spirv() { 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", }) ); |
