aboutsummaryrefslogtreecommitdiff
path: root/adapters/fparkan-render-vulkan/src
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-07-18 06:56:49 +0300
committerValentin Popov <valentin@popov.link>2026-07-18 06:56:49 +0300
commitb915554f3c5695bd8b9707359ffaa51ea8742341 (patch)
treeacc3dd51b4fbe33e0c3f32d5c5e0d06b5b57dac2 /adapters/fparkan-render-vulkan/src
parent116e7716b922dfe402d228f778085dc7b1c1606f (diff)
downloadfparkan-b915554f3c5695bd8b9707359ffaa51ea8742341.tar.xz
fparkan-b915554f3c5695bd8b9707359ffaa51ea8742341.zip
feat(render): preserve original mesh batch draws
Diffstat (limited to 'adapters/fparkan-render-vulkan/src')
-rw-r--r--adapters/fparkan-render-vulkan/src/asset_mesh.rs22
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi.rs4
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke.rs15
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs41
4 files changed, 73 insertions, 9 deletions
diff --git a/adapters/fparkan-render-vulkan/src/asset_mesh.rs b/adapters/fparkan-render-vulkan/src/asset_mesh.rs
index d6689b9..b90499a 100644
--- a/adapters/fparkan-render-vulkan/src/asset_mesh.rs
+++ b/adapters/fparkan-render-vulkan/src/asset_mesh.rs
@@ -1,6 +1,6 @@
//! Format-to-GPU geometry bridge for the initial static asset renderer.
-use crate::{VulkanStaticMesh, VulkanStaticVertex};
+use crate::{VulkanStaticDrawRange, VulkanStaticMesh, VulkanStaticVertex};
use fparkan_msh::ModelAsset;
/// Error returned when a validated MSH cannot enter the current static GPU path.
@@ -45,7 +45,10 @@ pub fn project_msh_to_static_mesh(
model: &ModelAsset,
) -> Result<VulkanStaticMesh, VulkanAssetMeshError> {
let mut indices = Vec::new();
+ let mut draw_ranges = Vec::with_capacity(model.batches.len());
for batch in &model.batches {
+ let first_index =
+ u32::try_from(indices.len()).map_err(|_| VulkanAssetMeshError::IndexOutOfRange)?;
let start = usize::try_from(batch.index_start)
.map_err(|_| VulkanAssetMeshError::IndexOutOfRange)?;
let end = start
@@ -62,6 +65,10 @@ pub fn project_msh_to_static_mesh(
.ok_or(VulkanAssetMeshError::IndexOutOfRange)?;
indices.push(u16::try_from(index).map_err(|_| VulkanAssetMeshError::IndexOutOfRange)?);
}
+ draw_ranges.push(VulkanStaticDrawRange {
+ first_index,
+ index_count: u32::from(batch.index_count),
+ });
}
if indices.is_empty() {
return Err(VulkanAssetMeshError::EmptyGeometry);
@@ -114,7 +121,11 @@ pub fn project_msh_to_static_mesh(
})
.collect();
- Ok(VulkanStaticMesh { vertices, indices })
+ Ok(VulkanStaticMesh {
+ vertices,
+ indices,
+ draw_ranges,
+ })
}
#[cfg(test)]
@@ -165,6 +176,13 @@ mod tests {
.expect("representable MSH");
assert_eq!(mesh.indices, vec![1, 2, 3]);
+ assert_eq!(
+ mesh.draw_ranges,
+ vec![VulkanStaticDrawRange {
+ first_index: 0,
+ index_count: 3,
+ }]
+ );
assert_eq!(mesh.vertices[1].position, [-0.8, -0.8]);
assert_eq!(mesh.vertices[2].position, [0.8, -0.8]);
assert_eq!(mesh.vertices[3].position, [-0.8, 0.8]);
diff --git a/adapters/fparkan-render-vulkan/src/ffi.rs b/adapters/fparkan-render-vulkan/src/ffi.rs
index 315d4ff..3beedbd 100644
--- a/adapters/fparkan-render-vulkan/src/ffi.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi.rs
@@ -65,8 +65,8 @@ pub use self::runtime::{
pub use self::smoke_types::{
VulkanSmokeBootstrapProgress, VulkanSmokeBootstrapSnapshot, VulkanSmokeFrameOutcome,
VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, VulkanSmokeRendererError,
- VulkanSmokeRendererReport, VulkanSmokeShutdownReport, VulkanStaticMesh, VulkanStaticTexture,
- VulkanStaticVertex, VulkanValidationReport,
+ VulkanSmokeRendererReport, VulkanSmokeShutdownReport, VulkanStaticDrawRange, VulkanStaticMesh,
+ VulkanStaticTexture, VulkanStaticVertex, VulkanValidationReport,
};
#[cfg(test)]
use self::surface::extension_name;
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
index d8cc0d5..a071e74 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
@@ -211,7 +211,7 @@ impl VulkanSmokeRenderer {
vertex_buffer: Some(vertex_buffer),
index_buffer: Some(index_buffer),
texture,
- index_count: u32::try_from(create_info.mesh.indices.len()).unwrap_or(u32::MAX),
+ draw_ranges: create_info.mesh.draw_ranges.clone(),
frame_sync: Vec::new(),
images_in_flight: Vec::new(),
current_frame: 0,
@@ -640,9 +640,16 @@ impl VulkanSmokeRenderer {
0,
vk::IndexType::UINT16,
);
- device
- .device()
- .cmd_draw_indexed(command_buffer, self.index_count, 1, 0, 0, 0);
+ for range in &self.draw_ranges {
+ device.device().cmd_draw_indexed(
+ command_buffer,
+ range.index_count,
+ 1,
+ range.first_index,
+ 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 af84fed..0821aba 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
@@ -53,6 +53,17 @@ pub struct VulkanStaticMesh {
pub vertices: Vec<VulkanStaticVertex>,
/// Triangle-list indices into [`Self::vertices`].
pub indices: Vec<u16>,
+ /// Source-preserving triangle draw ranges in [`Self::indices`].
+ pub draw_ranges: Vec<VulkanStaticDrawRange>,
+}
+
+/// One indexed triangle-list draw retained from an original mesh batch.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct VulkanStaticDrawRange {
+ /// First index in the shared index buffer.
+ pub first_index: u32,
+ /// Number of indices in this draw.
+ pub index_count: u32,
}
/// Decoded RGBA8 image accepted by the initial Vulkan texture upload path.
@@ -110,6 +121,10 @@ impl VulkanStaticMesh {
},
],
indices: vec![0, 1, 2],
+ draw_ranges: vec![VulkanStaticDrawRange {
+ first_index: 0,
+ index_count: 3,
+ }],
}
}
@@ -120,6 +135,24 @@ impl VulkanStaticMesh {
if self.indices.is_empty() || !self.indices.len().is_multiple_of(3) {
return Err("static mesh indices must contain complete triangles");
}
+ if self.draw_ranges.is_empty() {
+ return Err("static mesh has no draw ranges");
+ }
+ let mut expected_first = 0_u32;
+ for range in &self.draw_ranges {
+ if range.first_index != expected_first
+ || range.index_count == 0
+ || !range.index_count.is_multiple_of(3)
+ {
+ return Err("static mesh draw ranges must be contiguous complete triangles");
+ }
+ expected_first = expected_first
+ .checked_add(range.index_count)
+ .ok_or("static mesh draw range exceeds index count")?;
+ }
+ if usize::try_from(expected_first).ok() != Some(self.indices.len()) {
+ return Err("static mesh draw ranges must cover all indices");
+ }
if self
.indices
.iter()
@@ -148,14 +181,20 @@ mod static_mesh_tests {
let no_vertices = VulkanStaticMesh {
vertices: Vec::new(),
indices: vec![0, 1, 2],
+ draw_ranges: VulkanStaticMesh::smoke_triangle().draw_ranges,
};
let incomplete_triangle = VulkanStaticMesh {
vertices: VulkanStaticMesh::smoke_triangle().vertices,
indices: vec![0, 1],
+ draw_ranges: vec![VulkanStaticDrawRange {
+ first_index: 0,
+ index_count: 2,
+ }],
};
let out_of_range_index = VulkanStaticMesh {
vertices: VulkanStaticMesh::smoke_triangle().vertices,
indices: vec![0, 1, 3],
+ draw_ranges: VulkanStaticMesh::smoke_triangle().draw_ranges,
};
assert_eq!(no_vertices.validate(), Err("static mesh has no vertices"));
@@ -383,7 +422,7 @@ pub struct VulkanSmokeRenderer {
pub(super) vertex_buffer: Option<VulkanAllocatedBuffer>,
pub(super) index_buffer: Option<VulkanAllocatedBuffer>,
pub(super) texture: Option<VulkanAllocatedImage>,
- pub(super) index_count: u32,
+ pub(super) draw_ranges: Vec<VulkanStaticDrawRange>,
pub(super) frame_sync: Vec<VulkanFrameSync>,
pub(super) images_in_flight: Vec<vk::Fence>,
pub(super) current_frame: usize,