aboutsummaryrefslogtreecommitdiff
path: root/adapters/fparkan-render-vulkan/src
diff options
context:
space:
mode:
Diffstat (limited to 'adapters/fparkan-render-vulkan/src')
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi.rs9
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/resources.rs30
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke.rs47
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs110
4 files changed, 156 insertions, 40 deletions
diff --git a/adapters/fparkan-render-vulkan/src/ffi.rs b/adapters/fparkan-render-vulkan/src/ffi.rs
index 0406544..0b6f1f2 100644
--- a/adapters/fparkan-render-vulkan/src/ffi.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi.rs
@@ -50,9 +50,9 @@ pub use self::instance::{
#[cfg(test)]
use self::instance::{cstring_vec, ensure_instance_extensions_available};
use self::resources::{
- color_subresource_range, create_command_pool, create_frame_sync, create_triangle_index_buffer,
- create_triangle_vertex_buffer, destroy_allocated_buffer, VulkanAllocatedBuffer,
- VulkanFrameSync,
+ color_subresource_range, create_command_pool, create_frame_sync,
+ create_static_mesh_index_buffer, create_static_mesh_vertex_buffer, destroy_allocated_buffer,
+ VulkanAllocatedBuffer, VulkanFrameSync,
};
pub use self::runtime::{
create_vulkan_logical_device_probe, create_vulkan_logical_device_probe_for_request,
@@ -61,7 +61,8 @@ pub use self::runtime::{
pub use self::smoke_types::{
VulkanSmokeBootstrapProgress, VulkanSmokeBootstrapSnapshot, VulkanSmokeFrameOutcome,
VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, VulkanSmokeRendererError,
- VulkanSmokeRendererReport, VulkanSmokeShutdownReport, VulkanValidationReport,
+ VulkanSmokeRendererReport, VulkanSmokeShutdownReport, VulkanStaticMesh, VulkanStaticVertex,
+ VulkanValidationReport,
};
#[cfg(test)]
use self::surface::extension_name;
diff --git a/adapters/fparkan-render-vulkan/src/ffi/resources.rs b/adapters/fparkan-render-vulkan/src/ffi/resources.rs
index c6538d9..e5d725b 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/resources.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/resources.rs
@@ -2,7 +2,9 @@
use ash::vk;
-use super::{VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanSmokeRendererError};
+use super::{
+ VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanSmokeRendererError, VulkanStaticMesh,
+};
pub(super) struct VulkanAllocatedBuffer {
pub(super) buffer: vk::Buffer,
@@ -30,18 +32,14 @@ pub(super) fn create_command_pool(
})
}
-pub(super) fn create_triangle_vertex_buffer(
+pub(super) fn create_static_mesh_vertex_buffer(
instance: &VulkanInstanceProbe,
device: &VulkanLogicalDeviceProbe,
+ mesh: &VulkanStaticMesh,
) -> Result<VulkanAllocatedBuffer, VulkanSmokeRendererError> {
- let vertices: [[f32; 5]; 3] = [
- [0.0, -0.55, 1.0, 0.2, 0.2],
- [0.55, 0.55, 0.2, 1.0, 0.2],
- [-0.55, 0.55, 0.2, 0.4, 1.0],
- ];
- let mut bytes = Vec::with_capacity(vertices.len() * 5 * std::mem::size_of::<f32>());
- for vertex in vertices {
- for value in vertex {
+ let mut bytes = Vec::with_capacity(mesh.vertices.len() * 5 * std::mem::size_of::<f32>());
+ for vertex in &mesh.vertices {
+ for value in vertex.position.into_iter().chain(vertex.color) {
bytes.extend_from_slice(&value.to_ne_bytes());
}
}
@@ -50,17 +48,17 @@ pub(super) fn create_triangle_vertex_buffer(
device,
&bytes,
vk::BufferUsageFlags::VERTEX_BUFFER,
- "triangle vertex buffer",
+ "static mesh vertex buffer",
)
}
-pub(super) fn create_triangle_index_buffer(
+pub(super) fn create_static_mesh_index_buffer(
instance: &VulkanInstanceProbe,
device: &VulkanLogicalDeviceProbe,
+ mesh: &VulkanStaticMesh,
) -> Result<VulkanAllocatedBuffer, VulkanSmokeRendererError> {
- let indices = [0_u16, 1_u16, 2_u16];
- let mut bytes = Vec::with_capacity(indices.len() * std::mem::size_of::<u16>());
- for index in indices {
+ let mut bytes = Vec::with_capacity(mesh.indices.len() * std::mem::size_of::<u16>());
+ for &index in &mesh.indices {
bytes.extend_from_slice(&index.to_ne_bytes());
}
create_host_visible_buffer(
@@ -68,7 +66,7 @@ pub(super) fn create_triangle_index_buffer(
device,
&bytes,
vk::BufferUsageFlags::INDEX_BUFFER,
- "triangle index buffer",
+ "static mesh index buffer",
)
}
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
index afb5b2a..06f1182 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
@@ -3,8 +3,8 @@
use ash::vk;
use super::{
- create_command_pool, create_frame_sync, create_swapchain_resources,
- create_triangle_index_buffer, create_triangle_vertex_buffer, create_validation_messenger,
+ create_command_pool, create_frame_sync, create_static_mesh_index_buffer,
+ create_static_mesh_vertex_buffer, create_swapchain_resources, create_validation_messenger,
create_vulkan_instance_probe, create_vulkan_logical_device_probe_for_request,
create_vulkan_surface_probe, create_vulkan_swapchain_probe_for_extent,
destroy_allocated_buffer, destroy_swapchain_resources, plan_vulkan_surface,
@@ -105,6 +105,10 @@ impl VulkanSmokeRenderer {
pub fn new(
create_info: &VulkanSmokeRendererCreateInfo,
) -> Result<Self, VulkanSmokeRendererError> {
+ create_info
+ .mesh
+ .validate()
+ .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)?;
@@ -153,23 +157,25 @@ impl VulkanSmokeRenderer {
progress.mark_swapchain_created();
}
let command_pool = create_command_pool(&device)?;
- let vertex_buffer = match create_triangle_vertex_buffer(&instance, &device) {
- Ok(buffer) => buffer,
- Err(error) => {
- // SAFETY: The command pool belongs to this live logical device and is destroyed on setup failure.
- unsafe { device.device().destroy_command_pool(command_pool, None) };
- return Err(error);
- }
- };
- let index_buffer = match create_triangle_index_buffer(&instance, &device) {
- Ok(buffer) => buffer,
- Err(error) => {
- // SAFETY: The command pool belongs to this live logical device and is destroyed on setup failure.
- unsafe { device.device().destroy_command_pool(command_pool, None) };
- destroy_allocated_buffer(&device, &vertex_buffer);
- return Err(error);
- }
- };
+ let vertex_buffer =
+ match create_static_mesh_vertex_buffer(&instance, &device, &create_info.mesh) {
+ Ok(buffer) => buffer,
+ Err(error) => {
+ // SAFETY: The command pool belongs to this live logical device and is destroyed on setup failure.
+ unsafe { device.device().destroy_command_pool(command_pool, None) };
+ return Err(error);
+ }
+ };
+ let index_buffer =
+ match create_static_mesh_index_buffer(&instance, &device, &create_info.mesh) {
+ Ok(buffer) => buffer,
+ Err(error) => {
+ // SAFETY: The command pool belongs to this live logical device and is destroyed on setup failure.
+ unsafe { device.device().destroy_command_pool(command_pool, None) };
+ destroy_allocated_buffer(&device, &vertex_buffer);
+ return Err(error);
+ }
+ };
let mut renderer = Self {
instance: Some(instance),
validation,
@@ -180,6 +186,7 @@ impl VulkanSmokeRenderer {
swapchain_resources: None,
vertex_buffer: Some(vertex_buffer),
index_buffer: Some(index_buffer),
+ index_count: u32::try_from(create_info.mesh.indices.len()).unwrap_or(u32::MAX),
frame_sync: Vec::new(),
images_in_flight: Vec::new(),
current_frame: 0,
@@ -593,7 +600,7 @@ impl VulkanSmokeRenderer {
);
device
.device()
- .cmd_draw_indexed(command_buffer, 3, 1, 0, 0, 0);
+ .cmd_draw_indexed(command_buffer, self.index_count, 1, 0, 0, 0);
device.device().cmd_end_render_pass(command_buffer);
}
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
index a86de1b..31a2065 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
@@ -24,10 +24,113 @@ pub struct VulkanSmokeRendererCreateInfo {
pub render_request: RenderRequest,
/// Whether validation layers must be enabled.
pub enable_validation: bool,
+ /// Static indexed geometry uploaded before the first live frame.
+ ///
+ /// This initial bridge keeps positions in clip-space. MSH transforms,
+ /// materials, and textures are deliberately higher-level Stage 3 work.
+ pub mesh: VulkanStaticMesh,
/// Optional shared bootstrap progress tracker for failure evidence.
pub bootstrap_progress: Option<Arc<VulkanSmokeBootstrapProgress>>,
}
+/// One vertex accepted by the initial static Vulkan geometry path.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct VulkanStaticVertex {
+ /// Position in Vulkan clip-space XY coordinates.
+ pub position: [f32; 2],
+ /// Linear RGB vertex color.
+ pub color: [f32; 3],
+}
+
+/// Static indexed geometry uploaded to live Vulkan buffers.
+#[derive(Clone, Debug, PartialEq)]
+pub struct VulkanStaticMesh {
+ /// Vertex data in pipeline order.
+ pub vertices: Vec<VulkanStaticVertex>,
+ /// Triangle-list indices into [`Self::vertices`].
+ pub indices: Vec<u16>,
+}
+
+impl VulkanStaticMesh {
+ /// Returns the compatibility triangle used by the native Stage 0 smoke app.
+ #[must_use]
+ pub fn smoke_triangle() -> Self {
+ Self {
+ vertices: vec![
+ VulkanStaticVertex {
+ position: [0.0, -0.55],
+ color: [1.0, 0.2, 0.2],
+ },
+ VulkanStaticVertex {
+ position: [0.55, 0.55],
+ color: [0.2, 1.0, 0.2],
+ },
+ VulkanStaticVertex {
+ position: [-0.55, 0.55],
+ color: [0.2, 0.4, 1.0],
+ },
+ ],
+ indices: vec![0, 1, 2],
+ }
+ }
+
+ pub(super) fn validate(&self) -> Result<(), &'static str> {
+ if self.vertices.is_empty() {
+ return Err("static mesh has no vertices");
+ }
+ if self.indices.is_empty() || !self.indices.len().is_multiple_of(3) {
+ return Err("static mesh indices must contain complete triangles");
+ }
+ if self
+ .indices
+ .iter()
+ .any(|&index| usize::from(index) >= self.vertices.len())
+ {
+ return Err("static mesh index exceeds vertex count");
+ }
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod static_mesh_tests {
+ use super::*;
+
+ #[test]
+ fn smoke_triangle_is_valid_complete_geometry() {
+ let mesh = VulkanStaticMesh::smoke_triangle();
+
+ assert_eq!(mesh.indices, vec![0, 1, 2]);
+ assert_eq!(mesh.validate(), Ok(()));
+ }
+
+ #[test]
+ fn static_mesh_rejects_bad_triangle_topology_and_indices() {
+ let no_vertices = VulkanStaticMesh {
+ vertices: Vec::new(),
+ indices: vec![0, 1, 2],
+ };
+ let incomplete_triangle = VulkanStaticMesh {
+ vertices: VulkanStaticMesh::smoke_triangle().vertices,
+ indices: vec![0, 1],
+ };
+ let out_of_range_index = VulkanStaticMesh {
+ vertices: VulkanStaticMesh::smoke_triangle().vertices,
+ indices: vec![0, 1, 3],
+ };
+
+ assert_eq!(no_vertices.validate(), Err("static mesh has no vertices"));
+ assert_eq!(
+ incomplete_triangle.validate(),
+ Err("static mesh indices must contain complete triangles")
+ );
+ assert_eq!(
+ out_of_range_index.validate(),
+ Err("static mesh index exceeds vertex count")
+ );
+ }
+}
+
/// Shared bootstrap progress used to report partial renderer startup evidence.
#[derive(Debug, Default)]
pub struct VulkanSmokeBootstrapProgress {
@@ -185,6 +288,11 @@ pub enum VulkanSmokeRendererError {
/// Operation context.
context: &'static str,
},
+ /// The submitted static geometry cannot be represented by this path.
+ InvalidStaticMesh {
+ /// Validation failure detail.
+ context: &'static str,
+ },
/// Internal smoke renderer state was unexpectedly absent.
InvariantViolation {
/// Missing state context.
@@ -206,6 +314,7 @@ impl std::fmt::Display for VulkanSmokeRendererError {
Self::MissingMemoryType { context } => {
write!(f, "{context}: no compatible Vulkan memory type")
}
+ Self::InvalidStaticMesh { context } => write!(f, "invalid static mesh: {context}"),
Self::InvariantViolation { context } => {
write!(f, "renderer invariant violated: {context}")
}
@@ -226,6 +335,7 @@ pub struct VulkanSmokeRenderer {
pub(super) swapchain_resources: Option<VulkanSwapchainResources>,
pub(super) vertex_buffer: Option<VulkanAllocatedBuffer>,
pub(super) index_buffer: Option<VulkanAllocatedBuffer>,
+ pub(super) index_count: u32,
pub(super) frame_sync: Vec<VulkanFrameSync>,
pub(super) images_in_flight: Vec<vk::Fence>,
pub(super) current_frame: usize,