708 lines
27 KiB
Rust
708 lines
27 KiB
Rust
use std::sync::Arc;
|
|
use std::time::SystemTime;
|
|
|
|
use cgmath::{Matrix4, SquareMatrix};
|
|
use dds::get_block_size;
|
|
use rust_engine_proc::perf;
|
|
use vulkano::device::physical::PhysicalDevice;
|
|
use vulkano::pipeline::viewport::Viewport;
|
|
use vulkano::render_pass::{FramebufferAbstract, RenderPass};
|
|
use vulkano::{buffer::{BufferUsage, CpuAccessibleBuffer}, command_buffer::SubpassContents, image::{ImageAccess, ImageLayout, ImageUsage, MipmapsCount, immutable::SubImage}};
|
|
use vulkano::command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, PrimaryCommandBuffer};
|
|
use vulkano::device::{Device, DeviceExtensions, Features, Queue};
|
|
use vulkano::format::{ClearValue, Format};
|
|
use vulkano::image::{ImageCreateFlags, ImageDimensions, ImmutableImage};
|
|
use vulkano::instance::{ApplicationInfo, Instance, InstanceExtensions, Version};
|
|
use vulkano::instance::debug::{DebugCallback, MessageSeverity, MessageType};
|
|
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode};
|
|
use vulkano::swapchain::{AcquireError, FullscreenExclusive, PresentMode, Surface, SurfaceTransform, Swapchain, SwapchainCreationError};
|
|
use vulkano::swapchain;
|
|
use vulkano::sync::{FlushError, GpuFuture};
|
|
use vulkano::sync;
|
|
use vulkano_win::VkSurfaceBuild;
|
|
use winit::event::{Event, WindowEvent};
|
|
use winit::event_loop::{ControlFlow, EventLoop};
|
|
|
|
use mesh::CPUMesh;
|
|
use pipelines::Drawcall;
|
|
use pipelines::{DefaultShader, TextShader};
|
|
use pipelines::vs;
|
|
use winit::window::{Window, WindowBuilder};
|
|
|
|
use crate::config::RenderConfig;
|
|
use crate::perf::PerformanceCounter;
|
|
use crate::vulkan::gameobject::GameObject;
|
|
|
|
use self::mesh::CPUVertexList;
|
|
|
|
pub mod pipelines;
|
|
pub mod gameobject;
|
|
pub mod mesh;
|
|
pub mod dds;
|
|
mod renderpass;
|
|
mod framebuffers;
|
|
|
|
const VALIDATION_LAYERS: &[&str] = &[
|
|
"VK_LAYER_KHRONOS_validation",
|
|
];
|
|
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct Vertex {
|
|
pub position: [f32; 3],
|
|
pub uv: [f32; 2],
|
|
pub normal: [f32; 3],
|
|
pub tangent: [f32; 4],
|
|
pub bone_index: [i32; 4],
|
|
pub bone_weight: [f32; 4],
|
|
}
|
|
vulkano::impl_vertex!(Vertex, position, uv, normal, tangent, bone_index, bone_weight);
|
|
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct LinePoint {
|
|
pub position: [f32; 3],
|
|
}
|
|
vulkano::impl_vertex!(LinePoint, position);
|
|
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct TextVertex {
|
|
pub position: [f32; 3],
|
|
pub uv: [f32; 2],
|
|
}
|
|
vulkano::impl_vertex!(TextVertex, position, uv);
|
|
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct TextInstanceData {}
|
|
vulkano::impl_vertex!(TextInstanceData);
|
|
|
|
pub trait Game {
|
|
/// Returns true if event should be ignored by the vulkan handler
|
|
fn on_window_event(self: &mut Self, event: &Event<()>);
|
|
|
|
fn update(self: &mut Self, renderer: &mut VulkanRenderer);
|
|
|
|
fn get_game_objects(&self) -> &Vec<GameObject>;
|
|
|
|
fn get_ubo(&self) -> &vs::ty::ObjectUniformData;
|
|
}
|
|
|
|
pub struct Mesh<V> {
|
|
pub vertex_buffer: Arc<CpuAccessibleBuffer<[V]>>,
|
|
pub index_buffer: Arc<CpuAccessibleBuffer<[u32]>>,
|
|
pub original_path: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct MeshHandle {
|
|
pub index: usize,
|
|
pub textures: Vec<TextureHandle>,
|
|
pub original_path: Option<String>,
|
|
pub pipeline_index: usize
|
|
}
|
|
|
|
pub(crate) type TextureHandle = usize;
|
|
#[derive(Debug, Clone)]
|
|
pub struct Texture {
|
|
pub image: Arc<ImmutableImage>,
|
|
pub sampler: Arc<Sampler>
|
|
}
|
|
|
|
pub struct GameData {
|
|
pub start_time: SystemTime,
|
|
pub recreate_pipeline: bool,
|
|
pub dimensions: [u32; 2],
|
|
pub shutdown: bool,
|
|
pub meshes: Vec<Mesh<Vertex>>,
|
|
pub meshes_text: Vec<Mesh<TextVertex>>,
|
|
pub textures: Vec<Texture>,
|
|
}
|
|
|
|
pub struct VulkanRenderer {
|
|
pub game_data: GameData,
|
|
pub device: Arc<Device>,
|
|
pub framebuffers: Vec<Arc<dyn FramebufferAbstract + Send + Sync>>,
|
|
pub pipelines: Vec<Box<dyn Drawcall>>,
|
|
pub surface: Arc<Surface<Window>>,
|
|
pub swapchain: Arc<Swapchain<Window>>,
|
|
pub render_pass: Arc<RenderPass>,
|
|
pub queue: Arc<Queue>,
|
|
pub recreate_swapchain: bool,
|
|
pub debug_callback: Option<DebugCallback>,
|
|
pub previous_frame_end: Option<Box<dyn GpuFuture>>,
|
|
pub uniform_buffers: Vec<Arc<CpuAccessibleBuffer<vs::ty::ObjectUniformData>>>,
|
|
pub render_config: RenderConfig,
|
|
pub viewport: Viewport
|
|
}
|
|
|
|
impl VulkanRenderer {
|
|
pub fn init(enable_validation_layers: bool, render_config: RenderConfig) -> (VulkanRenderer, EventLoop<()>) {
|
|
// Create empty game data struct to be filled
|
|
let mut data = GameData {
|
|
start_time: SystemTime::now(),
|
|
recreate_pipeline: false,
|
|
shutdown: false,
|
|
dimensions: [0, 0],
|
|
meshes: vec![],
|
|
meshes_text: vec![],
|
|
textures: vec![],
|
|
};
|
|
|
|
// Create basic vulkan instance with layers and info
|
|
let instance = {
|
|
let extensions = InstanceExtensions {
|
|
ext_debug_utils: true,
|
|
..vulkano_win::required_extensions()
|
|
};
|
|
println!("Using extensions: {:?}", extensions);
|
|
|
|
let app_info = ApplicationInfo {
|
|
application_name: Some("Asuro's Editor".into()),
|
|
application_version: Some(Version { major: 0, minor: 1, patch: 0 }),
|
|
engine_name: Some("Asuro's Rust Engine".into()),
|
|
engine_version: Some(Version { major: 0, minor: 1, patch: 0 })
|
|
};
|
|
|
|
if enable_validation_layers {
|
|
println!("Enabling validation layers...");
|
|
let available_layers = vulkano::instance::layers_list().unwrap().map(|layer| String::from(layer.name())).collect::<Vec<String>>();
|
|
println!("Available layers: {:?}", available_layers);
|
|
|
|
VALIDATION_LAYERS.iter().for_each(|wanted_layer_name| {
|
|
if !available_layers.iter().any(|available_layer_name| available_layer_name == wanted_layer_name) {
|
|
panic!("Validation layer not found: {:?}", wanted_layer_name);
|
|
}
|
|
});
|
|
|
|
Instance::new(Some(&app_info), Version::V1_1, &extensions, VALIDATION_LAYERS.iter().cloned()).expect("failed to create Vulkan instance")
|
|
} else {
|
|
Instance::new(Some(&app_info), Version::V1_1, &extensions, None).expect("failed to create Vulkan instance")
|
|
}
|
|
};
|
|
|
|
// lifetime of this is important, even tho it isn't used!
|
|
let mut debug_callback = None;
|
|
|
|
// Debug stuff
|
|
if enable_validation_layers {
|
|
let msg_severity = MessageSeverity {
|
|
verbose: false,
|
|
information: true,
|
|
warning: true,
|
|
error: true
|
|
};
|
|
|
|
let msg_types = MessageType {
|
|
general: true,
|
|
performance: true,
|
|
validation: true
|
|
};
|
|
|
|
debug_callback = DebugCallback::new(&instance, msg_severity, msg_types, |msg| {
|
|
let type_str = match (msg.severity.error, msg.severity.warning, msg.severity.information, msg.severity.verbose) {
|
|
(true, _, _, _) => "!!",
|
|
(_, true, _, _) => "!",
|
|
(_, _, true, _) => "i",
|
|
_ => "v"
|
|
};
|
|
|
|
let layer_str = msg.layer_prefix;
|
|
|
|
println!("[{}][{}]: {}", type_str, layer_str.unwrap_or(""), msg.description);
|
|
}).ok();
|
|
}
|
|
|
|
// TODO: Create device selector
|
|
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
|
|
|
|
let events_loop = EventLoop::new();
|
|
let surface = WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap();
|
|
let window = surface.window();
|
|
|
|
// Queue
|
|
let queue_family = physical.queue_families().find(|&q| {
|
|
q.supports_graphics() && surface.is_supported(q).unwrap_or(false)
|
|
}).unwrap();
|
|
|
|
let device_ext = DeviceExtensions { khr_swapchain: true, khr_maintenance1: true, ..DeviceExtensions::none() };
|
|
let (device, mut queues) = Device::new(physical, &Features::none(), &device_ext,
|
|
[(queue_family, 0.5)].iter().cloned()).unwrap();
|
|
let queue = queues.next().unwrap();
|
|
|
|
// Swapchain
|
|
let (swapchain, images) = {
|
|
let caps = surface.capabilities(physical).unwrap();
|
|
let usage = caps.supported_usage_flags;
|
|
let alpha = caps.supported_composite_alpha.iter().next().unwrap();
|
|
let (format, color_space) = caps.supported_formats[2];
|
|
let inner_size = window.inner_size();
|
|
data.dimensions = [inner_size.width, inner_size.height];
|
|
|
|
Swapchain::start(device.clone(), surface.clone())
|
|
.num_images(caps.min_image_count)
|
|
.format(format)
|
|
.dimensions(data.dimensions)
|
|
.layers(1)
|
|
.usage(usage)
|
|
.sharing_mode(&queue)
|
|
.transform(SurfaceTransform::Identity)
|
|
.composite_alpha(alpha)
|
|
.present_mode(PresentMode::Fifo)
|
|
.fullscreen_exclusive(FullscreenExclusive::Default)
|
|
.clipped(true)
|
|
.color_space(color_space)
|
|
.build().unwrap()
|
|
};
|
|
|
|
let size = images[0].dimensions().width_height();
|
|
let viewport = create_viewport(size[0] as f32, size[1] as f32);
|
|
|
|
// Render pass
|
|
let render_pass = renderpass::create_render_pass(device.clone(), &render_config, swapchain.format());
|
|
let render_pass_text = renderpass::create_render_pass(device.clone(), &render_config, swapchain.format());
|
|
|
|
let pipelines: Vec<Box<dyn Drawcall>> = vec![
|
|
Box::new(DefaultShader::new(device.clone(), render_pass.clone())),
|
|
Box::new(TextShader::new(device.clone(), render_pass_text.clone())),
|
|
];
|
|
|
|
// The render pass we created above only describes the layout of our framebuffers. Before we
|
|
// can draw we also need to create the actual framebuffers.
|
|
let framebuffers = framebuffers::create_framebuffers(device.clone(), &swapchain, &images, render_config.get_msaa(), render_pass.clone());
|
|
|
|
let mut uniform_buffers = Vec::new();
|
|
let uniform_buffer = vs::ty::ObjectUniformData {
|
|
view: Matrix4::identity().into(),
|
|
projection: Matrix4::identity().into(),
|
|
ortho_projection: Matrix4::identity().into(),
|
|
time: 0.0,
|
|
light_position: [0.0, 0.0, 0.0],
|
|
light_directional_rotation: [0.0, 0.0, 0.0],
|
|
camera_position: [0.0, 0.0, 0.0],
|
|
_dummy0: [0; 12],
|
|
_dummy1: [0; 4],
|
|
_dummy2: [0; 4],
|
|
};
|
|
|
|
for _ in 0..swapchain.num_images() {
|
|
uniform_buffers.push(CpuAccessibleBuffer::from_data(
|
|
device.clone(),
|
|
BufferUsage::uniform_buffer_transfer_destination(),
|
|
false,
|
|
uniform_buffer,
|
|
).unwrap());
|
|
}
|
|
|
|
// In the loop below we are going to submit commands to the GPU. Submitting a command produces
|
|
// an object that implements the `GpuFuture` trait, which holds the resources for as long as
|
|
// they are in use by the GPU.
|
|
//
|
|
// Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid
|
|
// that, we store the submission of the previous frame here.
|
|
let previous_frame_end = Some(Box::new(sync::now(device.clone())) as Box<dyn GpuFuture>);
|
|
|
|
(VulkanRenderer { game_data: data, device, framebuffers,
|
|
pipelines, uniform_buffers,
|
|
surface, swapchain, render_pass, queue,
|
|
recreate_swapchain: false, debug_callback, previous_frame_end,
|
|
render_config, viewport
|
|
}, events_loop)
|
|
}
|
|
|
|
fn create_command_buffer(self: &mut Self, fb_index: usize, uniform_buffer_data: &vs::ty::ObjectUniformData, game_objects: &Vec<GameObject>) -> Arc<PrimaryAutoCommandBuffer> {
|
|
// General setup
|
|
let mut builder = AutoCommandBufferBuilder::primary(self.device.clone(), self.queue.family(), CommandBufferUsage::OneTimeSubmit).unwrap();
|
|
builder.update_buffer(self.uniform_buffers[fb_index].clone(), Arc::new(*uniform_buffer_data)).unwrap();
|
|
if self.render_config.msaa_samples > 0 {
|
|
builder.begin_render_pass(self.framebuffers[fb_index].clone(), SubpassContents::Inline, vec![ClearValue::None, ClearValue::Float([0.0, 0.0, 0.0, 1.0]), ClearValue::Depth(1.0)]).unwrap();
|
|
} else {
|
|
builder.begin_render_pass(self.framebuffers[fb_index].clone(), SubpassContents::Inline, vec![ClearValue::Float([0.0, 0.0, 0.0, 1.0]), ClearValue::Depth(1.0)]).unwrap();
|
|
}
|
|
|
|
builder.set_viewport(0, [self.viewport.clone()]);
|
|
|
|
// Draw meshes etc.
|
|
let mut index = 0;
|
|
for pipeline in &self.pipelines {
|
|
let objects = game_objects.iter().filter(|go| go.visible && go.pipeline_index == index).collect();
|
|
pipeline.draw(&mut builder, fb_index, objects, &self.game_data);
|
|
index += 1;
|
|
}
|
|
|
|
// General cleanup
|
|
builder.end_render_pass().unwrap();
|
|
Arc::new(builder.build().unwrap())
|
|
}
|
|
|
|
#[perf("renderer", crate::perf::PerformanceCounter)]
|
|
pub fn render_loop(self: &mut Self, new_ubo: &vs::ty::ObjectUniformData, game_objects: &Vec<GameObject>) {
|
|
// cleanup previous frame
|
|
self.previous_frame_end.as_mut().unwrap().cleanup_finished();
|
|
|
|
// recreate swapchain if window size changed
|
|
if self.recreate_swapchain {
|
|
let window = self.surface.window();
|
|
let inner_size = window.inner_size();
|
|
self.game_data.dimensions = [inner_size.width, inner_size.height];
|
|
|
|
let (new_swapchain, new_images) = match self.swapchain.recreate().dimensions(self.game_data.dimensions).build() {
|
|
Ok(r) => r,
|
|
// This error tends to happen when the user is manually resizing the window.
|
|
// Simply restarting the loop is the easiest way to fix this issue.
|
|
Err(SwapchainCreationError::UnsupportedDimensions) => {
|
|
println!("Swapchain rejected: UnsupportedDimensions");
|
|
return;
|
|
}
|
|
Err(err) => panic!("{:?}", err),
|
|
};
|
|
|
|
let size = new_images[0].dimensions().width_height();
|
|
self.viewport = create_viewport(size[0] as f32, size[1] as f32);
|
|
|
|
self.render_pass = renderpass::create_render_pass(self.device.clone(), &self.render_config, new_swapchain.format());
|
|
|
|
self.pipelines = vec![
|
|
Box::new(DefaultShader::new(self.device.clone(), self.render_pass.clone())),
|
|
Box::new(TextShader::new(self.device.clone(), self.render_pass.clone())),
|
|
];
|
|
|
|
self.swapchain = new_swapchain;
|
|
// Because framebuffers contains an Arc on the old swapchain, we need to
|
|
// recreate framebuffers as well.
|
|
self.framebuffers = framebuffers::create_framebuffers(self.device.clone(), &self.swapchain, &new_images, self.render_config.get_msaa(), self.render_pass.clone());
|
|
|
|
self.recreate_swapchain = false;
|
|
}
|
|
|
|
// recreate pipeline if requested
|
|
if self.game_data.recreate_pipeline {
|
|
let device = self.device.clone();
|
|
let render_pass = self.render_pass.clone();
|
|
self.pipelines.iter_mut().for_each(|pipeline| pipeline.recreate_pipeline(device.clone(), render_pass.clone()));
|
|
self.game_data.recreate_pipeline = false;
|
|
}
|
|
|
|
// Before we can draw on the output, we have to *acquire* an image from the swapchain. If
|
|
// no image is available (which happens if you submit draw commands too quickly), then the
|
|
// function will block.
|
|
// This operation returns the index of the image that we are allowed to draw upon.
|
|
//
|
|
// This function can block if no image is available. The parameter is an optional timeout
|
|
// after which the function call will return an error.
|
|
let (fb_index, _, acquire_future) = match swapchain::acquire_next_image(self.swapchain.clone(), None) {
|
|
Ok(r) => r,
|
|
Err(AcquireError::OutOfDate) => {
|
|
self.recreate_swapchain = true;
|
|
return;
|
|
},
|
|
Err(err) => panic!("{:?}", err)
|
|
};
|
|
|
|
let command_buffer = self.create_command_buffer(fb_index, new_ubo, game_objects).clone();
|
|
|
|
let future = self.previous_frame_end.take().unwrap()
|
|
.join(acquire_future)
|
|
.then_execute(self.queue.clone(), command_buffer).unwrap()
|
|
.then_swapchain_present(self.queue.clone(), self.swapchain.clone(), fb_index)
|
|
.then_signal_fence_and_flush();
|
|
|
|
match future {
|
|
Ok(future) => {
|
|
// we're joining on the previous future but the CPU is running faster than the GPU so
|
|
// eventually it stutters, and jumps ahead to the newer frames.
|
|
//
|
|
// See vulkano issue 1135: https://github.com/vulkano-rs/vulkano/issues/1135
|
|
// This makes sure the CPU stays in sync with the GPU in situations when the CPU is
|
|
// running "too fast"
|
|
#[cfg(target_os = "macos")]
|
|
future.wait(None).unwrap();
|
|
|
|
self.previous_frame_end = Some(Box::new(future) as Box<_>);
|
|
},
|
|
Err(FlushError::OutOfDate) => {
|
|
println!("Swapchain out of date!");
|
|
self.recreate_swapchain = true;
|
|
self.previous_frame_end = Some(Box::new(sync::now(self.device.clone())) as Box<_>);
|
|
}
|
|
Err(e) => {
|
|
println!("{:?}", e);
|
|
self.previous_frame_end = Some(Box::new(sync::now(self.device.clone())) as Box<_>);
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn upload_mesh(self: &mut Self, mesh: CPUMesh, original_path: Option<String>) -> usize {
|
|
let index_buffer = CpuAccessibleBuffer::from_iter(self.device.clone(), BufferUsage::index_buffer(), false, mesh.indices.into_iter()).unwrap();
|
|
|
|
match mesh.vertices {
|
|
CPUVertexList::Vertex3D(verts) => {
|
|
let vertex_buffer = CpuAccessibleBuffer::from_iter(self.device.clone(), BufferUsage::vertex_buffer(), false, verts.into_iter()).unwrap();
|
|
self.game_data.meshes.push(Mesh { vertex_buffer, index_buffer, original_path });
|
|
self.game_data.meshes.len() - 1
|
|
},
|
|
CPUVertexList::VertexText(verts) => {
|
|
let vertex_buffer = CpuAccessibleBuffer::from_iter(self.device.clone(), BufferUsage::vertex_buffer(), false, verts.into_iter()).unwrap();
|
|
self.game_data.meshes_text.push(Mesh { vertex_buffer, index_buffer, original_path });
|
|
self.game_data.meshes_text.len() - 1
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn update_mesh(self: &mut Self, mesh_index: usize, vertices: CPUVertexList, indices: Vec<u32>) {
|
|
let index_buffer = CpuAccessibleBuffer::from_iter(self.device.clone(), BufferUsage::index_buffer(), false, indices.into_iter()).unwrap();
|
|
|
|
match vertices {
|
|
CPUVertexList::Vertex3D(verts) => {
|
|
let mesh = &mut self.game_data.meshes[mesh_index];
|
|
mesh.vertex_buffer = CpuAccessibleBuffer::from_iter(self.device.clone(), BufferUsage::vertex_buffer(), false, verts.into_iter()).unwrap();
|
|
mesh.index_buffer = index_buffer;
|
|
},
|
|
CPUVertexList::VertexText(verts) => {
|
|
let mesh = &mut self.game_data.meshes_text[mesh_index];
|
|
mesh.vertex_buffer = CpuAccessibleBuffer::from_iter(self.device.clone(), BufferUsage::vertex_buffer(), false, verts.into_iter()).unwrap();
|
|
mesh.index_buffer = index_buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn upload_texture(self: &mut Self, bytes: &[u8], width: u32, height: u32, format: Format, filter: Filter, wrap: SamplerAddressMode, device: Arc<Device>) -> Texture {
|
|
let dimensions = ImageDimensions::Dim2d { width, height, array_layers: 1 };
|
|
|
|
let usage = ImageUsage {
|
|
transfer_destination: true,
|
|
transfer_source: true,
|
|
sampled: true,
|
|
..ImageUsage::none()
|
|
};
|
|
|
|
let mip_maps = if format == Format::R8_UINT { MipmapsCount::One } else { MipmapsCount::Log2 };
|
|
|
|
let (image_view, initializer) = ImmutableImage::uninitialized(
|
|
device.clone(),
|
|
dimensions,
|
|
format,
|
|
mip_maps,
|
|
usage,
|
|
ImageCreateFlags::default(),
|
|
ImageLayout::ShaderReadOnlyOptimal,
|
|
device.active_queue_families(),
|
|
).unwrap();
|
|
|
|
let init = SubImage::new(
|
|
Arc::new(initializer),
|
|
0,
|
|
image_view.mipmap_levels(),
|
|
0,
|
|
1,
|
|
ImageLayout::ShaderReadOnlyOptimal,
|
|
);
|
|
|
|
let mut cbb = AutoCommandBufferBuilder::primary(device.clone(), self.queue.family(), CommandBufferUsage::OneTimeSubmit).unwrap();
|
|
|
|
let mut offset = 0;
|
|
|
|
let block_bytes = get_block_size(format);
|
|
|
|
let mut upload_bytes = |data: &[u8], mip_index: u32, mip_size: [u32; 3]| {
|
|
let source = CpuAccessibleBuffer::from_iter(
|
|
device.clone(),
|
|
BufferUsage::transfer_source(),
|
|
false,
|
|
data.iter().cloned(),
|
|
).unwrap();
|
|
|
|
cbb.copy_buffer_to_image_dimensions(
|
|
source.clone(),
|
|
init.clone(),
|
|
[0, 0, 0],
|
|
mip_size,
|
|
0,
|
|
dimensions.array_layers(),
|
|
mip_index,
|
|
).unwrap();
|
|
};
|
|
|
|
if let Some(block_byte_size) = block_bytes {
|
|
for i in 0..image_view.mipmap_levels() {
|
|
|
|
let mip_size = dimensions.mipmap_dimensions(i).unwrap().width_height_depth();
|
|
|
|
let mip_byte_size = (
|
|
(u32::max(4, mip_size[0]) / 4)
|
|
* (u32::max(4, mip_size[1]) / 4)
|
|
* block_byte_size) as usize;
|
|
|
|
let data = &bytes[offset..(offset + mip_byte_size)];
|
|
upload_bytes(data, i, mip_size);
|
|
offset += mip_byte_size;
|
|
}
|
|
} else {
|
|
let mut texture_bytes: Vec<u8> = bytes.to_vec();
|
|
texture_bytes.resize((width * height) as usize, 0u8);
|
|
upload_bytes(&texture_bytes, 0, dimensions.width_height_depth());
|
|
}
|
|
|
|
let cb = cbb.build().unwrap();
|
|
|
|
let future = match cb.execute(self.queue.clone()) {
|
|
Ok(f) => f,
|
|
Err(e) => unreachable!("{:?}", e)
|
|
};
|
|
|
|
future.flush().unwrap();
|
|
|
|
let sampler = Sampler::new(device.clone(), filter, filter,
|
|
MipmapMode::Linear, wrap, wrap, wrap,
|
|
0.0, 1.0, 0.0, (image_view.mipmap_levels() - 1) as f32).unwrap();
|
|
|
|
Texture { image: image_view, sampler }
|
|
}
|
|
|
|
pub fn update_texture(&mut self, tex_handle: TextureHandle, new_data: &[u8], new_data_dimensions: [u32; 3], new_data_offset: [u32; 3], device: Arc<Device>) {
|
|
let texture = &mut self.game_data.textures[tex_handle];
|
|
|
|
let old_sub_image = SubImage::new(
|
|
texture.image.clone(),
|
|
0,
|
|
1,
|
|
0,
|
|
1,
|
|
ImageLayout::ShaderReadOnlyOptimal,
|
|
);
|
|
|
|
let mut cbb = AutoCommandBufferBuilder::primary(device.clone(), self.queue.family(), CommandBufferUsage::OneTimeSubmit).unwrap();
|
|
|
|
let upload_source = CpuAccessibleBuffer::from_iter(
|
|
device.clone(),
|
|
BufferUsage::transfer_source(),
|
|
false,
|
|
new_data.iter().cloned(),
|
|
).unwrap();
|
|
|
|
cbb.copy_buffer_to_image_dimensions(
|
|
upload_source.clone(),
|
|
old_sub_image.clone(),
|
|
new_data_offset,
|
|
new_data_dimensions,
|
|
0,
|
|
1,
|
|
0,
|
|
).unwrap();
|
|
|
|
let cb = cbb.build().unwrap();
|
|
|
|
let future = cb.execute(self.queue.clone()).unwrap();
|
|
|
|
future.flush().unwrap();
|
|
}
|
|
|
|
pub fn resize_texture(&mut self, game_object: &mut GameObject, texture_handle: TextureHandle, new_size: ImageDimensions) {
|
|
let mut texture = &mut self.game_data.textures[texture_handle];
|
|
|
|
let new_image_usage = ImageUsage {
|
|
transfer_destination: true,
|
|
transfer_source: true,
|
|
sampled: true,
|
|
..ImageUsage::none()
|
|
};
|
|
|
|
let (new_image_view, new_image_initializer) = ImmutableImage::uninitialized(
|
|
self.device.clone(),
|
|
new_size,
|
|
texture.image.format(),
|
|
texture.image.mipmap_levels(),
|
|
new_image_usage,
|
|
ImageCreateFlags::default(),
|
|
ImageLayout::ShaderReadOnlyOptimal,
|
|
self.device.active_queue_families(),
|
|
).unwrap();
|
|
|
|
let old_sub_image = SubImage::new(
|
|
texture.image.clone(),
|
|
0,
|
|
1,
|
|
0,
|
|
1,
|
|
ImageLayout::ShaderReadOnlyOptimal,
|
|
);
|
|
|
|
let new_sub_image = SubImage::new(
|
|
Arc::new(new_image_initializer),
|
|
0,
|
|
new_image_view.mipmap_levels(),
|
|
0,
|
|
1,
|
|
ImageLayout::ShaderReadOnlyOptimal,
|
|
);
|
|
|
|
let mut cbb = AutoCommandBufferBuilder::primary(self.device.clone(), self.queue.family(), CommandBufferUsage::OneTimeSubmit).unwrap();
|
|
|
|
cbb.copy_image(
|
|
old_sub_image.clone(),
|
|
[0, 0, 0],
|
|
0,
|
|
0,
|
|
new_sub_image.clone(),
|
|
[10, 0, 0],
|
|
0,
|
|
0,
|
|
old_sub_image.dimensions().width_height_depth(),
|
|
1
|
|
).unwrap();
|
|
|
|
let cb = cbb.build().unwrap();
|
|
let future = cb.execute(self.queue.clone()).unwrap();
|
|
future.flush().unwrap();
|
|
|
|
texture.image = new_image_view;
|
|
game_object.init_descriptor_sets(self);
|
|
}
|
|
|
|
pub fn clear_all(&mut self) {
|
|
self.game_data.meshes.clear();
|
|
self.game_data.textures.clear();
|
|
}
|
|
}
|
|
|
|
pub fn start_event_loop(mut renderer: VulkanRenderer, mut game: Box<dyn Game>, event_loop: EventLoop<()>) {
|
|
PerformanceCounter::init_perf();
|
|
|
|
event_loop.run(move |event, _, control_flow| {
|
|
game.on_window_event(&event);
|
|
|
|
if renderer.game_data.shutdown {
|
|
*control_flow = ControlFlow::Exit;
|
|
}
|
|
|
|
match event {
|
|
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
|
*control_flow = ControlFlow::Exit;
|
|
},
|
|
Event::WindowEvent { event: WindowEvent::Resized(..), .. } => {
|
|
renderer.recreate_swapchain = true;
|
|
},
|
|
Event::RedrawRequested(..) => {
|
|
PerformanceCounter::perf_next_frame("renderer");
|
|
renderer.render_loop(game.get_ubo(), &game.get_game_objects());
|
|
},
|
|
Event::MainEventsCleared => {
|
|
PerformanceCounter::perf_next_frame("update");
|
|
PerformanceCounter::perf_next_frame("input_events");
|
|
game.update(&mut renderer);
|
|
renderer.surface.window().request_redraw();
|
|
},
|
|
_ => {}
|
|
}
|
|
});
|
|
}
|
|
|
|
pub fn create_viewport(width: f32, height: f32) -> Viewport {
|
|
Viewport {
|
|
origin: [0.0, 0.0],
|
|
dimensions: [width, height],
|
|
depth_range: 0.0..1.0,
|
|
}
|
|
// Viewport {
|
|
// origin: [0.0, height],
|
|
// dimensions: [width, -height],
|
|
// depth_range: 0.0..1.0,
|
|
// }
|
|
} |