feat(render): upload original TEXM to Vulkan
This commit is contained in:
@@ -54,8 +54,9 @@ pub use self::instance::{
|
||||
use self::instance::{cstring_vec, ensure_instance_extensions_available};
|
||||
use self::resources::{
|
||||
color_subresource_range, create_command_pool, create_frame_sync,
|
||||
create_static_mesh_index_buffer, create_static_mesh_vertex_buffer, destroy_allocated_buffer,
|
||||
VulkanAllocatedBuffer, VulkanFrameSync,
|
||||
create_static_mesh_index_buffer, create_static_mesh_vertex_buffer, create_static_texture_image,
|
||||
destroy_allocated_buffer, destroy_allocated_image, VulkanAllocatedBuffer, VulkanAllocatedImage,
|
||||
VulkanFrameSync,
|
||||
};
|
||||
pub use self::runtime::{
|
||||
create_vulkan_logical_device_probe, create_vulkan_logical_device_probe_for_request,
|
||||
@@ -64,8 +65,8 @@ pub use self::runtime::{
|
||||
pub use self::smoke_types::{
|
||||
VulkanSmokeBootstrapProgress, VulkanSmokeBootstrapSnapshot, VulkanSmokeFrameOutcome,
|
||||
VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, VulkanSmokeRendererError,
|
||||
VulkanSmokeRendererReport, VulkanSmokeShutdownReport, VulkanStaticMesh, VulkanStaticVertex,
|
||||
VulkanValidationReport,
|
||||
VulkanSmokeRendererReport, VulkanSmokeShutdownReport, VulkanStaticMesh, VulkanStaticTexture,
|
||||
VulkanStaticVertex, VulkanValidationReport,
|
||||
};
|
||||
#[cfg(test)]
|
||||
use self::surface::extension_name;
|
||||
|
||||
@@ -4,6 +4,7 @@ use ash::vk;
|
||||
|
||||
use super::{
|
||||
VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanSmokeRendererError, VulkanStaticMesh,
|
||||
VulkanStaticTexture,
|
||||
};
|
||||
|
||||
pub(super) struct VulkanAllocatedBuffer {
|
||||
@@ -11,6 +12,12 @@ pub(super) struct VulkanAllocatedBuffer {
|
||||
pub(super) memory: vk::DeviceMemory,
|
||||
}
|
||||
|
||||
pub(super) struct VulkanAllocatedImage {
|
||||
pub(super) image: vk::Image,
|
||||
pub(super) memory: vk::DeviceMemory,
|
||||
pub(super) view: vk::ImageView,
|
||||
}
|
||||
|
||||
pub(super) struct VulkanFrameSync {
|
||||
pub(super) image_available: vk::Semaphore,
|
||||
pub(super) render_finished: vk::Semaphore,
|
||||
@@ -70,6 +77,272 @@ pub(super) fn create_static_mesh_index_buffer(
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(super) fn create_static_texture_image(
|
||||
instance: &VulkanInstanceProbe,
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
command_pool: vk::CommandPool,
|
||||
texture: &VulkanStaticTexture,
|
||||
) -> Result<VulkanAllocatedImage, VulkanSmokeRendererError> {
|
||||
let staging = create_host_visible_buffer(
|
||||
instance,
|
||||
device,
|
||||
&texture.rgba8,
|
||||
vk::BufferUsageFlags::TRANSFER_SRC,
|
||||
"static texture staging buffer",
|
||||
)?;
|
||||
let image_info = vk::ImageCreateInfo::default()
|
||||
.image_type(vk::ImageType::TYPE_2D)
|
||||
.format(vk::Format::R8G8B8A8_UNORM)
|
||||
.extent(vk::Extent3D {
|
||||
width: texture.width,
|
||||
height: texture.height,
|
||||
depth: 1,
|
||||
})
|
||||
.mip_levels(1)
|
||||
.array_layers(1)
|
||||
.samples(vk::SampleCountFlags::TYPE_1)
|
||||
.tiling(vk::ImageTiling::OPTIMAL)
|
||||
.usage(vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::SAMPLED)
|
||||
.sharing_mode(vk::SharingMode::EXCLUSIVE)
|
||||
.initial_layout(vk::ImageLayout::UNDEFINED);
|
||||
// SAFETY: The create info only contains stack-owned values and a validated non-zero extent.
|
||||
let image = unsafe { device.device().create_image(&image_info, None) }.map_err(|result| {
|
||||
destroy_allocated_buffer(device, &staging);
|
||||
VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkCreateImage(static texture)",
|
||||
result,
|
||||
}
|
||||
})?;
|
||||
// SAFETY: The image belongs to this device and is queried immediately after creation.
|
||||
let requirements = unsafe { device.device().get_image_memory_requirements(image) };
|
||||
let Some(memory_type_index) = find_memory_type(
|
||||
instance,
|
||||
device.physical_device(),
|
||||
requirements.memory_type_bits,
|
||||
vk::MemoryPropertyFlags::DEVICE_LOCAL,
|
||||
) else {
|
||||
// SAFETY: Both resources were created on this device and are being rolled back once.
|
||||
unsafe { device.device().destroy_image(image, None) };
|
||||
destroy_allocated_buffer(device, &staging);
|
||||
return Err(VulkanSmokeRendererError::MissingMemoryType {
|
||||
context: "static texture image",
|
||||
});
|
||||
};
|
||||
let allocate_info = vk::MemoryAllocateInfo::default()
|
||||
.allocation_size(requirements.size)
|
||||
.memory_type_index(memory_type_index);
|
||||
// SAFETY: The allocation matches the image memory requirements queried above.
|
||||
let memory = {
|
||||
// SAFETY: The allocation matches the image memory requirements queried above.
|
||||
unsafe { device.device().allocate_memory(&allocate_info, None) }
|
||||
}
|
||||
.map_err(|result| {
|
||||
// SAFETY: Both resources were created on this device and are being rolled back once.
|
||||
unsafe { device.device().destroy_image(image, None) };
|
||||
destroy_allocated_buffer(device, &staging);
|
||||
VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkAllocateMemory(static texture)",
|
||||
result,
|
||||
}
|
||||
})?;
|
||||
// SAFETY: The allocation matches this image's queried requirements.
|
||||
if let Err(result) = unsafe { device.device().bind_image_memory(image, memory, 0) } {
|
||||
// SAFETY: Both resources were created on this device and are being rolled back once.
|
||||
unsafe {
|
||||
device.device().destroy_image(image, None);
|
||||
device.device().free_memory(memory, None);
|
||||
};
|
||||
destroy_allocated_buffer(device, &staging);
|
||||
return Err(VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkBindImageMemory(static texture)",
|
||||
result,
|
||||
});
|
||||
}
|
||||
if let Err(error) = upload_static_texture(
|
||||
device,
|
||||
command_pool,
|
||||
staging.buffer,
|
||||
image,
|
||||
texture.width,
|
||||
texture.height,
|
||||
) {
|
||||
// SAFETY: Both resources were created on this device and are being rolled back once.
|
||||
unsafe {
|
||||
device.device().destroy_image(image, None);
|
||||
device.device().free_memory(memory, None);
|
||||
};
|
||||
destroy_allocated_buffer(device, &staging);
|
||||
return Err(error);
|
||||
}
|
||||
destroy_allocated_buffer(device, &staging);
|
||||
let view_info = vk::ImageViewCreateInfo::default()
|
||||
.image(image)
|
||||
.view_type(vk::ImageViewType::TYPE_2D)
|
||||
.format(vk::Format::R8G8B8A8_UNORM)
|
||||
.subresource_range(color_subresource_range());
|
||||
// SAFETY: The image is live, initialized and has the stated color subresource.
|
||||
let view = {
|
||||
// SAFETY: The image is live, initialized and has the stated color subresource.
|
||||
unsafe { device.device().create_image_view(&view_info, None) }
|
||||
}
|
||||
.map_err(|result| {
|
||||
// SAFETY: Both resources were created on this device and are being rolled back once.
|
||||
unsafe {
|
||||
device.device().destroy_image(image, None);
|
||||
device.device().free_memory(memory, None);
|
||||
};
|
||||
VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkCreateImageView(static texture)",
|
||||
result,
|
||||
}
|
||||
})?;
|
||||
Ok(VulkanAllocatedImage {
|
||||
image,
|
||||
memory,
|
||||
view,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn upload_static_texture(
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
command_pool: vk::CommandPool,
|
||||
staging_buffer: vk::Buffer,
|
||||
image: vk::Image,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Result<(), VulkanSmokeRendererError> {
|
||||
let allocate_info = vk::CommandBufferAllocateInfo::default()
|
||||
.command_pool(command_pool)
|
||||
.level(vk::CommandBufferLevel::PRIMARY)
|
||||
.command_buffer_count(1);
|
||||
// SAFETY: The command pool is live and owned by the current logical device.
|
||||
let command_buffer = unsafe { device.device().allocate_command_buffers(&allocate_info) }
|
||||
.map_err(|result| VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkAllocateCommandBuffers(texture upload)",
|
||||
result,
|
||||
})?[0];
|
||||
let begin =
|
||||
vk::CommandBufferBeginInfo::default().flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
|
||||
// SAFETY: The command buffer is freshly allocated from the current pool.
|
||||
if let Err(result) = unsafe { device.device().begin_command_buffer(command_buffer, &begin) } {
|
||||
// SAFETY: The command buffer belongs to this pool and is released on failure.
|
||||
unsafe {
|
||||
device
|
||||
.device()
|
||||
.free_command_buffers(command_pool, &[command_buffer]);
|
||||
};
|
||||
return Err(VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkBeginCommandBuffer(texture upload)",
|
||||
result,
|
||||
});
|
||||
}
|
||||
let range = color_subresource_range();
|
||||
let to_transfer = vk::ImageMemoryBarrier::default()
|
||||
.old_layout(vk::ImageLayout::UNDEFINED)
|
||||
.new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.src_access_mask(vk::AccessFlags::empty())
|
||||
.dst_access_mask(vk::AccessFlags::TRANSFER_WRITE)
|
||||
.image(image)
|
||||
.subresource_range(range);
|
||||
let region = vk::BufferImageCopy::default()
|
||||
.image_subresource(
|
||||
vk::ImageSubresourceLayers::default()
|
||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
||||
.mip_level(0)
|
||||
.base_array_layer(0)
|
||||
.layer_count(1),
|
||||
)
|
||||
.image_extent(vk::Extent3D {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
});
|
||||
let to_sampled = vk::ImageMemoryBarrier::default()
|
||||
.old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
|
||||
.new_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
|
||||
.src_access_mask(vk::AccessFlags::TRANSFER_WRITE)
|
||||
.dst_access_mask(vk::AccessFlags::SHADER_READ)
|
||||
.image(image)
|
||||
.subresource_range(range);
|
||||
// SAFETY: The commands operate on live, exclusively owned resources and the stated color subresource.
|
||||
unsafe {
|
||||
device.device().cmd_pipeline_barrier(
|
||||
command_buffer,
|
||||
vk::PipelineStageFlags::TOP_OF_PIPE,
|
||||
vk::PipelineStageFlags::TRANSFER,
|
||||
vk::DependencyFlags::empty(),
|
||||
&[],
|
||||
&[],
|
||||
&[to_transfer],
|
||||
);
|
||||
device.device().cmd_copy_buffer_to_image(
|
||||
command_buffer,
|
||||
staging_buffer,
|
||||
image,
|
||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
||||
&[region],
|
||||
);
|
||||
device.device().cmd_pipeline_barrier(
|
||||
command_buffer,
|
||||
vk::PipelineStageFlags::TRANSFER,
|
||||
vk::PipelineStageFlags::FRAGMENT_SHADER,
|
||||
vk::DependencyFlags::empty(),
|
||||
&[],
|
||||
&[],
|
||||
&[to_sampled],
|
||||
);
|
||||
}
|
||||
// SAFETY: Command recording is complete on the current command buffer.
|
||||
if let Err(result) = unsafe { device.device().end_command_buffer(command_buffer) } {
|
||||
// SAFETY: The command buffer belongs to this pool and is released on failure.
|
||||
unsafe {
|
||||
device
|
||||
.device()
|
||||
.free_command_buffers(command_pool, &[command_buffer]);
|
||||
};
|
||||
return Err(VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkEndCommandBuffer(texture upload)",
|
||||
result,
|
||||
});
|
||||
}
|
||||
let command_buffers = [command_buffer];
|
||||
let submit = [vk::SubmitInfo::default().command_buffers(&command_buffers)];
|
||||
// SAFETY: The graphics queue and submitted command buffer are live for the duration of the wait.
|
||||
let submit_result = unsafe {
|
||||
device
|
||||
.device()
|
||||
.queue_submit(device.graphics_queue(), &submit, vk::Fence::null())
|
||||
};
|
||||
let result = submit_result.and_then(|()| {
|
||||
// SAFETY: The graphics queue remains live until the synchronous idle wait completes.
|
||||
unsafe { device.device().queue_wait_idle(device.graphics_queue()) }
|
||||
});
|
||||
// SAFETY: Submission completed or failed synchronously and the command buffer is no longer needed.
|
||||
unsafe {
|
||||
device
|
||||
.device()
|
||||
.free_command_buffers(command_pool, &[command_buffer]);
|
||||
};
|
||||
result.map_err(|result| VulkanSmokeRendererError::VulkanOperation {
|
||||
context: "vkQueueSubmit/WaitIdle(texture upload)",
|
||||
result,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn destroy_allocated_image(
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
image: &VulkanAllocatedImage,
|
||||
) {
|
||||
// SAFETY: The image, view and allocation belong to this device and are destroyed once after idle.
|
||||
unsafe {
|
||||
device.device().destroy_image_view(image.view, None);
|
||||
device.device().destroy_image(image.image, None);
|
||||
device.device().free_memory(image.memory, None);
|
||||
};
|
||||
}
|
||||
|
||||
fn create_host_visible_buffer(
|
||||
instance: &VulkanInstanceProbe,
|
||||
device: &VulkanLogicalDeviceProbe,
|
||||
|
||||
@@ -4,15 +4,15 @@ use ash::vk;
|
||||
|
||||
use super::{
|
||||
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,
|
||||
VulkanAllocatedBuffer, VulkanInstanceConfig, VulkanInstanceProbe, VulkanLogicalDeviceProbe,
|
||||
VulkanSmokeFrameOutcome, VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo,
|
||||
VulkanSmokeRendererError, VulkanSmokeRendererReport, VulkanSmokeShutdownReport,
|
||||
VulkanSurfaceProbe, VulkanSwapchainProbe, VulkanSwapchainResources, VulkanValidationMessenger,
|
||||
VulkanValidationReport,
|
||||
create_static_mesh_vertex_buffer, create_static_texture_image, 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_allocated_image,
|
||||
destroy_swapchain_resources, plan_vulkan_surface, VulkanAllocatedBuffer, VulkanInstanceConfig,
|
||||
VulkanInstanceProbe, VulkanLogicalDeviceProbe, VulkanSmokeFrameOutcome, VulkanSmokeRenderer,
|
||||
VulkanSmokeRendererCreateInfo, VulkanSmokeRendererError, VulkanSmokeRendererReport,
|
||||
VulkanSmokeShutdownReport, VulkanSurfaceProbe, VulkanSwapchainProbe, VulkanSwapchainResources,
|
||||
VulkanValidationMessenger, VulkanValidationReport,
|
||||
};
|
||||
use crate::policy::KHR_PORTABILITY_SUBSET_EXTENSION;
|
||||
use crate::shader_manifest::{triangle_shader_manifest, validate_shader_manifest};
|
||||
@@ -109,6 +109,11 @@ impl VulkanSmokeRenderer {
|
||||
.mesh
|
||||
.validate()
|
||||
.map_err(|context| VulkanSmokeRendererError::InvalidStaticMesh { context })?;
|
||||
if let Some(texture) = &create_info.texture {
|
||||
texture
|
||||
.validate()
|
||||
.map_err(|context| VulkanSmokeRendererError::InvalidStaticTexture { context })?;
|
||||
}
|
||||
let bootstrap_progress = create_info.bootstrap_progress.as_ref();
|
||||
let shader_manifest = validate_shader_manifest(&triangle_shader_manifest())
|
||||
.map_err(VulkanSmokeRendererError::ShaderManifest)?;
|
||||
@@ -176,6 +181,21 @@ impl VulkanSmokeRenderer {
|
||||
return Err(error);
|
||||
}
|
||||
};
|
||||
let texture = match create_info.texture.as_ref() {
|
||||
None => None,
|
||||
Some(texture) => {
|
||||
match create_static_texture_image(&instance, &device, command_pool, texture) {
|
||||
Ok(image) => Some(image),
|
||||
Err(error) => {
|
||||
// SAFETY: These resources belong to this live device and are rolled back before it drops.
|
||||
unsafe { device.device().destroy_command_pool(command_pool, None) };
|
||||
destroy_allocated_buffer(&device, &index_buffer);
|
||||
destroy_allocated_buffer(&device, &vertex_buffer);
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut renderer = Self {
|
||||
instance: Some(instance),
|
||||
validation,
|
||||
@@ -186,6 +206,7 @@ impl VulkanSmokeRenderer {
|
||||
swapchain_resources: None,
|
||||
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),
|
||||
frame_sync: Vec::new(),
|
||||
images_in_flight: Vec::new(),
|
||||
@@ -640,6 +661,9 @@ impl VulkanSmokeRenderer {
|
||||
fn destroy_device_owned_resources(&mut self) {
|
||||
self.destroy_swapchain_resources();
|
||||
if let Some(device) = self.device.as_ref() {
|
||||
if let Some(texture) = self.texture.take() {
|
||||
destroy_allocated_image(device, &texture);
|
||||
}
|
||||
if let Some(buffer) = self.index_buffer.take() {
|
||||
// SAFETY: Buffer and memory belong to this device and are destroyed once after the device has been idled and frame work has been torn down.
|
||||
unsafe {
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{
|
||||
VulkanAllocatedBuffer, VulkanFrameSync, VulkanInstanceError, VulkanInstanceProbe,
|
||||
VulkanLogicalDeviceError, VulkanLogicalDeviceProbe, VulkanSurfaceError, VulkanSurfaceProbe,
|
||||
VulkanSwapchainProbe, VulkanSwapchainProbeError, VulkanSwapchainResources,
|
||||
VulkanAllocatedBuffer, VulkanAllocatedImage, VulkanFrameSync, VulkanInstanceError,
|
||||
VulkanInstanceProbe, VulkanLogicalDeviceError, VulkanLogicalDeviceProbe, VulkanSurfaceError,
|
||||
VulkanSurfaceProbe, VulkanSwapchainProbe, VulkanSwapchainProbeError, VulkanSwapchainResources,
|
||||
VulkanValidationMessenger,
|
||||
};
|
||||
use crate::shader_manifest::VulkanShaderManifestError;
|
||||
@@ -29,6 +29,8 @@ pub struct VulkanSmokeRendererCreateInfo {
|
||||
/// This initial bridge keeps positions in clip-space. MSH transforms,
|
||||
/// materials, and textures are deliberately higher-level Stage 3 work.
|
||||
pub mesh: VulkanStaticMesh,
|
||||
/// Optional RGBA8 texture uploaded before the first live frame.
|
||||
pub texture: Option<VulkanStaticTexture>,
|
||||
/// Optional shared bootstrap progress tracker for failure evidence.
|
||||
pub bootstrap_progress: Option<Arc<VulkanSmokeBootstrapProgress>>,
|
||||
}
|
||||
@@ -51,6 +53,38 @@ pub struct VulkanStaticMesh {
|
||||
pub indices: Vec<u16>,
|
||||
}
|
||||
|
||||
/// Decoded RGBA8 image accepted by the initial Vulkan texture upload path.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct VulkanStaticTexture {
|
||||
/// Image width in texels.
|
||||
pub width: u32,
|
||||
/// Image height in texels.
|
||||
pub height: u32,
|
||||
/// Row-major RGBA8 pixels.
|
||||
pub rgba8: Vec<u8>,
|
||||
}
|
||||
|
||||
impl VulkanStaticTexture {
|
||||
pub(super) fn validate(&self) -> Result<(), &'static str> {
|
||||
let pixels = usize::try_from(self.width)
|
||||
.ok()
|
||||
.and_then(|width| {
|
||||
usize::try_from(self.height)
|
||||
.ok()
|
||||
.and_then(|height| width.checked_mul(height))
|
||||
})
|
||||
.and_then(|pixels| pixels.checked_mul(4));
|
||||
match pixels {
|
||||
None => Err("static texture dimensions overflow address space"),
|
||||
Some(0) => Err("static texture has zero extent"),
|
||||
Some(expected) if expected != self.rgba8.len() => {
|
||||
Err("static texture rgba8 byte count does not match extent")
|
||||
}
|
||||
Some(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanStaticMesh {
|
||||
/// Returns the compatibility triangle used by the native Stage 0 smoke app.
|
||||
#[must_use]
|
||||
@@ -293,6 +327,11 @@ pub enum VulkanSmokeRendererError {
|
||||
/// Validation failure detail.
|
||||
context: &'static str,
|
||||
},
|
||||
/// The submitted static texture cannot be represented by this path.
|
||||
InvalidStaticTexture {
|
||||
/// Validation failure detail.
|
||||
context: &'static str,
|
||||
},
|
||||
/// Internal smoke renderer state was unexpectedly absent.
|
||||
InvariantViolation {
|
||||
/// Missing state context.
|
||||
@@ -315,6 +354,9 @@ impl std::fmt::Display for VulkanSmokeRendererError {
|
||||
write!(f, "{context}: no compatible Vulkan memory type")
|
||||
}
|
||||
Self::InvalidStaticMesh { context } => write!(f, "invalid static mesh: {context}"),
|
||||
Self::InvalidStaticTexture { context } => {
|
||||
write!(f, "invalid static texture: {context}")
|
||||
}
|
||||
Self::InvariantViolation { context } => {
|
||||
write!(f, "renderer invariant violated: {context}")
|
||||
}
|
||||
@@ -335,6 +377,7 @@ pub struct VulkanSmokeRenderer {
|
||||
pub(super) swapchain_resources: Option<VulkanSwapchainResources>,
|
||||
pub(super) vertex_buffer: Option<VulkanAllocatedBuffer>,
|
||||
pub(super) index_buffer: Option<VulkanAllocatedBuffer>,
|
||||
pub(super) texture: Option<VulkanAllocatedImage>,
|
||||
pub(super) index_count: u32,
|
||||
pub(super) frame_sync: Vec<VulkanFrameSync>,
|
||||
pub(super) images_in_flight: Vec<vk::Fence>,
|
||||
|
||||
Reference in New Issue
Block a user