feat(render): preserve original mesh batch draws
This commit is contained in:
@@ -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]);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user