aboutsummaryrefslogtreecommitdiff
path: root/stockton-render/src
diff options
context:
space:
mode:
Diffstat (limited to 'stockton-render/src')
-rw-r--r--stockton-render/src/culling.rs18
-rw-r--r--stockton-render/src/draw/camera.rs7
-rw-r--r--stockton-render/src/draw/context.rs132
-rw-r--r--stockton-render/src/draw/depth_buffer.rs298
-rw-r--r--stockton-render/src/draw/draw_buffers.rs2
-rw-r--r--stockton-render/src/draw/macros.rs2
-rw-r--r--stockton-render/src/draw/mod.rs3
-rw-r--r--stockton-render/src/draw/render.rs71
-rw-r--r--stockton-render/src/draw/target.rs15
-rw-r--r--stockton-render/src/draw/texture/block.rs63
-rw-r--r--stockton-render/src/draw/texture/chunk.rs207
-rw-r--r--stockton-render/src/draw/texture/image.rs639
-rw-r--r--stockton-render/src/draw/texture/load.rs352
-rw-r--r--stockton-render/src/draw/texture/loader.rs569
-rw-r--r--stockton-render/src/draw/texture/mod.rs22
-rw-r--r--stockton-render/src/draw/texture/repo.rs192
-rw-r--r--stockton-render/src/draw/texture/resolver.rs14
-rw-r--r--stockton-render/src/draw/texture/staging_buffer.rs56
-rw-r--r--stockton-render/src/draw/ui/mod.rs6
-rw-r--r--stockton-render/src/draw/ui/pipeline.rs6
-rw-r--r--stockton-render/src/draw/ui/render.rs30
-rwxr-xr-xstockton-render/src/draw/ui/texture.rs49
-rw-r--r--stockton-render/src/lib.rs33
-rw-r--r--stockton-render/src/types.rs5
-rw-r--r--stockton-render/src/window.rs25
25 files changed, 1534 insertions, 1282 deletions
diff --git a/stockton-render/src/culling.rs b/stockton-render/src/culling.rs
index 8ee2877..9b5844c 100644
--- a/stockton-render/src/culling.rs
+++ b/stockton-render/src/culling.rs
@@ -19,12 +19,12 @@
#![allow(dead_code)]
use stockton_levels::prelude::*;
-use stockton_levels::traits::tree::{BSPNode, BSPNodeValue};
+use stockton_levels::traits::tree::{BspNode, BspNodeValue};
use stockton_types::Vector3;
/// Get the visible faces according to visdata and frustum culling
// TODO: Write this. For now, just render all faces
-pub fn get_visible_faces<X: CoordSystem, T: MinBSPFeatures<X>>(pos: Vector3, file: &T) -> Vec<u32> {
+pub fn get_visible_faces<X: CoordSystem, T: MinBspFeatures<X>>(pos: Vector3, file: &T) -> Vec<u32> {
let vis_cluster = get_cluster_id(pos, file);
let mut visible = Vec::with_capacity(file.faces_len() as usize);
@@ -43,16 +43,16 @@ pub fn get_visible_faces<X: CoordSystem, T: MinBSPFeatures<X>>(pos: Vector3, fil
visible
}
-pub fn walk_bsp_tree<X: CoordSystem, T: MinBSPFeatures<X>>(
- node: &BSPNode,
+pub fn walk_bsp_tree<X: CoordSystem, T: MinBspFeatures<X>>(
+ node: &BspNode,
vis_cluster: u32,
visible_faces: &mut Vec<u32>,
file: &T,
) {
- if let BSPNodeValue::Children(front, back) = &node.value {
+ if let BspNodeValue::Children(front, back) = &node.value {
walk_bsp_tree(back, vis_cluster, visible_faces, file);
walk_bsp_tree(front, vis_cluster, visible_faces, file);
- } else if let BSPNodeValue::Leaf(leaf) = &node.value {
+ } else if let BspNodeValue::Leaf(leaf) = &node.value {
if (leaf.cluster_id & 0x80000000) != 0 {
// Negative means invalid leaf
return;
@@ -66,9 +66,9 @@ pub fn walk_bsp_tree<X: CoordSystem, T: MinBSPFeatures<X>>(
}
/// Get the viscluster pos lies in
-fn get_cluster_id<X: CoordSystem, T: MinBSPFeatures<X>>(pos: Vector3, file: &T) -> u32 {
+fn get_cluster_id<X: CoordSystem, T: MinBspFeatures<X>>(pos: Vector3, file: &T) -> u32 {
let mut node = file.get_bsp_root();
- while let BSPNodeValue::Children(front, back) = &node.value {
+ while let BspNodeValue::Children(front, back) = &node.value {
let plane = file.get_plane(node.plane_idx);
let dist = plane.normal.dot(&pos) - plane.dist;
@@ -79,7 +79,7 @@ fn get_cluster_id<X: CoordSystem, T: MinBSPFeatures<X>>(pos: Vector3, file: &T)
}
}
- if let BSPNodeValue::Leaf(leaf) = &node.value {
+ if let BspNodeValue::Leaf(leaf) = &node.value {
leaf.cluster_id
} else {
panic!("should have had a leaf but didn't");
diff --git a/stockton-render/src/draw/camera.rs b/stockton-render/src/draw/camera.rs
index aa5efac..15692ac 100644
--- a/stockton-render/src/draw/camera.rs
+++ b/stockton-render/src/draw/camera.rs
@@ -21,6 +21,7 @@ use legion::maybe_changed;
use nalgebra_glm::look_at_lh;
use nalgebra_glm::perspective_lh_zo;
+use stockton_levels::prelude::{MinBspFeatures, VulkanSystem};
use crate::Renderer;
use stockton_types::components::{CameraSettings, Transform};
@@ -40,10 +41,10 @@ fn euler_to_direction(euler: &Vector3) -> Vector3 {
#[system(for_each)]
#[filter(maybe_changed::<Transform>() | maybe_changed::<CameraSettings>())]
-pub fn calc_vp_matrix(
+pub fn calc_vp_matrix<M: 'static + MinBspFeatures<VulkanSystem>>(
transform: &Transform,
settings: &CameraSettings,
- #[resource] renderer: &mut Renderer<'static>,
+ #[resource] renderer: &mut Renderer<'static, M>,
) {
let ratio = renderer.context.target_chain.properties.extent.width as f32
/ renderer.context.target_chain.properties.extent.height as f32;
@@ -54,7 +55,7 @@ pub fn calc_vp_matrix(
let view_matrix = look_at_lh(
&transform.position,
&(transform.position + direction),
- &Vector3::new(0.0, 1.0, 0.0), // TODO
+ &Vector3::new(0.0, 1.0, 0.0),
);
// Converts camera space to screen space
diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs
index 139c59a..b65e078 100644
--- a/stockton-render/src/draw/context.rs
+++ b/stockton-render/src/draw/context.rs
@@ -19,7 +19,7 @@
//! In the end, this takes in a depth-sorted list of faces and a map file and renders them.
//! You'll need something else to actually find/sort the faces though.
-use std::{mem::ManuallyDrop, ops::Deref};
+use std::{mem::ManuallyDrop, pin::Pin};
use arrayvec::ArrayVec;
use hal::{pool::CommandPoolCreateFlags, prelude::*};
@@ -30,27 +30,32 @@ use winit::window::Window;
use super::{
buffer::ModifiableBuffer,
- draw_buffers::{DrawBuffers, UVPoint},
+ draw_buffers::{DrawBuffers, UvPoint},
pipeline::CompletePipeline,
render::do_render,
target::{SwapchainProperties, TargetChain},
- texture::TextureStore,
- ui::{do_render as do_render_ui, ensure_textures as ensure_textures_ui, UIPipeline, UIPoint},
+ texture::{resolver::BasicFsResolver, TextureRepo},
+ ui::{
+ do_render as do_render_ui, ensure_textures as ensure_textures_ui, UiPipeline, UiPoint,
+ UiTextures,
+ },
utils::find_memory_type_id,
};
-use crate::{error, types::*, window::UIState};
+use crate::{error, types::*, window::UiState};
use stockton_levels::prelude::*;
/// Contains all the hal related stuff.
/// In the end, this takes in a depth-sorted list of faces and a map file and renders them.
// TODO: Settings for clear colour, buffer sizes, etc
-pub struct RenderingContext<'a> {
+pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> {
+ pub map: Pin<Box<M>>,
+
// Parents for most of these things
/// Vulkan Instance
instance: ManuallyDrop<back::Instance>,
/// Device we're using
- device: ManuallyDrop<Device>,
+ device: Pin<Box<Device>>,
/// Adapter we're using
adapter: Adapter,
@@ -66,7 +71,7 @@ pub struct RenderingContext<'a> {
pipeline: ManuallyDrop<CompletePipeline>,
/// 2D Graphics pipeline and associated objects
- ui_pipeline: ManuallyDrop<UIPipeline>,
+ ui_pipeline: ManuallyDrop<UiPipeline>,
// Command pool and buffers
/// The command pool used for our buffers
@@ -75,17 +80,17 @@ pub struct RenderingContext<'a> {
/// The queue group our buffers belong to
queue_group: QueueGroup,
- /// Texture store
- texture_store: ManuallyDrop<TextureStore>,
+ /// Main Texture repo
+ tex_repo: ManuallyDrop<TextureRepo<'a>>,
- /// Texture store for UI
- ui_texture_store: ManuallyDrop<TextureStore>,
+ /// UI Texture repo
+ ui_tex_repo: ManuallyDrop<TextureRepo<'a>>,
/// Buffers used for drawing
- draw_buffers: ManuallyDrop<DrawBuffers<'a, UVPoint>>,
+ draw_buffers: ManuallyDrop<DrawBuffers<'a, UvPoint>>,
/// Buffers used for drawing the UI
- ui_draw_buffers: ManuallyDrop<DrawBuffers<'a, UIPoint>>,
+ ui_draw_buffers: ManuallyDrop<DrawBuffers<'a, UiPoint>>,
/// Memory allocator used for any sort of textures / maps
/// Guaranteed suitable for 2D RGBA images with `Optimal` tiling and `Usage::Sampled`
@@ -97,9 +102,11 @@ pub struct RenderingContext<'a> {
pub(crate) pixels_per_point: f32,
}
-impl<'a> RenderingContext<'a> {
+impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> {
/// Create a new RenderingContext for the given window.
- pub fn new<T: HasTextures>(window: &Window, file: &T) -> Result<Self, error::CreationError> {
+ pub fn new(window: &Window, map: M) -> Result<Self, error::CreationError> {
+ let map = Box::pin(map);
+
// Create surface
let (instance, mut surface, mut adapters) = unsafe {
use hal::Instance;
@@ -115,10 +122,10 @@ impl<'a> RenderingContext<'a> {
};
// TODO: Properly figure out which adapter to use
- let mut adapter = adapters.remove(0);
+ let adapter = adapters.remove(0);
// Device & Queue group
- let (mut device, mut queue_group) = {
+ let (mut device, queue_group) = {
let family = adapter
.queue_families
.iter()
@@ -134,7 +141,7 @@ impl<'a> RenderingContext<'a> {
.unwrap()
};
- (gpu.device, gpu.queue_groups.pop().unwrap())
+ (Box::pin(gpu.device), gpu.queue_groups.pop().unwrap())
};
// Figure out what our swapchain will look like
@@ -159,7 +166,7 @@ impl<'a> RenderingContext<'a> {
let ui_draw_buffers = DrawBuffers::new(&mut device, &adapter)?;
// Memory allocators
- let mut texture_allocator = unsafe {
+ let texture_allocator = unsafe {
use hal::{
format::Format,
image::{Kind, Tiling, Usage, ViewCapabilities},
@@ -204,31 +211,32 @@ impl<'a> RenderingContext<'a> {
)
};
- // Texture store
- let texture_store = TextureStore::new(
- &mut device,
- &mut adapter,
- &mut texture_allocator,
- &mut queue_group.queues[0],
- &mut cmd_pool,
- file,
- )?;
+ // Texture repos
+ let long_device_pointer = unsafe { &mut *(&mut *device as *mut Device) };
+ let long_texs_pointer: &'static M = unsafe { &*(&*map as *const M) };
- // Texture store for UI elements
- let ui_texture_store = TextureStore::new_empty(
- &mut device,
- &mut adapter,
- &mut texture_allocator,
- &mut queue_group.queues[0],
- &mut cmd_pool,
- 1, // TODO
- )?;
+ let tex_repo = TextureRepo::new(
+ long_device_pointer,
+ &adapter,
+ long_texs_pointer,
+ BasicFsResolver::new(std::path::Path::new(".")),
+ )
+ .unwrap(); // TODO
+ let long_device_pointer = unsafe { &mut *(&mut *device as *mut Device) };
+
+ let ui_tex_repo = TextureRepo::new(
+ long_device_pointer,
+ &adapter,
+ &UiTextures,
+ BasicFsResolver::new(std::path::Path::new(".")),
+ )
+ .unwrap(); // TODO
let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new();
- descriptor_set_layouts.push(texture_store.descriptor_set_layout.deref());
+ descriptor_set_layouts.push(tex_repo.get_ds_layout());
let mut ui_descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new();
- ui_descriptor_set_layouts.push(ui_texture_store.descriptor_set_layout.deref());
+ ui_descriptor_set_layouts.push(tex_repo.get_ds_layout());
// Graphics pipeline
let pipeline = CompletePipeline::new(
@@ -239,7 +247,7 @@ impl<'a> RenderingContext<'a> {
)?;
// UI pipeline
- let ui_pipeline = UIPipeline::new(
+ let ui_pipeline = UiPipeline::new(
&mut device,
swapchain_properties.extent,
&swapchain_properties,
@@ -260,10 +268,11 @@ impl<'a> RenderingContext<'a> {
.map_err(error::CreationError::TargetChainCreationError)?;
Ok(RenderingContext {
+ map,
instance: ManuallyDrop::new(instance),
surface: ManuallyDrop::new(surface),
- device: ManuallyDrop::new(device),
+ device,
adapter,
queue_group,
@@ -273,13 +282,12 @@ impl<'a> RenderingContext<'a> {
pipeline: ManuallyDrop::new(pipeline),
ui_pipeline: ManuallyDrop::new(ui_pipeline),
- texture_store: ManuallyDrop::new(texture_store),
+ tex_repo: ManuallyDrop::new(tex_repo),
+ ui_tex_repo: ManuallyDrop::new(ui_tex_repo),
draw_buffers: ManuallyDrop::new(draw_buffers),
ui_draw_buffers: ManuallyDrop::new(ui_draw_buffers),
- ui_texture_store: ManuallyDrop::new(ui_texture_store),
-
texture_allocator: ManuallyDrop::new(texture_allocator),
vp_matrix: Mat4::identity(),
@@ -304,7 +312,7 @@ impl<'a> RenderingContext<'a> {
ManuallyDrop::into_inner(read(&self.pipeline)).deactivate(&mut self.device);
self.pipeline = ManuallyDrop::new({
let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new();
- descriptor_set_layouts.push(self.texture_store.descriptor_set_layout.deref());
+ descriptor_set_layouts.push(self.tex_repo.get_ds_layout());
CompletePipeline::new(
&mut self.device,
@@ -319,9 +327,9 @@ impl<'a> RenderingContext<'a> {
ManuallyDrop::into_inner(read(&self.ui_pipeline)).deactivate(&mut self.device);
self.ui_pipeline = ManuallyDrop::new({
let mut descriptor_set_layouts: ArrayVec<[_; 1]> = ArrayVec::new();
- descriptor_set_layouts.push(self.ui_texture_store.descriptor_set_layout.deref());
+ descriptor_set_layouts.push(self.ui_tex_repo.get_ds_layout());
- UIPipeline::new(
+ UiPipeline::new(
&mut self.device,
properties.extent,
&properties,
@@ -349,15 +357,10 @@ impl<'a> RenderingContext<'a> {
}
/// Draw all vertices in the buffer
- pub fn draw_vertices<M: MinBSPFeatures<VulkanSystem>>(
- &mut self,
- file: &M,
- ui: &mut UIState,
- faces: &[u32],
- ) -> Result<(), &'static str> {
+ pub fn draw_vertices(&mut self, ui: &mut UiState, faces: &[u32]) -> Result<(), &'static str> {
// Ensure UI texture(s) are loaded
ensure_textures_ui(
- &mut self.ui_texture_store,
+ &mut self.ui_tex_repo,
ui,
&mut self.device,
&mut self.adapter,
@@ -366,6 +369,9 @@ impl<'a> RenderingContext<'a> {
&mut self.cmd_pool,
);
+ // Get any textures that just finished loading
+ self.tex_repo.process_responses();
+
// 3D Pass
let cmd_buffer = self.target_chain.prep_next_target(
&mut self.device,
@@ -376,9 +382,9 @@ impl<'a> RenderingContext<'a> {
do_render(
cmd_buffer,
&mut self.draw_buffers,
- &self.texture_store,
+ &mut self.tex_repo,
&self.pipeline.pipeline_layout,
- file,
+ &*self.map,
faces,
);
@@ -390,7 +396,7 @@ impl<'a> RenderingContext<'a> {
cmd_buffer,
&self.ui_pipeline.pipeline_layout,
&mut self.ui_draw_buffers,
- &mut self.ui_texture_store,
+ &mut self.ui_tex_repo,
ui,
);
@@ -427,7 +433,7 @@ impl<'a> RenderingContext<'a> {
}
}
-impl<'a> core::ops::Drop for RenderingContext<'a> {
+impl<'a, M: MinBspFeatures<VulkanSystem>> core::ops::Drop for RenderingContext<'a, M> {
fn drop(&mut self) {
self.device.wait_idle().unwrap();
@@ -436,10 +442,8 @@ impl<'a> core::ops::Drop for RenderingContext<'a> {
ManuallyDrop::into_inner(read(&self.draw_buffers)).deactivate(&mut self.device);
ManuallyDrop::into_inner(read(&self.ui_draw_buffers)).deactivate(&mut self.device);
- ManuallyDrop::into_inner(read(&self.texture_store))
- .deactivate(&mut self.device, &mut self.texture_allocator);
- ManuallyDrop::into_inner(read(&self.ui_texture_store))
- .deactivate(&mut self.device, &mut self.texture_allocator);
+ ManuallyDrop::into_inner(read(&self.tex_repo)).deactivate(&mut self.device);
+ ManuallyDrop::into_inner(read(&self.ui_tex_repo)).deactivate(&mut self.device);
ManuallyDrop::into_inner(read(&self.texture_allocator)).dispose();
@@ -454,8 +458,6 @@ impl<'a> core::ops::Drop for RenderingContext<'a> {
self.instance
.destroy_surface(ManuallyDrop::into_inner(read(&self.surface)));
-
- ManuallyDrop::drop(&mut self.device);
}
}
}
diff --git a/stockton-render/src/draw/depth_buffer.rs b/stockton-render/src/draw/depth_buffer.rs
new file mode 100644
index 0000000..14b4d30
--- /dev/null
+++ b/stockton-render/src/draw/depth_buffer.rs
@@ -0,0 +1,298 @@
+use crate::draw::buffer::create_buffer;
+use gfx_hal::{format::Aspects, memory::Properties, queue::Submission, MemoryTypeId};
+use hal::{
+ buffer::Usage as BufUsage,
+ format::{Format, Swizzle},
+ image::{SubresourceRange, Usage, ViewKind},
+ memory,
+};
+use std::convert::TryInto;
+
+use crate::types::*;
+use hal::prelude::*;
+use std::mem::ManuallyDrop;
+
+use super::texture::{LoadableImage, PIXEL_SIZE};
+
+/// Holds an image that's loaded into GPU memory dedicated only to that image, bypassing the memory allocator.
+pub struct DedicatedLoadedImage {
+ /// The GPU Image handle
+ image: ManuallyDrop<Image>,
+
+ /// The full view of the image
+ pub image_view: ManuallyDrop<ImageView>,
+
+ /// The memory backing the image
+ memory: ManuallyDrop<Memory>,
+}
+
+impl DedicatedLoadedImage {
+ pub fn new(
+ device: &mut Device,
+ adapter: &Adapter,
+ format: Format,
+ usage: Usage,
+ resources: SubresourceRange,
+ width: usize,
+ height: usize,
+ ) -> Result<DedicatedLoadedImage, &'static str> {
+ let (memory, image_ref) = {
+ // Round up the size to align properly
+ let initial_row_size = PIXEL_SIZE * width;
+ let limits = adapter.physical_device.limits();
+ let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
+
+ let row_size =
+ ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
+ debug_assert!(row_size as usize >= initial_row_size);
+
+ // Make the image
+ let mut image_ref = unsafe {
+ use hal::image::{Kind, Tiling, ViewCapabilities};
+
+ device.create_image(
+ Kind::D2(width as u32, height as u32, 1, 1),
+ 1,
+ format,
+ Tiling::Optimal,
+ usage,
+ ViewCapabilities::empty(),
+ )
+ }
+ .map_err(|_| "Couldn't create image")?;
+
+ // Allocate memory
+
+ // Allocate memory
+ let memory = unsafe {
+ let requirements = device.get_image_requirements(&image_ref);
+
+ let memory_type_id = adapter
+ .physical_device
+ .memory_properties()
+ .memory_types
+ .iter()
+ .enumerate()
+ .find(|&(id, memory_type)| {
+ requirements.type_mask & (1 << id) != 0
+ && memory_type.properties.contains(Properties::DEVICE_LOCAL)
+ })
+ .map(|(id, _)| MemoryTypeId(id))
+ .ok_or("Couldn't find a memory type for image memory")?;
+
+ let memory = device
+ .allocate_memory(memory_type_id, requirements.size)
+ .map_err(|_| "Couldn't allocate image memory")?;
+
+ device
+ .bind_image_memory(&memory, 0, &mut image_ref)
+ .map_err(|_| "Couldn't bind memory to image")?;
+
+ Ok(memory)
+ }?;
+
+ Ok((memory, image_ref))
+ }?;
+
+ // Create ImageView and sampler
+ let image_view = unsafe {
+ device.create_image_view(&image_ref, ViewKind::D2, format, Swizzle::NO, resources)
+ }
+ .map_err(|_| "Couldn't create the image view!")?;
+
+ Ok(DedicatedLoadedImage {
+ image: ManuallyDrop::new(image_ref),
+ image_view: ManuallyDrop::new(image_view),
+ memory: ManuallyDrop::new(memory),
+ })
+ }
+
+ /// Load the given image
+ pub fn load<T: LoadableImage>(
+ &mut self,
+ img: T,
+ device: &mut Device,
+ adapter: &Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ ) -> Result<(), &'static str> {
+ let initial_row_size = PIXEL_SIZE * img.width() as usize;
+ let limits = adapter.physical_device.limits();
+ let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
+
+ let row_size =
+ ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
+ let total_size = (row_size * (img.height() as usize)) as u64;
+ debug_assert!(row_size as usize >= initial_row_size);
+
+ // Make a staging buffer
+ let (staging_buffer, staging_memory) = create_buffer(
+ device,
+ adapter,
+ BufUsage::TRANSFER_SRC,
+ memory::Properties::CPU_VISIBLE | memory::Properties::COHERENT,
+ total_size,
+ )
+ .map_err(|_| "Couldn't create staging buffer")?;
+
+ // Copy everything into it
+ unsafe {
+ let mapped_memory: *mut u8 = std::mem::transmute(
+ device
+ .map_memory(&staging_memory, 0..total_size)
+ .map_err(|_| "Couldn't map buffer memory")?,
+ );
+
+ for y in 0..img.height() as usize {
+ let dest_base: isize = (y * row_size).try_into().unwrap();
+ img.copy_row(y as u32, mapped_memory.offset(dest_base));
+ }
+
+ device.unmap_memory(&staging_memory);
+ }
+
+ // Copy from staging to image memory
+ let buf = unsafe {
+ use hal::command::{BufferImageCopy, CommandBufferFlags};
+ use hal::image::{Access, Extent, Layout, Offset, SubresourceLayers};
+ use hal::memory::Barrier;
+ use hal::pso::PipelineStage;
+
+ // Get a command buffer
+ let mut buf = command_pool.allocate_one(hal::command::Level::Primary);
+ buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT);
+
+ // Setup the layout of our image for copying
+ let image_barrier = Barrier::Image {
+ states: (Access::empty(), Layout::Undefined)
+ ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal),
+ target: &(*self.image),
+ families: None,
+ range: SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ },
+ };
+ buf.pipeline_barrier(
+ PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER,
+ memory::Dependencies::empty(),
+ &[image_barrier],
+ );
+
+ // Copy from buffer to image
+ buf.copy_buffer_to_image(
+ &staging_buffer,
+ &(*self.image),
+ Layout::TransferDstOptimal,
+ &[BufferImageCopy {
+ buffer_offset: 0,
+ buffer_width: (row_size / PIXEL_SIZE) as u32,
+ buffer_height: img.height(),
+ image_layers: SubresourceLayers {
+ aspects: Aspects::COLOR,
+ level: 0,
+ layers: 0..1,
+ },
+ image_offset: Offset { x: 0, y: 0, z: 0 },
+ image_extent: Extent {
+ width: img.width(),
+ height: img.height(),
+ depth: 1,
+ },
+ }],
+ );
+
+ // Setup the layout of our image for shaders
+ let image_barrier = Barrier::Image {
+ states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal)
+ ..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal),
+ target: &(*self.image),
+ families: None,
+ range: SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ },
+ };
+
+ buf.pipeline_barrier(
+ PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER,
+ memory::Dependencies::empty(),
+ &[image_barrier],
+ );
+
+ buf.finish();
+
+ buf
+ };
+
+ // Submit our commands and wait for them to finish
+ unsafe {
+ let setup_finished = device.create_fence(false).unwrap();
+ command_queue.submit::<_, _, Semaphore, _, _>(
+ Submission {
+ command_buffers: &[&buf],
+ wait_semaphores: std::iter::empty::<_>(),
+ signal_semaphores: std::iter::empty::<_>(),
+ },
+ Some(&setup_finished),
+ );
+
+ device
+ .wait_for_fence(&setup_finished, core::u64::MAX)
+ .unwrap();
+ device.destroy_fence(setup_finished);
+ };
+
+ // Clean up temp resources
+ unsafe {
+ command_pool.free(std::iter::once(buf));
+
+ device.free_memory(staging_memory);
+ device.destroy_buffer(staging_buffer);
+ }
+
+ Ok(())
+ }
+
+ /// Load the given image into a new buffer
+ pub fn load_into_new<T: LoadableImage>(
+ img: T,
+ device: &mut Device,
+ adapter: &Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ format: Format,
+ usage: Usage,
+ ) -> Result<DedicatedLoadedImage, &'static str> {
+ let mut loaded_image = Self::new(
+ device,
+ adapter,
+ format,
+ usage | Usage::TRANSFER_DST,
+ SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ },
+ img.width() as usize,
+ img.height() as usize,
+ )?;
+ loaded_image.load(img, device, adapter, command_queue, command_pool)?;
+
+ Ok(loaded_image)
+ }
+
+ /// Properly frees/destroys all the objects in this struct
+ /// Dropping without doing this is a bad idea
+ pub fn deactivate(self, device: &mut Device) {
+ unsafe {
+ use core::ptr::read;
+
+ device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view)));
+ device.destroy_image(ManuallyDrop::into_inner(read(&self.image)));
+ device.free_memory(ManuallyDrop::into_inner(read(&self.memory)));
+ }
+ }
+}
diff --git a/stockton-render/src/draw/draw_buffers.rs b/stockton-render/src/draw/draw_buffers.rs
index 02625ad..72029ba 100644
--- a/stockton-render/src/draw/draw_buffers.rs
+++ b/stockton-render/src/draw/draw_buffers.rs
@@ -22,7 +22,7 @@ use stockton_types::{Vector2, Vector3};
/// Represents a point of a triangle, including UV and texture information.
#[derive(Debug, Clone, Copy)]
-pub struct UVPoint(pub Vector3, pub i32, pub Vector2);
+pub struct UvPoint(pub Vector3, pub i32, pub Vector2);
/// Initial size of vertex buffer. TODO: Way of overriding this
pub const INITIAL_VERT_SIZE: u64 = 3 * 3000;
diff --git a/stockton-render/src/draw/macros.rs b/stockton-render/src/draw/macros.rs
index e1ee515..1dd081d 100644
--- a/stockton-render/src/draw/macros.rs
+++ b/stockton-render/src/draw/macros.rs
@@ -28,9 +28,11 @@
/// );
/// ```
/// See the hal::pso::Format enum for possible types
+#[allow(clippy::vec_init_then_push)]
macro_rules! pipeline_vb_attributes {
// Special case for single item
( $binding:expr, $firstSize:expr; $firstType:ident ) => ({
+ #![allow(clippy::vec_init_then_push)]
vec![
AttributeDesc {
location: 0,
diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs
index 27e41cf..05e3b2b 100644
--- a/stockton-render/src/draw/mod.rs
+++ b/stockton-render/src/draw/mod.rs
@@ -24,6 +24,7 @@ mod macros;
mod buffer;
mod camera;
mod context;
+mod depth_buffer;
mod draw_buffers;
mod pipeline;
mod render;
@@ -33,4 +34,4 @@ mod utils;
pub use self::camera::calc_vp_matrix_system;
pub use self::context::RenderingContext;
-pub use self::draw_buffers::UVPoint;
+pub use self::draw_buffers::UvPoint;
diff --git a/stockton-render/src/draw/render.rs b/stockton-render/src/draw/render.rs
index 093e257..b713a8d 100644
--- a/stockton-render/src/draw/render.rs
+++ b/stockton-render/src/draw/render.rs
@@ -17,8 +17,7 @@
use crate::draw::draw_buffers::INITIAL_INDEX_SIZE;
use crate::draw::draw_buffers::INITIAL_VERT_SIZE;
-use crate::draw::texture::TextureStore;
-use crate::draw::UVPoint;
+use crate::draw::UvPoint;
use arrayvec::ArrayVec;
use faces::FaceType;
use hal::prelude::*;
@@ -29,10 +28,32 @@ use stockton_types::Vector2;
use crate::draw::draw_buffers::DrawBuffers;
use crate::types::*;
-pub fn do_render<M: MinBSPFeatures<VulkanSystem>>(
+use super::texture::TextureRepo;
+
+fn draw_or_queue(
+ current_chunk: usize,
+ tex_repo: &mut TextureRepo,
+ cmd_buffer: &mut CommandBuffer,
+ pipeline_layout: &PipelineLayout,
+ chunk_start: u32,
+ curr_idx_idx: u32,
+) {
+ if let Some(ds) = tex_repo.attempt_get_descriptor_set(current_chunk) {
+ let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
+ descriptor_sets.push(ds);
+ unsafe {
+ cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, descriptor_sets, &[]);
+ cmd_buffer.draw_indexed(chunk_start * 3..(curr_idx_idx * 3) + 1, 0, 0..1);
+ }
+ } else {
+ tex_repo.queue_load(current_chunk).unwrap()
+ }
+}
+
+pub fn do_render<M: MinBspFeatures<VulkanSystem>>(
cmd_buffer: &mut CommandBuffer,
- draw_buffers: &mut DrawBuffers<UVPoint>,
- texture_store: &TextureStore,
+ draw_buffers: &mut DrawBuffers<UvPoint>,
+ tex_repo: &mut TextureRepo,
pipeline_layout: &PipelineLayout,
file: &M,
faces: &[u32],
@@ -46,17 +67,15 @@ pub fn do_render<M: MinBSPFeatures<VulkanSystem>>(
for face in faces.iter().map(|idx| file.get_face(*idx)) {
if current_chunk != face.texture_idx as usize / 8 {
- // Last index was last of group, so draw it all.
- let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
- descriptor_sets.push(texture_store.get_chunk_descriptor_set(current_chunk));
- unsafe {
- cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, descriptor_sets, &[]);
- cmd_buffer.draw_indexed(
- chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1,
- 0,
- 0..1,
- );
- }
+ // Last index was last of group, so draw it all if textures are loaded.
+ draw_or_queue(
+ current_chunk,
+ tex_repo,
+ cmd_buffer,
+ pipeline_layout,
+ chunk_start as u32,
+ curr_idx_idx as u32,
+ );
// Next group of same-chunked faces starts here.
chunk_start = curr_idx_idx;
@@ -74,7 +93,7 @@ pub fn do_render<M: MinBSPFeatures<VulkanSystem>>(
let vert = &file.resolve_meshvert(idx2 as u32, base);
let uv = Vector2::new(vert.tex.u[0], vert.tex.v[0]);
- let uvp = UVPoint(vert.position, face.texture_idx.try_into().unwrap(), uv);
+ let uvp = UvPoint(vert.position, face.texture_idx.try_into().unwrap(), uv);
draw_buffers.vertex_buffer[curr_vert_idx] = uvp;
curr_vert_idx += 1;
@@ -104,14 +123,12 @@ pub fn do_render<M: MinBSPFeatures<VulkanSystem>>(
}
// Draw the final group of chunks
- let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
- descriptor_sets.push(texture_store.get_chunk_descriptor_set(current_chunk));
- unsafe {
- cmd_buffer.bind_graphics_descriptor_sets(&pipeline_layout, 0, descriptor_sets, &[]);
- cmd_buffer.draw_indexed(
- chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1,
- 0,
- 0..1,
- );
- }
+ draw_or_queue(
+ current_chunk,
+ tex_repo,
+ cmd_buffer,
+ pipeline_layout,
+ chunk_start as u32,
+ curr_idx_idx as u32,
+ );
}
diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs
index 2183a90..d7c070e 100644
--- a/stockton-render/src/draw/target.rs
+++ b/stockton-render/src/draw/target.rs
@@ -32,10 +32,10 @@ use na::Mat4;
use super::{
buffer::ModifiableBuffer,
- draw_buffers::{DrawBuffers, UVPoint},
+ depth_buffer::DedicatedLoadedImage,
+ draw_buffers::{DrawBuffers, UvPoint},
pipeline::CompletePipeline,
- texture::image::DedicatedLoadedImage,
- ui::{UIPipeline, UIPoint},
+ ui::{UiPipeline, UiPoint},
};
use crate::types::*;
@@ -170,7 +170,7 @@ impl TargetChain {
adapter: &Adapter,
surface: &mut Surface,
pipeline: &CompletePipeline,
- ui_pipeline: &UIPipeline,
+ ui_pipeline: &UiPipeline,
cmd_pool: &mut CommandPool,
properties: SwapchainProperties,
old_swapchain: Option<Swapchain>,
@@ -288,7 +288,7 @@ impl TargetChain {
pub fn prep_next_target<'a>(
&'a mut self,
device: &mut Device,
- draw_buffers: &mut DrawBuffers<UVPoint>,
+ draw_buffers: &mut DrawBuffers<UvPoint>,
pipeline: &CompletePipeline,
vp: &Mat4,
) -> Result<&'a mut crate::types::CommandBuffer, &'static str> {
@@ -386,8 +386,8 @@ impl TargetChain {
pub fn target_2d_pass<'a>(
&'a mut self,
- draw_buffers: &mut DrawBuffers<UIPoint>,
- pipeline: &UIPipeline,
+ draw_buffers: &mut DrawBuffers<UiPoint>,
+ pipeline: &UiPipeline,
) -> Result<&'a mut CommandBuffer, &'static str> {
let target = &mut self.targets[self.last_image as usize];
@@ -487,7 +487,6 @@ impl TargetChain {
.map_err(|_| "FrameError::PresentError")?;
};
- // TODO
Ok(())
}
}
diff --git a/stockton-render/src/draw/texture/block.rs b/stockton-render/src/draw/texture/block.rs
new file mode 100644
index 0000000..7735f5c
--- /dev/null
+++ b/stockton-render/src/draw/texture/block.rs
@@ -0,0 +1,63 @@
+use super::{loader::BlockRef, repo::BLOCK_SIZE};
+use crate::types::*;
+
+use arrayvec::ArrayVec;
+use hal::prelude::*;
+use rendy_memory::{Allocator, Block};
+use std::{iter::once, mem::ManuallyDrop};
+
+pub struct TexturesBlock<B: Block<back::Backend>> {
+ pub id: BlockRef,
+ pub descriptor_set: ManuallyDrop<RDescriptorSet>,
+ pub imgs: ArrayVec<[LoadedImage<B>; BLOCK_SIZE]>,
+}
+
+impl<B: Block<back::Backend>> TexturesBlock<B> {
+ pub fn deactivate<T: Allocator<back::Backend, Block = B>>(
+ mut self,
+ device: &mut Device,
+ tex_alloc: &mut T,
+ desc_alloc: &mut DescriptorAllocator,
+ ) {
+ unsafe {
+ use std::ptr::read;
+
+ // Descriptor set
+ desc_alloc.free(once(read(&*self.descriptor_set)));
+
+ // Images
+ self.imgs
+ .drain(..)
+ .map(|x| x.deactivate(device, tex_alloc))
+ .for_each(|_| {});
+ }
+ }
+}
+
+pub struct LoadedImage<B: Block<back::Backend>> {
+ pub mem: ManuallyDrop<B>,
+ pub img: ManuallyDrop<Image>,
+ pub img_view: ManuallyDrop<ImageView>,
+ pub sampler: ManuallyDrop<Sampler>,
+ pub row_size: usize,
+ pub height: u32,
+ pub width: u32,
+}
+
+impl<B: Block<back::Backend>> LoadedImage<B> {
+ pub fn deactivate<T: Allocator<back::Backend, Block = B>>(
+ self,
+ device: &mut Device,
+ alloc: &mut T,
+ ) {
+ unsafe {
+ use std::ptr::read;
+
+ device.destroy_image_view(read(&*self.img_view));
+ device.destroy_image(read(&*self.img));
+ device.destroy_sampler(read(&*self.sampler));
+
+ alloc.free(device, read(&*self.mem));
+ }
+ }
+}
diff --git a/stockton-render/src/draw/texture/chunk.rs b/stockton-render/src/draw/texture/chunk.rs
deleted file mode 100644
index 0cb6737..0000000
--- a/stockton-render/src/draw/texture/chunk.rs
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) Oscar Shrimpton 2020
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation, either version 3 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-//! A chunk of textures is an array of textures, the size of which is known at compile time.
-//! This reduces the number of times we need to re-bind our descriptor sets
-
-use crate::draw::texture::image::LoadableImage;
-use hal::prelude::*;
-use image::{Rgba, RgbaImage};
-
-use core::mem::replace;
-use std::ops::Deref;
-
-use crate::{error, types::*};
-
-use super::image::SampledImage;
-use super::resolver::TextureResolver;
-use log::debug;
-use std::iter::Iterator;
-use stockton_levels::traits::textures::Texture;
-
-/// The size of a chunk. Needs to match up with the fragment shader
-pub const CHUNK_SIZE: usize = 8;
-
-/// An array of textures
-pub struct TextureChunk {
- pub(crate) descriptor_set: DescriptorSet,
- sampled_images: Vec<SampledImage>,
-}
-
-impl TextureChunk {
- /// Create a new empty texture chunk
- pub fn new_empty(
- device: &mut Device,
- adapter: &mut Adapter,
- allocator: &mut DynamicAllocator,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- descriptor_set: DescriptorSet,
- ) -> Result<TextureChunk, error::CreationError> {
- let mut store = TextureChunk {
- descriptor_set,
- sampled_images: Vec::with_capacity(CHUNK_SIZE),
- };
-
- for i in 0..CHUNK_SIZE {
- debug!("Putting a placeholder in slot {}", i);
- store
- .put_texture(
- RgbaImage::from_pixel(1, 1, Rgba([0, 0, 0, 1])),
- i,
- device,
- adapter,
- allocator,
- command_queue,
- command_pool,
- )
- .unwrap();
- }
-
- Ok(store)
- }
-
- /// Create a new texture chunk and load in the textures specified by `range` from `file` using `resolver`
- /// Can error if the descriptor pool is too small or if a texture isn't found
- pub fn new<'a, I, R: TextureResolver<T>, T: LoadableImage>(
- device: &mut Device,
- adapter: &mut Adapter,
- allocator: &mut DynamicAllocator,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- descriptor_set: DescriptorSet,
- textures: I,
- resolver: &mut R,
- ) -> Result<TextureChunk, error::CreationError>
- where
- I: 'a + Iterator<Item = &'a Texture>,
- {
- let mut store = TextureChunk {
- descriptor_set,
- sampled_images: Vec::with_capacity(CHUNK_SIZE),
- };
-
- let mut local_idx = 0;
-
- debug!("Created descriptor set");
- for tex in textures {
- if let Some(img) = resolver.resolve(tex) {
- store
- .put_texture(
- img,
- local_idx,
- device,
- adapter,
- allocator,
- command_queue,
- command_pool,
- )
- .unwrap();
- } else {
- // Texture not found. For now, tear everything down.
- store.deactivate(device, allocator);
-
- return Err(error::CreationError::BadDataError);
- }
-
- local_idx += 1;
- }
-
- // Pad out the end if needed
- while local_idx < CHUNK_SIZE {
- debug!("Putting a placeholder in slot {}", local_idx);
- store
- .put_texture(
- RgbaImage::from_pixel(1, 1, Rgba([0, 0, 0, 1])),
- local_idx,
- device,
- adapter,
- allocator,
- command_queue,
- command_pool,
- )
- .unwrap();
-
- local_idx += 1;
- }
-
- Ok(store)
- }
-
- pub fn put_texture<T: LoadableImage>(
- &mut self,
- image: T,
- idx: usize,
- device: &mut Device,
- adapter: &mut Adapter,
- allocator: &mut DynamicAllocator,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- ) -> Result<(), &'static str> {
- // Load the image
- let texture = SampledImage::load_into_new(
- image,
- device,
- adapter,
- allocator,
- command_queue,
- command_pool,
- hal::format::Format::Rgba8Srgb, // TODO
- hal::image::Usage::empty(),
- )?;
-
- // Write it to the descriptor set
- unsafe {
- use hal::image::Layout;
- use hal::pso::{Descriptor, DescriptorSetWrite};
-
- device.write_descriptor_sets(vec![
- DescriptorSetWrite {
- set: &self.descriptor_set,
- binding: 0,
- array_offset: idx,
- descriptors: Some(Descriptor::Image(
- texture.image.image_view.deref(),
- Layout::ShaderReadOnlyOptimal,
- )),
- },
- DescriptorSetWrite {
- set: &self.descriptor_set,
- binding: 1,
- array_offset: idx,
- descriptors: Some(Descriptor::Sampler(texture.sampler.deref())),
- },
- ]);
- };
-
- // Store it so we can safely deactivate it when we need to
- // Deactivate the old image if we need to
- if idx < self.sampled_images.len() {
- replace(&mut self.sampled_images[idx], texture).deactivate(device, allocator);
- } else {
- self.sampled_images.push(texture);
- }
-
- Ok(())
- }
-
- pub fn deactivate(mut self, device: &mut Device, allocator: &mut DynamicAllocator) {
- for img in self.sampled_images.drain(..) {
- img.deactivate(device, allocator);
- }
- }
-}
diff --git a/stockton-render/src/draw/texture/image.rs b/stockton-render/src/draw/texture/image.rs
index 7cf84a7..aea1cb0 100644
--- a/stockton-render/src/draw/texture/image.rs
+++ b/stockton-render/src/draw/texture/image.rs
@@ -15,34 +15,19 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-use core::{
- mem::{size_of, ManuallyDrop},
- ptr::copy_nonoverlapping,
-};
-use hal::{
- buffer::Usage as BufUsage,
- format::{Aspects, Format, Swizzle},
- image::{SubresourceRange, Usage as ImgUsage, ViewKind},
- memory::{Dependencies as MemDependencies, Properties as MemProperties},
- prelude::*,
- queue::Submission,
- MemoryTypeId,
-};
-use image::RgbaImage;
-use rendy_memory::{Allocator, Block};
-use std::{convert::TryInto, iter::once};
+use super::PIXEL_SIZE;
-use crate::draw::buffer::create_buffer;
-use crate::types::*;
+use core::ptr::copy_nonoverlapping;
+use std::convert::TryInto;
-/// The size of each pixel in an image
-const PIXEL_SIZE: usize = size_of::<u8>() * 4;
+use image::RgbaImage;
/// An object that can be loaded as an image into GPU memory
pub trait LoadableImage {
fn width(&self) -> u32;
fn height(&self) -> u32;
fn copy_row(&self, y: u32, ptr: *mut u8);
+ unsafe fn copy_into(&self, ptr: *mut u8, row_size: usize);
}
impl LoadableImage for RgbaImage {
@@ -63,617 +48,11 @@ impl LoadableImage for RgbaImage {
copy_nonoverlapping(row.as_ptr(), ptr, row.len());
}
}
-}
-
-/// Holds an image that's loaded into GPU memory and can be sampled from
-pub struct LoadedImage {
- /// The GPU Image handle
- image: ManuallyDrop<Image>,
-
- /// The full view of the image
- pub image_view: ManuallyDrop<ImageView>,
-
- /// The memory backing the image
- memory: ManuallyDrop<DynamicBlock>,
-}
-
-pub fn create_image_view(
- device: &mut Device,
- adapter: &Adapter,
- allocator: &mut DynamicAllocator,
- format: Format,
- usage: ImgUsage,
- width: usize,
- height: usize,
-) -> Result<(DynamicBlock, Image), &'static str> {
- // Round up the size to align properly
- let initial_row_size = PIXEL_SIZE * width;
- let limits = adapter.physical_device.limits();
- let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
-
- let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
- debug_assert!(row_size as usize >= initial_row_size);
-
- // Make the image
- let mut image_ref = unsafe {
- use hal::image::{Kind, Tiling, ViewCapabilities};
-
- device.create_image(
- Kind::D2(width as u32, height as u32, 1, 1),
- 1,
- format,
- Tiling::Optimal,
- usage,
- ViewCapabilities::empty(),
- )
- }
- .map_err(|_| "Couldn't create image")?;
-
- // Allocate memory
- let (block, _) = unsafe {
- let requirements = device.get_image_requirements(&image_ref);
-
- allocator.alloc(device, requirements.size, requirements.alignment)
- }
- .map_err(|_| "Out of memory")?;
-
- unsafe {
- device
- .bind_image_memory(&block.memory(), block.range().start, &mut image_ref)
- .map_err(|_| "Couldn't bind memory to image")?;
- }
-
- Ok((block, image_ref))
-}
-
-impl LoadedImage {
- pub fn new(
- device: &mut Device,
- adapter: &Adapter,
- allocator: &mut DynamicAllocator,
- format: Format,
- usage: ImgUsage,
- resources: SubresourceRange,
- width: usize,
- height: usize,
- ) -> Result<LoadedImage, &'static str> {
- let (memory, image_ref) =
- create_image_view(device, adapter, allocator, format, usage, width, height)?;
-
- // Create ImageView and sampler
- let image_view = unsafe {
- device.create_image_view(&image_ref, ViewKind::D2, format, Swizzle::NO, resources)
- }
- .map_err(|_| "Couldn't create the image view!")?;
-
- Ok(LoadedImage {
- image: ManuallyDrop::new(image_ref),
- image_view: ManuallyDrop::new(image_view),
- memory: ManuallyDrop::new(memory),
- })
- }
-
- /// Load the given image
- pub fn load<T: LoadableImage>(
- &mut self,
- img: T,
- device: &mut Device,
- adapter: &Adapter,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- ) -> Result<(), &'static str> {
- let initial_row_size = PIXEL_SIZE * img.width() as usize;
- let limits = adapter.physical_device.limits();
- let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
-
- let row_size =
- ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
- let total_size = (row_size * (img.height() as usize)) as u64;
- debug_assert!(row_size as usize >= initial_row_size);
-
- // Make a staging buffer
- let (staging_buffer, staging_memory) = create_buffer(
- device,
- adapter,
- BufUsage::TRANSFER_SRC,
- MemProperties::CPU_VISIBLE | MemProperties::COHERENT,
- total_size,
- )
- .map_err(|_| "Couldn't create staging buffer")?;
-
- // Copy everything into it
- unsafe {
- let mapped_memory: *mut u8 = std::mem::transmute(
- device
- .map_memory(&staging_memory, 0..total_size)
- .map_err(|_| "Couldn't map buffer memory")?,
- );
-
- for y in 0..img.height() as usize {
- let dest_base: isize = (y * row_size).try_into().unwrap();
- img.copy_row(y as u32, mapped_memory.offset(dest_base));
- }
-
- device.unmap_memory(&staging_memory);
- }
-
- // Copy from staging to image memory
- let buf = unsafe {
- use hal::command::{BufferImageCopy, CommandBufferFlags};
- use hal::image::{Access, Extent, Layout, Offset, SubresourceLayers};
- use hal::memory::Barrier;
- use hal::pso::PipelineStage;
-
- // Get a command buffer
- let mut buf = command_pool.allocate_one(hal::command::Level::Primary);
- buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT);
-
- // Setup the layout of our image for copying
- let image_barrier = Barrier::Image {
- states: (Access::empty(), Layout::Undefined)
- ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal),
- target: &(*self.image),
- families: None,
- range: SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- },
- };
- buf.pipeline_barrier(
- PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER,
- MemDependencies::empty(),
- &[image_barrier],
- );
-
- // Copy from buffer to image
- buf.copy_buffer_to_image(
- &staging_buffer,
- &(*self.image),
- Layout::TransferDstOptimal,
- &[BufferImageCopy {
- buffer_offset: 0,
- buffer_width: (row_size / PIXEL_SIZE) as u32,
- buffer_height: img.height(),
- image_layers: SubresourceLayers {
- aspects: Aspects::COLOR,
- level: 0,
- layers: 0..1,
- },
- image_offset: Offset { x: 0, y: 0, z: 0 },
- image_extent: Extent {
- width: img.width(),
- height: img.height(),
- depth: 1,
- },
- }],
- );
-
- // Setup the layout of our image for shaders
- let image_barrier = Barrier::Image {
- states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal)
- ..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal),
- target: &(*self.image),
- families: None,
- range: SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- },
- };
-
- buf.pipeline_barrier(
- PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER,
- MemDependencies::empty(),
- &[image_barrier],
- );
-
- buf.finish();
-
- buf
- };
-
- // Submit our commands and wait for them to finish
- unsafe {
- let setup_finished = device.create_fence(false).unwrap();
- command_queue.submit::<_, _, Semaphore, _, _>(
- Submission {
- command_buffers: &[&buf],
- wait_semaphores: std::iter::empty::<_>(),
- signal_semaphores: std::iter::empty::<_>(),
- },
- Some(&setup_finished),
- );
-
- device
- .wait_for_fence(&setup_finished, core::u64::MAX)
- .unwrap();
- device.destroy_fence(setup_finished);
- };
-
- // Clean up temp resources
- unsafe {
- command_pool.free(once(buf));
-
- device.free_memory(staging_memory);
- device.destroy_buffer(staging_buffer);
- }
-
- Ok(())
- }
-
- /// Properly frees/destroys all the objects in this struct
- /// Dropping without doing this is a bad idea
- pub fn deactivate(self, device: &mut Device, allocator: &mut DynamicAllocator) {
- unsafe {
- use core::ptr::read;
-
- device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view)));
- device.destroy_image(ManuallyDrop::into_inner(read(&self.image)));
- allocator.free(device, ManuallyDrop::into_inner(read(&self.memory)));
- }
- }
-}
-
-pub struct SampledImage {
- pub image: ManuallyDrop<LoadedImage>,
- pub sampler: ManuallyDrop<Sampler>,
-}
-
-impl SampledImage {
- pub fn new(
- device: &mut Device,
- adapter: &Adapter,
- allocator: &mut DynamicAllocator,
- format: Format,
- usage: ImgUsage,
- width: usize,
- height: usize,
- ) -> Result<SampledImage, &'static str> {
- let image = LoadedImage::new(
- device,
- adapter,
- allocator,
- format,
- usage | ImgUsage::SAMPLED,
- SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- },
- width,
- height,
- )?;
-
- let sampler = unsafe {
- use hal::image::{Filter, SamplerDesc, WrapMode};
-
- device.create_sampler(&SamplerDesc::new(Filter::Nearest, WrapMode::Tile))
- }
- .map_err(|_| "Couldn't create the sampler!")?;
-
- Ok(SampledImage {
- image: ManuallyDrop::new(image),
- sampler: ManuallyDrop::new(sampler),
- })
- }
-
- pub fn load_into_new<T: LoadableImage>(
- img: T,
- device: &mut Device,
- adapter: &Adapter,
- allocator: &mut DynamicAllocator,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- format: Format,
- usage: ImgUsage,
- ) -> Result<SampledImage, &'static str> {
- let mut sampled_image = SampledImage::new(
- device,
- adapter,
- allocator,
- format,
- usage | ImgUsage::TRANSFER_DST,
- img.width() as usize,
- img.height() as usize,
- )?;
- sampled_image
- .image
- .load(img, device, adapter, command_queue, command_pool)?;
-
- Ok(sampled_image)
- }
-
- pub fn deactivate(self, device: &mut Device, allocator: &mut DynamicAllocator) {
- unsafe {
- use core::ptr::read;
-
- device.destroy_sampler(ManuallyDrop::into_inner(read(&self.sampler)));
-
- ManuallyDrop::into_inner(read(&self.image)).deactivate(device, allocator);
- }
- }
-}
-
-/// Holds an image that's loaded into GPU memory dedicated only to that image, bypassing the memory allocator.
-pub struct DedicatedLoadedImage {
- /// The GPU Image handle
- image: ManuallyDrop<Image>,
-
- /// The full view of the image
- pub image_view: ManuallyDrop<ImageView>,
-
- /// The memory backing the image
- memory: ManuallyDrop<Memory>,
-}
-
-impl DedicatedLoadedImage {
- pub fn new(
- device: &mut Device,
- adapter: &Adapter,
- format: Format,
- usage: ImgUsage,
- resources: SubresourceRange,
- width: usize,
- height: usize,
- ) -> Result<DedicatedLoadedImage, &'static str> {
- let (memory, image_ref) = {
- // Round up the size to align properly
- let initial_row_size = PIXEL_SIZE * width;
- let limits = adapter.physical_device.limits();
- let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
-
- let row_size =
- ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
- debug_assert!(row_size as usize >= initial_row_size);
-
- // Make the image
- let mut image_ref = unsafe {
- use hal::image::{Kind, Tiling, ViewCapabilities};
-
- device.create_image(
- Kind::D2(width as u32, height as u32, 1, 1),
- 1,
- format,
- Tiling::Optimal,
- usage,
- ViewCapabilities::empty(),
- )
- }
- .map_err(|_| "Couldn't create image")?;
-
- // Allocate memory
-
- // Allocate memory
- let memory = unsafe {
- let requirements = device.get_image_requirements(&image_ref);
-
- let memory_type_id = adapter
- .physical_device
- .memory_properties()
- .memory_types
- .iter()
- .enumerate()
- .find(|&(id, memory_type)| {
- requirements.type_mask & (1 << id) != 0
- && memory_type.properties.contains(MemProperties::DEVICE_LOCAL)
- })
- .map(|(id, _)| MemoryTypeId(id))
- .ok_or("Couldn't find a memory type for image memory")?;
-
- let memory = device
- .allocate_memory(memory_type_id, requirements.size)
- .map_err(|_| "Couldn't allocate image memory")?;
-
- device
- .bind_image_memory(&memory, 0, &mut image_ref)
- .map_err(|_| "Couldn't bind memory to image")?;
-
- Ok(memory)
- }?;
-
- Ok((memory, image_ref))
- }?;
-
- // Create ImageView and sampler
- let image_view = unsafe {
- device.create_image_view(&image_ref, ViewKind::D2, format, Swizzle::NO, resources)
- }
- .map_err(|_| "Couldn't create the image view!")?;
-
- Ok(DedicatedLoadedImage {
- image: ManuallyDrop::new(image_ref),
- image_view: ManuallyDrop::new(image_view),
- memory: ManuallyDrop::new(memory),
- })
- }
-
- /// Load the given image
- pub fn load<T: LoadableImage>(
- &mut self,
- img: T,
- device: &mut Device,
- adapter: &Adapter,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- ) -> Result<(), &'static str> {
- let initial_row_size = PIXEL_SIZE * img.width() as usize;
- let limits = adapter.physical_device.limits();
- let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1;
-
- let row_size =
- ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
- let total_size = (row_size * (img.height() as usize)) as u64;
- debug_assert!(row_size as usize >= initial_row_size);
-
- // Make a staging buffer
- let (staging_buffer, staging_memory) = create_buffer(
- device,
- adapter,
- BufUsage::TRANSFER_SRC,
- MemProperties::CPU_VISIBLE | MemProperties::COHERENT,
- total_size,
- )
- .map_err(|_| "Couldn't create staging buffer")?;
-
- // Copy everything into it
- unsafe {
- let mapped_memory: *mut u8 = std::mem::transmute(
- device
- .map_memory(&staging_memory, 0..total_size)
- .map_err(|_| "Couldn't map buffer memory")?,
- );
-
- for y in 0..img.height() as usize {
- let dest_base: isize = (y * row_size).try_into().unwrap();
- img.copy_row(y as u32, mapped_memory.offset(dest_base));
- }
-
- device.unmap_memory(&staging_memory);
- }
-
- // Copy from staging to image memory
- let buf = unsafe {
- use hal::command::{BufferImageCopy, CommandBufferFlags};
- use hal::image::{Access, Extent, Layout, Offset, SubresourceLayers};
- use hal::memory::Barrier;
- use hal::pso::PipelineStage;
-
- // Get a command buffer
- let mut buf = command_pool.allocate_one(hal::command::Level::Primary);
- buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT);
-
- // Setup the layout of our image for copying
- let image_barrier = Barrier::Image {
- states: (Access::empty(), Layout::Undefined)
- ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal),
- target: &(*self.image),
- families: None,
- range: SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- },
- };
- buf.pipeline_barrier(
- PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER,
- MemDependencies::empty(),
- &[image_barrier],
- );
-
- // Copy from buffer to image
- buf.copy_buffer_to_image(
- &staging_buffer,
- &(*self.image),
- Layout::TransferDstOptimal,
- &[BufferImageCopy {
- buffer_offset: 0,
- buffer_width: (row_size / PIXEL_SIZE) as u32,
- buffer_height: img.height(),
- image_layers: SubresourceLayers {
- aspects: Aspects::COLOR,
- level: 0,
- layers: 0..1,
- },
- image_offset: Offset { x: 0, y: 0, z: 0 },
- image_extent: Extent {
- width: img.width(),
- height: img.height(),
- depth: 1,
- },
- }],
- );
-
- // Setup the layout of our image for shaders
- let image_barrier = Barrier::Image {
- states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal)
- ..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal),
- target: &(*self.image),
- families: None,
- range: SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- },
- };
-
- buf.pipeline_barrier(
- PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER,
- MemDependencies::empty(),
- &[image_barrier],
- );
-
- buf.finish();
-
- buf
- };
-
- // Submit our commands and wait for them to finish
- unsafe {
- let setup_finished = device.create_fence(false).unwrap();
- command_queue.submit::<_, _, Semaphore, _, _>(
- Submission {
- command_buffers: &[&buf],
- wait_semaphores: std::iter::empty::<_>(),
- signal_semaphores: std::iter::empty::<_>(),
- },
- Some(&setup_finished),
- );
-
- device
- .wait_for_fence(&setup_finished, core::u64::MAX)
- .unwrap();
- device.destroy_fence(setup_finished);
- };
-
- // Clean up temp resources
- unsafe {
- command_pool.free(once(buf));
-
- device.free_memory(staging_memory);
- device.destroy_buffer(staging_buffer);
- }
-
- Ok(())
- }
-
- /// Load the given image into a new buffer
- pub fn load_into_new<T: LoadableImage>(
- img: T,
- device: &mut Device,
- adapter: &Adapter,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- format: Format,
- usage: ImgUsage,
- ) -> Result<DedicatedLoadedImage, &'static str> {
- let mut loaded_image = Self::new(
- device,
- adapter,
- format,
- usage | ImgUsage::TRANSFER_DST,
- SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- },
- img.width() as usize,
- img.height() as usize,
- )?;
- loaded_image.load(img, device, adapter, command_queue, command_pool)?;
-
- Ok(loaded_image)
- }
-
- /// Properly frees/destroys all the objects in this struct
- /// Dropping without doing this is a bad idea
- pub fn deactivate(self, device: &mut Device) {
- unsafe {
- use core::ptr::read;
- device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view)));
- device.destroy_image(ManuallyDrop::into_inner(read(&self.image)));
- device.free_memory(ManuallyDrop::into_inner(read(&self.memory)));
+ unsafe fn copy_into(&self, ptr: *mut u8, row_size: usize) {
+ for y in 0..self.height() as usize {
+ let dest_base: isize = (y * row_size).try_into().unwrap();
+ self.copy_row(y as u32, ptr.offset(dest_base));
}
}
}
diff --git a/stockton-render/src/draw/texture/load.rs b/stockton-render/src/draw/texture/load.rs
new file mode 100644
index 0000000..011ae29
--- /dev/null
+++ b/stockton-render/src/draw/texture/load.rs
@@ -0,0 +1,352 @@
+use super::{
+ block::LoadedImage, block::TexturesBlock, loader::TextureLoader, repo::BLOCK_SIZE,
+ resolver::TextureResolver, staging_buffer::StagingBuffer, LoadableImage, PIXEL_SIZE,
+};
+use crate::types::*;
+use stockton_levels::prelude::*;
+
+use arrayvec::ArrayVec;
+use hal::{
+ command::{BufferImageCopy, CommandBufferFlags},
+ format::{Aspects, Format, Swizzle},
+ image::{
+ Extent, Filter, Layout, Offset, SamplerDesc, SubresourceLayers, SubresourceRange,
+ Usage as ImgUsage, ViewKind, WrapMode,
+ },
+ memory::{Barrier, Dependencies},
+ prelude::*,
+ pso::{Descriptor, DescriptorSetWrite, PipelineStage, ShaderStageFlags},
+ queue::Submission,
+};
+use rendy_descriptor::{DescriptorRanges, DescriptorSetLayoutBinding, DescriptorType};
+use rendy_memory::{Allocator, Block};
+use std::mem::ManuallyDrop;
+
+pub struct QueuedLoad<B: Block<back::Backend>> {
+ pub fence: Fence,
+ pub buf: CommandBuffer,
+ pub block: TexturesBlock<B>,
+ pub staging_bufs: ArrayVec<[StagingBuffer; BLOCK_SIZE]>,
+}
+
+impl<B: Block<back::Backend>> QueuedLoad<B> {
+ pub fn dissolve(
+ self,
+ ) -> (
+ (Fence, CommandBuffer),
+ ArrayVec<[StagingBuffer; BLOCK_SIZE]>,
+ TexturesBlock<B>,
+ ) {
+ ((self.fence, self.buf), self.staging_bufs, self.block)
+ }
+}
+
+impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<'a, T, R, I> {
+ const FORMAT: Format = Format::Rgba8Srgb;
+ const RESOURCES: SubresourceRange = SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ };
+ const LAYERS: SubresourceLayers = SubresourceLayers {
+ aspects: Aspects::COLOR,
+ level: 0,
+ layers: 0..1,
+ };
+
+ pub(crate) unsafe fn attempt_queue_load(
+ &mut self,
+ block_ref: usize,
+ ) -> Option<QueuedLoad<DynamicBlock>> {
+ // Get assets to use
+ let (fence, mut buf) = self.buffers.pop_front()?;
+
+ // Create descriptor set
+ let descriptor_set = self.create_descriptor_set();
+
+ // Get a command buffer
+ buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT);
+
+ let mut copy_cmds: ArrayVec<[_; BLOCK_SIZE]> = ArrayVec::new();
+ let mut imgs: ArrayVec<[_; BLOCK_SIZE]> = ArrayVec::new();
+ let mut staging_bufs: ArrayVec<[_; BLOCK_SIZE]> = ArrayVec::new();
+
+ // For each texture in block
+ for tex_idx in (block_ref * BLOCK_SIZE)..(block_ref + 1) * BLOCK_SIZE {
+ // Get texture and Resolve image
+ let tex = self.textures.get_texture(tex_idx as u32);
+ if tex.is_none() {
+ break; // Past the end
+ // TODO: We should actually write blank descriptors
+ }
+ let tex = tex.unwrap();
+
+ let img_data = self.resolver.resolve(tex).expect("Invalid texture");
+
+ // Calculate buffer size
+ let (row_size, total_size) =
+ tex_size_info(&img_data, self.optimal_buffer_copy_pitch_alignment);
+
+ // Create staging buffer
+ let mut staging_buffer = StagingBuffer::new(
+ &mut self.device,
+ &mut self.staging_allocator,
+ total_size as u64,
+ self.staging_memory_type,
+ )
+ .expect("Couldn't create staging buffer");
+
+ // Write to staging buffer
+ let mapped_memory = staging_buffer
+ .map_memory(&mut self.device)
+ .expect("Error mapping staged memory");
+
+ img_data.copy_into(mapped_memory, row_size);
+
+ staging_buffer.unmap_memory(&mut self.device);
+
+ // Create image
+ let (img_mem, img) = create_image_view(
+ &mut self.device,
+ &mut *self.tex_allocator,
+ Self::FORMAT,
+ ImgUsage::SAMPLED,
+ &img_data,
+ )
+ .unwrap();
+
+ // Create image view
+ let img_view = self
+ .device
+ .create_image_view(
+ &img,
+ ViewKind::D2,
+ Self::FORMAT,
+ Swizzle::NO,
+ Self::RESOURCES,
+ )
+ .expect("Error creating image view");
+
+ // Queue copy from buffer to image
+ copy_cmds.push(BufferImageCopy {
+ buffer_offset: 0,
+ buffer_width: (row_size / super::PIXEL_SIZE) as u32,
+ buffer_height: img_data.height(),
+ image_layers: Self::LAYERS,
+ image_offset: Offset { x: 0, y: 0, z: 0 },
+ image_extent: Extent {
+ width: img_data.width(),
+ height: img_data.height(),
+ depth: 1,
+ },
+ });
+
+ // Create sampler
+ let sampler = self
+ .device
+ .create_sampler(&SamplerDesc::new(Filter::Nearest, WrapMode::Tile))
+ .expect("Failed to create sampler");
+
+ // Write to descriptor set
+ {
+ self.device.write_descriptor_sets(vec![
+ DescriptorSetWrite {
+ set: descriptor_set.raw(),
+ binding: 0,
+ array_offset: tex_idx % BLOCK_SIZE,
+ descriptors: Some(Descriptor::Image(
+ &img_view,
+ Layout::ShaderReadOnlyOptimal,
+ )),
+ },
+ DescriptorSetWrite {
+ set: descriptor_set.raw(),
+ binding: 1,
+ array_offset: tex_idx % BLOCK_SIZE,
+ descriptors: Some(Descriptor::Sampler(&sampler)),
+ },
+ ]);
+ }
+
+ imgs.push(LoadedImage {
+ mem: ManuallyDrop::new(img_mem),
+ img: ManuallyDrop::new(img),
+ img_view: ManuallyDrop::new(img_view),
+ sampler: ManuallyDrop::new(sampler),
+ row_size,
+ height: img_data.height(),
+ width: img_data.width(),
+ });
+
+ staging_bufs.push(staging_buffer);
+ }
+
+ // Add start pipeline barriers
+ for li in imgs.iter() {
+ use hal::image::Access;
+
+ buf.pipeline_barrier(
+ PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER,
+ Dependencies::empty(),
+ &[Barrier::Image {
+ states: (Access::empty(), Layout::Undefined)
+ ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal),
+ target: &*li.img,
+ families: None,
+ range: SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ },
+ }],
+ );
+ }
+
+ // Record copy commands
+ for (li, sb) in imgs.iter().zip(staging_bufs.iter()) {
+ buf.copy_buffer_to_image(
+ &*sb.buf,
+ &*li.img,
+ Layout::TransferDstOptimal,
+ &[BufferImageCopy {
+ buffer_offset: 0,
+ buffer_width: (li.row_size / super::PIXEL_SIZE) as u32,
+ buffer_height: li.height,
+ image_layers: SubresourceLayers {
+ aspects: Aspects::COLOR,
+ level: 0,
+ layers: 0..1,
+ },
+ image_offset: Offset { x: 0, y: 0, z: 0 },
+ image_extent: gfx_hal::image::Extent {
+ width: li.width,
+ height: li.height,
+ depth: 1,
+ },
+ }],
+ );
+ }
+ for li in imgs.iter() {
+ use hal::image::Access;
+
+ buf.pipeline_barrier(
+ PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER,
+ Dependencies::empty(),
+ &[Barrier::Image {
+ states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal)
+ ..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal),
+ target: &*li.img,
+ families: None,
+ range: Self::RESOURCES,
+ }],
+ );
+ }
+
+ buf.finish();
+
+ // Submit command buffer
+ self.gpu.queue_groups[self.cmd_queue_idx].queues[0].submit::<_, _, Semaphore, _, _>(
+ Submission {
+ command_buffers: &[&buf],
+ signal_semaphores: std::iter::empty(),
+ wait_semaphores: std::iter::empty(),
+ },
+ Some(&fence),
+ );
+
+ Some(QueuedLoad {
+ staging_bufs,
+ fence,
+ buf,
+ block: TexturesBlock {
+ id: block_ref,
+ imgs,
+ descriptor_set: ManuallyDrop::new(descriptor_set),
+ },
+ })
+ }
+
+ pub(crate) unsafe fn create_descriptor_set(&mut self) -> RDescriptorSet {
+ let mut v: ArrayVec<[RDescriptorSet; 1]> = ArrayVec::new();
+ self.descriptor_allocator
+ .allocate(
+ self.device,
+ &*self.ds_layout,
+ DescriptorRanges::from_bindings(&[
+ DescriptorSetLayoutBinding {
+ binding: 0,
+ ty: DescriptorType::SampledImage,
+ count: BLOCK_SIZE,
+ stage_flags: ShaderStageFlags::FRAGMENT,
+ immutable_samplers: false,
+ },
+ DescriptorSetLayoutBinding {
+ binding: 1,
+ ty: DescriptorType::Sampler,
+ count: BLOCK_SIZE,
+ stage_flags: ShaderStageFlags::FRAGMENT,
+ immutable_samplers: false,
+ },
+ ]),
+ 1,
+ &mut v,
+ )
+ .unwrap();
+
+ v.pop().unwrap()
+ }
+}
+
+pub fn tex_size_info<T: LoadableImage>(img: &T, obcpa: hal::buffer::Offset) -> (usize, usize) {
+ let initial_row_size = PIXEL_SIZE * img.width() as usize;
+ let row_alignment_mask = obcpa as u32 - 1;
+
+ let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize;
+ let total_size = (row_size * (img.height() as usize)) as u64;
+ debug_assert!(row_size as usize >= initial_row_size);
+
+ (row_size, total_size as usize)
+}
+
+fn create_image_view<T, I>(
+ device: &mut Device,
+ allocator: &mut T,
+ format: Format,
+ usage: ImgUsage,
+ img: &I,
+) -> Result<(T::Block, Image), &'static str>
+where
+ T: Allocator<back::Backend>,
+ I: LoadableImage,
+{
+ // Make the image
+ let mut image_ref = unsafe {
+ use hal::image::{Kind, Tiling, ViewCapabilities};
+
+ device.create_image(
+ Kind::D2(img.width(), img.height(), 1, 1),
+ 1,
+ format,
+ Tiling::Optimal,
+ usage,
+ ViewCapabilities::empty(),
+ )
+ }
+ .map_err(|_| "Couldn't create image")?;
+
+ // Allocate memory
+ let (block, _) = unsafe {
+ let requirements = device.get_image_requirements(&image_ref);
+
+ allocator.alloc(device, requirements.size, requirements.alignment)
+ }
+ .map_err(|_| "Out of memory")?;
+
+ unsafe {
+ device
+ .bind_image_memory(&block.memory(), block.range().start, &mut image_ref)
+ .map_err(|_| "Couldn't bind memory to image")?;
+ }
+
+ Ok((block, image_ref))
+}
diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs
index 0cfe0c3..33025a6 100644
--- a/stockton-render/src/draw/texture/loader.rs
+++ b/stockton-render/src/draw/texture/loader.rs
@@ -15,284 +15,357 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-//! Deals with loading textures into GPU memory
+//! Manages the loading/unloading of textures
-use super::chunk::TextureChunk;
-use crate::draw::texture::chunk::CHUNK_SIZE;
-use crate::draw::texture::image::LoadableImage;
-use crate::draw::texture::resolver::BasicFSResolver;
-use core::mem::ManuallyDrop;
-use std::path::Path;
+use super::{block::TexturesBlock, load::QueuedLoad, resolver::TextureResolver, LoadableImage};
+use crate::{draw::utils::find_memory_type_id, types::*};
-use log::debug;
+use std::{
+ collections::VecDeque,
+ marker::PhantomData,
+ mem::ManuallyDrop,
+ sync::mpsc::{Receiver, Sender},
+ thread::sleep,
+ time::Duration,
+};
-use hal::prelude::*;
+use arrayvec::ArrayVec;
+use hal::{
+ format::Format, memory::Properties as MemProps, prelude::*, queue::family::QueueFamilyId,
+ MemoryTypeId,
+};
+use log::*;
+use rendy_memory::DynamicConfig;
+use stockton_levels::prelude::HasTextures;
-use stockton_levels::prelude::*;
+/// The number of command buffers to have in flight simultaneously.
+pub const NUM_SIMULTANEOUS_CMDS: usize = 2;
-use crate::error;
-use crate::types::*;
+/// A reference to a texture of the current map
+pub type BlockRef = usize;
-/// Stores all loaded textures in GPU memory.
-/// When rendering, the descriptor sets are bound to the buffer
-/// The descriptor set layout should have the same count of textures as this does.
-/// All descriptors will be properly initialised images.
-pub struct TextureStore {
- descriptor_pool: ManuallyDrop<DescriptorPool>,
- pub(crate) descriptor_set_layout: ManuallyDrop<DescriptorSetLayout>,
- chunks: Box<[TextureChunk]>,
+/// Manages the loading/unloading of textures
+/// This is expected to load the textures, then send the loaded blocks back
+pub struct TextureLoader<'a, T, R, I> {
+ /// Handle to the device we're using
+ pub(crate) device: &'a mut Device,
+
+ /// Blocks for which commands have been queued and are done loading once the fence is triggered.
+ pub(crate) commands_queued: ArrayVec<[QueuedLoad<DynamicBlock>; NUM_SIMULTANEOUS_CMDS]>,
+
+ /// The command buffers used and a fence to go with them
+ pub(crate) buffers: VecDeque<(Fence, CommandBuffer)>,
+
+ /// The command pool buffers were allocated from
+ pub(crate) pool: ManuallyDrop<CommandPool>,
+
+ /// The GPU we're submitting to
+ pub(crate) gpu: ManuallyDrop<Gpu>,
+
+ /// The index of the command queue being used
+ pub(crate) cmd_queue_idx: usize,
+
+ /// The memory allocator being used for textures
+ pub(crate) tex_allocator: ManuallyDrop<DynamicAllocator>,
+
+ /// The memory allocator for staging memory
+ pub(crate) staging_allocator: ManuallyDrop<DynamicAllocator>,
+
+ /// Allocator for descriptor sets
+ pub(crate) descriptor_allocator: ManuallyDrop<DescriptorAllocator>,
+
+ pub(crate) ds_layout: &'a DescriptorSetLayout,
+
+ /// Type ID for staging memory
+ pub(crate) staging_memory_type: MemoryTypeId,
+
+ /// From adapter, used for determining alignment
+ pub(crate) optimal_buffer_copy_pitch_alignment: hal::buffer::Offset,
+
+ /// The textures lump to get info from
+ pub(crate) textures: &'a T,
+
+ /// The resolver which gets image data for a given texture.
+ pub(crate) resolver: R,
+
+ /// The channel requests come in.
+ /// Requests should reference a texture **block**, for example textures 8..16 is block 1.
+ pub(crate) request_channel: Receiver<LoaderRequest>,
+
+ /// The channel blocks are returned to.
+ pub(crate) return_channel: Sender<TexturesBlock<DynamicBlock>>,
+
+ pub(crate) _li: PhantomData<I>,
}
-impl TextureStore {
- pub fn new_empty(
- device: &mut Device,
- adapter: &mut Adapter,
- allocator: &mut DynamicAllocator,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- size: usize,
- ) -> Result<TextureStore, error::CreationError> {
- // Figure out how many textures in this file / how many chunks needed
- let num_chunks = {
- let mut x = size / CHUNK_SIZE;
- if size % CHUNK_SIZE != 0 {
- x += 1;
- }
- x
- };
- let rounded_size = num_chunks * CHUNK_SIZE;
-
- // Descriptor pool, where we get our sets from
- let mut descriptor_pool = unsafe {
- use hal::pso::{DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType};
-
- device
- .create_descriptor_pool(
- num_chunks,
- &[
- DescriptorRangeDesc {
- ty: DescriptorType::SampledImage,
- count: rounded_size,
- },
- DescriptorRangeDesc {
- ty: DescriptorType::Sampler,
- count: rounded_size,
- },
- ],
- DescriptorPoolCreateFlags::empty(),
- )
- .map_err(|e| {
- println!("{:?}", e);
- error::CreationError::OutOfMemoryError
- })?
- };
+impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<'a, T, R, I> {
+ pub fn loop_forever(mut self) -> Result<TextureLoaderRemains, &'static str> {
+ debug!("TextureLoader starting main loop");
+ let mut res = Ok(());
+ while res.is_ok() {
+ res = self.main();
+ sleep(Duration::from_secs(0));
+ }
- // Layout of our descriptor sets
- let descriptor_set_layout = unsafe {
- use hal::pso::{DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags};
-
- device.create_descriptor_set_layout(
- &[
- DescriptorSetLayoutBinding {
- binding: 0,
- ty: DescriptorType::SampledImage,
- count: CHUNK_SIZE,
- stage_flags: ShaderStageFlags::FRAGMENT,
- immutable_samplers: false,
- },
- DescriptorSetLayoutBinding {
- binding: 1,
- ty: DescriptorType::Sampler,
- count: CHUNK_SIZE,
- stage_flags: ShaderStageFlags::FRAGMENT,
- immutable_samplers: false,
- },
- ],
- &[],
- )
+ match res {
+ Err(r) => match r {
+ LoopEndReason::Graceful => {
+ debug!("Starting to deactivate TextureLoader");
+
+ Ok(self.deactivate())
+ }
+ LoopEndReason::Error(r) => Err(r),
+ },
+ Ok(_) => Err(""),
}
- .map_err(|_| error::CreationError::OutOfMemoryError)?;
-
- log::debug!("texture ds layout: {:?}", descriptor_set_layout);
-
- // Create texture chunks
- debug!("Starting to load textures...");
- let mut chunks = Vec::with_capacity(num_chunks);
- for i in 0..num_chunks {
- debug!("Chunk {} / {}", i + 1, num_chunks);
-
- let descriptor_set = unsafe {
- descriptor_pool
- .allocate_set(&descriptor_set_layout)
- .map_err(|_| error::CreationError::OutOfMemoryError)?
- };
- chunks.push(TextureChunk::new_empty(
- device,
- adapter,
- allocator,
- command_queue,
- command_pool,
- descriptor_set,
- )?);
+ }
+ fn main(&mut self) -> Result<(), LoopEndReason> {
+ // Check for blocks that are finished, then send them back
+ let mut i = 0;
+ while i < self.commands_queued.len() {
+ let signalled = unsafe { self.device.get_fence_status(&self.commands_queued[i].fence) }
+ .map_err(|_| LoopEndReason::Error("Device lost by TextureManager"))?;
+
+ if signalled {
+ let (assets, staging_bufs, block) = self.commands_queued.remove(i).dissolve();
+ debug!("Done loading texture block {:?}", block.id);
+
+ // Destroy staging buffers
+ staging_bufs
+ .into_iter()
+ .map(|x| x.deactivate(self.device, &mut self.staging_allocator))
+ .for_each(|_| {});
+
+ self.buffers.push_back(assets);
+ self.return_channel.send(block).unwrap();
+ } else {
+ i += 1;
+ }
}
- debug!("All textures loaded.");
+ // Check for messages to start loading blocks
+ let req_iter: Vec<_> = self.request_channel.try_iter().collect();
+ for to_load in req_iter {
+ match to_load {
+ LoaderRequest::Load(to_load) => {
+ // Attempt to load given block
+ if let Some(queued_load) = unsafe { self.attempt_queue_load(to_load) } {
+ self.commands_queued.push(queued_load);
+ }
+ }
+ LoaderRequest::End => return Err(LoopEndReason::Graceful),
+ }
+ }
- Ok(TextureStore {
- descriptor_pool: ManuallyDrop::new(descriptor_pool),
- descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout),
- chunks: chunks.into_boxed_slice(),
- })
+ Ok(())
}
- /// Create a new texture store for the given file, loading all textures from it.
- pub fn new<T: HasTextures>(
- device: &mut Device,
- adapter: &mut Adapter,
- allocator: &mut DynamicAllocator,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- file: &T,
- ) -> Result<TextureStore, error::CreationError> {
- // Figure out how many textures in this file / how many chunks needed
- let size = file.textures_iter().count();
- let num_chunks = {
- let mut x = size / CHUNK_SIZE;
- if size % CHUNK_SIZE != 0 {
- x += 1;
- }
- x
- };
- let rounded_size = num_chunks * CHUNK_SIZE;
-
- // Descriptor pool, where we get our sets from
- let mut descriptor_pool = unsafe {
- use hal::pso::{DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType};
-
- device
- .create_descriptor_pool(
- num_chunks,
- &[
- DescriptorRangeDesc {
- ty: DescriptorType::SampledImage,
- count: rounded_size,
- },
- DescriptorRangeDesc {
- ty: DescriptorType::Sampler,
- count: rounded_size,
- },
- ],
- DescriptorPoolCreateFlags::empty(),
+ pub fn new(
+ device: &'a mut Device,
+ adapter: &Adapter,
+ family: QueueFamilyId,
+ gpu: Gpu,
+ ds_layout: &'a DescriptorSetLayout,
+ request_channel: Receiver<LoaderRequest>,
+ return_channel: Sender<TexturesBlock<DynamicBlock>>,
+ texs: &'a T,
+ resolver: R,
+ ) -> Result<Self, &'static str> {
+ // Pool
+ let mut pool = unsafe {
+ use hal::pool::CommandPoolCreateFlags;
+
+ device.create_command_pool(family, CommandPoolCreateFlags::RESET_INDIVIDUAL)
+ }
+ .map_err(|_| "Couldn't create command pool")?;
+
+ let type_mask = unsafe {
+ use hal::image::{Kind, Tiling, Usage, ViewCapabilities};
+
+ // We create an empty image with the same format as used for textures
+ // this is to get the type_mask required, which will stay the same for
+ // all colour images of the same tiling. (certain memory flags excluded).
+
+ // Size and alignment don't necessarily stay the same, so we're forced to
+ // guess at the alignment for our allocator.
+
+ // TODO: Way to tune these options
+ let img = device
+ .create_image(
+ Kind::D2(16, 16, 1, 1),
+ 1,
+ Format::Rgba8Srgb,
+ Tiling::Optimal,
+ Usage::SAMPLED,
+ ViewCapabilities::empty(),
)
- .map_err(|e| {
- println!("{:?}", e);
- error::CreationError::OutOfMemoryError
- })?
+ .map_err(|_| "Couldn't make image to get memory requirements")?;
+
+ let type_mask = device.get_image_requirements(&img).type_mask;
+
+ device.destroy_image(img);
+
+ type_mask
};
- // Layout of our descriptor sets
- let descriptor_set_layout = unsafe {
- use hal::pso::{DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags};
-
- device.create_descriptor_set_layout(
- &[
- DescriptorSetLayoutBinding {
- binding: 0,
- ty: DescriptorType::SampledImage,
- count: CHUNK_SIZE,
- stage_flags: ShaderStageFlags::FRAGMENT,
- immutable_samplers: false,
- },
- DescriptorSetLayoutBinding {
- binding: 1,
- ty: DescriptorType::Sampler,
- count: CHUNK_SIZE,
- stage_flags: ShaderStageFlags::FRAGMENT,
- immutable_samplers: false,
+ // Tex Allocator
+ let tex_allocator = {
+ let props = MemProps::DEVICE_LOCAL;
+
+ DynamicAllocator::new(
+ find_memory_type_id(&adapter, type_mask, props)
+ .ok_or("Couldn't find memory type supporting image")?,
+ props,
+ DynamicConfig {
+ block_size_granularity: 4 * 32 * 32, // 32x32 image
+ max_chunk_size: u64::pow(2, 63),
+ min_device_allocation: 4 * 32 * 32,
+ },
+ )
+ };
+
+ let (staging_memory_type, staging_allocator) = {
+ let props = MemProps::CPU_VISIBLE | MemProps::COHERENT;
+ let t = find_memory_type_id(&adapter, type_mask, props)
+ .ok_or("Couldn't find memory type supporting image")?;
+ (
+ t,
+ DynamicAllocator::new(
+ t,
+ props,
+ DynamicConfig {
+ block_size_granularity: 4 * 32 * 32, // 32x32 image
+ max_chunk_size: u64::pow(2, 63),
+ min_device_allocation: 4 * 32 * 32,
},
- ],
- &[],
+ ),
)
- }
- .map_err(|_| error::CreationError::OutOfMemoryError)?;
-
- // TODO: Proper way to set up resolver
- let mut resolver = BasicFSResolver::new(Path::new("."));
-
- // Create texture chunks
- debug!("Starting to load textures...");
- let mut chunks = Vec::with_capacity(num_chunks);
- for i in 0..num_chunks {
- debug!("Chunk {} / {}", i + 1, num_chunks);
-
- let descriptor_set = unsafe {
- descriptor_pool
- .allocate_set(&descriptor_set_layout)
- .map_err(|_| error::CreationError::OutOfMemoryError)?
- };
- chunks.push(TextureChunk::new(
- device,
- adapter,
- allocator,
- command_queue,
- command_pool,
- descriptor_set,
- file.textures_iter().skip(i * CHUNK_SIZE).take(CHUNK_SIZE),
- &mut resolver,
- )?);
- }
+ };
+
+ let buffers = {
+ let mut data = VecDeque::with_capacity(NUM_SIMULTANEOUS_CMDS);
+
+ for _ in 0..NUM_SIMULTANEOUS_CMDS {
+ unsafe {
+ data.push_back((
+ device
+ .create_fence(false)
+ .map_err(|_| "Couldn't create fence")?,
+ pool.allocate_one(hal::command::Level::Primary),
+ ));
+ };
+ }
+
+ data
+ };
- debug!("All textures loaded.");
+ let cmd_queue_idx = gpu
+ .queue_groups
+ .iter()
+ .position(|x| x.family == family)
+ .unwrap();
- Ok(TextureStore {
- descriptor_pool: ManuallyDrop::new(descriptor_pool),
- descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout),
- chunks: chunks.into_boxed_slice(),
+ Ok(TextureLoader {
+ device,
+ commands_queued: ArrayVec::new(),
+ buffers,
+ pool: ManuallyDrop::new(pool),
+ gpu: ManuallyDrop::new(gpu),
+ cmd_queue_idx,
+ ds_layout,
+
+ tex_allocator: ManuallyDrop::new(tex_allocator),
+ staging_allocator: ManuallyDrop::new(staging_allocator),
+ descriptor_allocator: ManuallyDrop::new(DescriptorAllocator::new()),
+
+ staging_memory_type,
+ optimal_buffer_copy_pitch_alignment: adapter
+ .physical_device
+ .limits()
+ .optimal_buffer_copy_pitch_alignment,
+
+ request_channel,
+ return_channel,
+ textures: texs,
+ resolver,
+ _li: PhantomData::default(),
})
}
- /// Call this before dropping
- pub fn deactivate(mut self, device: &mut Device, allocator: &mut DynamicAllocator) {
- unsafe {
- use core::ptr::read;
+ /// Safely destroy all the vulkan stuff in this instance
+ /// Note that this returns the memory allocators, from which should be freed any TextureBlocks
+ /// All in-progress things are sent to return_channel.
+ fn deactivate(mut self) -> TextureLoaderRemains {
+ use std::ptr::read;
- for chunk in self.chunks.into_vec().drain(..) {
- chunk.deactivate(device, allocator);
+ unsafe {
+ // Wait for any currently queued loads to be done
+ while self.commands_queued.len() > 0 {
+ let mut i = 0;
+ while i < self.commands_queued.len() {
+ let signalled = self
+ .device
+ .get_fence_status(&self.commands_queued[i].fence)
+ .expect("Device lost by TextureManager");
+
+ if signalled {
+ // Destroy finished ones
+ let (assets, mut staging_bufs, block) =
+ self.commands_queued.remove(i).dissolve();
+
+ self.device.destroy_fence(assets.0);
+ // Command buffer will be freed when we reset the command pool
+ staging_bufs
+ .drain(..)
+ .map(|x| x.deactivate(self.device, &mut self.staging_allocator))
+ .for_each(|_| {});
+
+ self.return_channel
+ .send(block)
+ .expect("Sending through return channel failed");
+ } else {
+ i += 1;
+ }
+ }
+
+ sleep(Duration::from_secs(0));
}
- self.descriptor_pool.reset();
- device.destroy_descriptor_set_layout(ManuallyDrop::into_inner(read(
- &self.descriptor_set_layout,
- )));
- device.destroy_descriptor_pool(ManuallyDrop::into_inner(read(&self.descriptor_pool)));
+ // Destroy fences
+ let vec: Vec<_> = self.buffers.drain(..).collect();
+
+ vec.into_iter()
+ .map(|(f, _)| self.device.destroy_fence(f))
+ .for_each(|_| {});
+
+ // Free command pool
+ self.pool.reset(true);
+ self.device.destroy_command_pool(read(&*self.pool));
+
+ debug!("Done deactivating TextureLoader");
+
+ TextureLoaderRemains {
+ tex_allocator: ManuallyDrop::new(read(&*self.tex_allocator)),
+ descriptor_allocator: ManuallyDrop::new(read(&*self.descriptor_allocator)),
+ }
}
}
+}
- /// Get the descriptor set for a given chunk
- pub fn get_chunk_descriptor_set(&self, idx: usize) -> &DescriptorSet {
- &self.chunks[idx].descriptor_set
- }
+pub struct TextureLoaderRemains {
+ pub tex_allocator: ManuallyDrop<DynamicAllocator>,
+ pub descriptor_allocator: ManuallyDrop<DescriptorAllocator>,
+}
- pub fn put_texture<T: LoadableImage>(
- &mut self,
- idx: usize,
- img: T,
- device: &mut Device,
- adapter: &mut Adapter,
- allocator: &mut DynamicAllocator,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
- ) -> Result<(), &'static str> {
- // TODO: Resizing, etc?
- let chunk = &mut self.chunks[idx / CHUNK_SIZE];
- chunk.put_texture(
- img,
- idx % CHUNK_SIZE,
- device,
- adapter,
- allocator,
- command_queue,
- command_pool,
- )
- }
+enum LoopEndReason {
+ Graceful,
+ Error(&'static str),
+}
+
+pub enum LoaderRequest {
+ /// Load the given block
+ Load(BlockRef),
+
+ /// Stop looping and deactivate
+ End,
}
diff --git a/stockton-render/src/draw/texture/mod.rs b/stockton-render/src/draw/texture/mod.rs
index 3878472..09896ab 100644
--- a/stockton-render/src/draw/texture/mod.rs
+++ b/stockton-render/src/draw/texture/mod.rs
@@ -17,14 +17,18 @@
//! Everything related to loading textures into GPU memory
-// Since this is in the process of being rewritten, we ignore this for now
-#![allow(clippy::too_many_arguments)]
-
-mod chunk;
-pub mod image;
-pub mod loader;
-mod resolver;
+mod block;
+mod image;
+mod load;
+mod loader;
+mod repo;
+pub mod resolver;
+mod staging_buffer;
+pub use self::block::TexturesBlock;
pub use self::image::LoadableImage;
-pub use self::image::{LoadedImage, SampledImage};
-pub use self::loader::TextureStore;
+pub use self::loader::BlockRef;
+pub use self::repo::TextureRepo;
+
+/// The size of each pixel in an image
+pub const PIXEL_SIZE: usize = std::mem::size_of::<u8>() * 4;
diff --git a/stockton-render/src/draw/texture/repo.rs b/stockton-render/src/draw/texture/repo.rs
new file mode 100644
index 0000000..7bc8fb5
--- /dev/null
+++ b/stockton-render/src/draw/texture/repo.rs
@@ -0,0 +1,192 @@
+use stockton_levels::prelude::HasTextures;
+
+use super::{
+ block::TexturesBlock,
+ loader::{BlockRef, LoaderRequest, TextureLoader, TextureLoaderRemains, NUM_SIMULTANEOUS_CMDS},
+ resolver::TextureResolver,
+ LoadableImage,
+};
+use crate::types::*;
+
+use std::{
+ collections::HashMap,
+ marker::PhantomData,
+ mem::ManuallyDrop,
+ pin::Pin,
+ sync::mpsc::{channel, Receiver, Sender},
+ thread::JoinHandle,
+};
+
+use hal::{
+ prelude::*,
+ pso::{DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags},
+ Features,
+};
+use log::debug;
+
+/// The number of textures in one 'block'
+/// The textures of the loaded file are divided into blocks of this size.
+/// Whenever a texture is needed, the whole block its in is loaded.
+pub const BLOCK_SIZE: usize = 8;
+
+pub struct TextureRepo<'a> {
+ joiner: ManuallyDrop<JoinHandle<Result<TextureLoaderRemains, &'static str>>>,
+ ds_layout: Pin<Box<DescriptorSetLayout>>,
+ req_send: Sender<LoaderRequest>,
+ resp_recv: Receiver<TexturesBlock<DynamicBlock>>,
+ blocks: HashMap<BlockRef, Option<TexturesBlock<DynamicBlock>>>,
+
+ _a: PhantomData<&'a ()>,
+}
+
+impl<'a> TextureRepo<'a> {
+ pub fn new<
+ T: HasTextures + Send + Sync,
+ R: 'static + TextureResolver<I> + Send + Sync,
+ I: 'static + LoadableImage + Send,
+ >(
+ device: &'static mut Device,
+ adapter: &Adapter,
+ texs: &'static T,
+ resolver: R,
+ ) -> Result<Self, &'static str> {
+ let (req_send, req_recv) = channel();
+ let (resp_send, resp_recv) = channel();
+ let family = adapter
+ .queue_families
+ .iter()
+ .find(|family| {
+ family.queue_type().supports_transfer()
+ && family.max_queues() >= NUM_SIMULTANEOUS_CMDS
+ })
+ .unwrap();
+ let gpu = unsafe {
+ adapter
+ .physical_device
+ .open(&[(family, &[1.0])], Features::empty())
+ .unwrap()
+ };
+
+ let mut ds_layout = Box::pin(
+ unsafe {
+ device.create_descriptor_set_layout(
+ &[
+ DescriptorSetLayoutBinding {
+ binding: 0,
+ ty: DescriptorType::SampledImage,
+ count: BLOCK_SIZE,
+ stage_flags: ShaderStageFlags::FRAGMENT,
+ immutable_samplers: false,
+ },
+ DescriptorSetLayoutBinding {
+ binding: 1,
+ ty: DescriptorType::Sampler,
+ count: BLOCK_SIZE,
+ stage_flags: ShaderStageFlags::FRAGMENT,
+ immutable_samplers: false,
+ },
+ ],
+ &[],
+ )
+ }
+ .map_err(|_| "Couldn't create descriptor set layout")?,
+ );
+
+ let long_ds_pointer: &'static DescriptorSetLayout =
+ unsafe { &mut *(&mut *ds_layout as *mut DescriptorSetLayout) };
+
+ let joiner = {
+ let loader = TextureLoader::new(
+ device,
+ adapter,
+ family.id(),
+ gpu,
+ long_ds_pointer,
+ req_recv,
+ resp_send,
+ texs,
+ resolver,
+ )?;
+
+ std::thread::spawn(move || loader.loop_forever())
+ };
+
+ Ok(TextureRepo {
+ joiner: ManuallyDrop::new(joiner),
+ ds_layout,
+ blocks: HashMap::new(),
+ req_send,
+ resp_recv,
+ _a: PhantomData::default(),
+ })
+ }
+
+ pub fn get_ds_layout(&self) -> &DescriptorSetLayout {
+ &*self.ds_layout
+ }
+
+ pub fn queue_load(&mut self, block_id: BlockRef) -> Result<(), &'static str> {
+ if self.blocks.contains_key(&block_id) {
+ return Ok(());
+ }
+
+ self.force_queue_load(block_id)
+ }
+
+ pub fn force_queue_load(&mut self, block_id: BlockRef) -> Result<(), &'static str> {
+ self.req_send
+ .send(LoaderRequest::Load(block_id))
+ .map_err(|_| "Couldn't send load request")?;
+
+ self.blocks.insert(block_id, None);
+
+ Ok(())
+ }
+
+ pub fn attempt_get_descriptor_set(&mut self, block_id: BlockRef) -> Option<&DescriptorSet> {
+ self.blocks
+ .get(&block_id)
+ .and_then(|opt| opt.as_ref().map(|z| z.descriptor_set.raw()))
+ }
+
+ pub fn process_responses(&mut self) {
+ let resp_iter: Vec<_> = self.resp_recv.try_iter().collect();
+ for resp in resp_iter {
+ debug!("Got block {:?} back from loader", resp.id);
+ self.blocks.insert(resp.id, Some(resp));
+ }
+ }
+
+ pub fn deactivate(mut self, device: &mut Device) {
+ unsafe {
+ use std::ptr::read;
+
+ // Join the loader thread
+ self.req_send.send(LoaderRequest::End).unwrap();
+ let mut remains = read(&*self.joiner).join().unwrap().unwrap();
+
+ // Process any ones that just got done loading
+ self.process_responses();
+
+ // Return all the texture memory and descriptors.
+ for (i, v) in self.blocks.drain() {
+ debug!("Deactivating blockref {:?}", i);
+ if let Some(block) = v {
+ block.deactivate(
+ device,
+ &mut *remains.tex_allocator,
+ &mut remains.descriptor_allocator,
+ );
+ }
+ }
+
+ debug!("Deactivated all blocks");
+
+ // Dispose of both allocators
+ read(&*remains.tex_allocator).dispose();
+ read(&*remains.descriptor_allocator).dispose(device);
+
+ debug!("Disposed of allocators");
+ }
+ }
+}
diff --git a/stockton-render/src/draw/texture/resolver.rs b/stockton-render/src/draw/texture/resolver.rs
index f33f0a1..a0df7d8 100644
--- a/stockton-render/src/draw/texture/resolver.rs
+++ b/stockton-render/src/draw/texture/resolver.rs
@@ -20,10 +20,10 @@
use crate::draw::texture::image::LoadableImage;
use stockton_levels::traits::textures::Texture;
-use image::{io::Reader, RgbaImage};
-
use std::path::Path;
+use image::{io::Reader, RgbaImage};
+
/// An object that can be used to resolve a texture from a BSP File
pub trait TextureResolver<T: LoadableImage> {
/// Get the given texture, or None if it's corrupt/not there.
@@ -31,17 +31,17 @@ pub trait TextureResolver<T: LoadableImage> {
}
/// A basic filesystem resolver which expects no file extension and guesses the image format
-pub struct BasicFSResolver<'a> {
+pub struct BasicFsResolver<'a> {
path: &'a Path,
}
-impl<'a> BasicFSResolver<'a> {
- pub fn new(path: &'a Path) -> BasicFSResolver<'a> {
- BasicFSResolver { path }
+impl<'a> BasicFsResolver<'a> {
+ pub fn new(path: &'a Path) -> BasicFsResolver<'a> {
+ BasicFsResolver { path }
}
}
-impl<'a> TextureResolver<RgbaImage> for BasicFSResolver<'a> {
+impl<'a> TextureResolver<RgbaImage> for BasicFsResolver<'a> {
fn resolve(&mut self, tex: &Texture) -> Option<RgbaImage> {
let path = self.path.join(&tex.name);
diff --git a/stockton-render/src/draw/texture/staging_buffer.rs b/stockton-render/src/draw/texture/staging_buffer.rs
new file mode 100644
index 0000000..d1897ad
--- /dev/null
+++ b/stockton-render/src/draw/texture/staging_buffer.rs
@@ -0,0 +1,56 @@
+use crate::types::*;
+
+use std::mem::ManuallyDrop;
+
+use hal::{device::MapError, prelude::*, MemoryTypeId};
+use rendy_memory::{Allocator, Block};
+
+pub struct StagingBuffer {
+ pub buf: ManuallyDrop<Buffer>,
+ pub mem: ManuallyDrop<DynamicBlock>,
+}
+
+impl StagingBuffer {
+ const USAGE: hal::buffer::Usage = hal::buffer::Usage::TRANSFER_SRC;
+
+ pub fn new(
+ device: &mut Device,
+ alloc: &mut DynamicAllocator,
+ size: u64,
+ _memory_type_id: MemoryTypeId,
+ ) -> Result<StagingBuffer, &'static str> {
+ let mut buffer = unsafe { device.create_buffer(size, Self::USAGE) }
+ .map_err(|_| "Couldn't create staging buffer")?;
+
+ let requirements = unsafe { device.get_buffer_requirements(&buffer) };
+
+ let (memory, _) = alloc
+ .alloc(device, requirements.size, requirements.alignment)
+ .map_err(|_| "Couldn't allocate staging memory")?;
+
+ unsafe { device.bind_buffer_memory(memory.memory(), 0, &mut buffer) }
+ .map_err(|_| "Couldn't bind staging memory to buffer")?;
+
+ Ok(StagingBuffer {
+ buf: ManuallyDrop::new(buffer),
+ mem: ManuallyDrop::new(memory),
+ })
+ }
+
+ pub unsafe fn map_memory(&mut self, device: &mut Device) -> Result<*mut u8, MapError> {
+ device.map_memory(self.mem.memory(), self.mem.range())
+ }
+ pub unsafe fn unmap_memory(&mut self, device: &mut Device) {
+ device.unmap_memory(self.mem.memory()); // TODO: What if the same Memory is mapped in multiple places?
+ }
+
+ pub fn deactivate(self, device: &mut Device, alloc: &mut DynamicAllocator) {
+ unsafe {
+ use std::ptr::read;
+ // Destroy buffer
+ device.destroy_buffer(read(&*self.buf));
+ // Free memory
+ alloc.free(device, read(&*self.mem));
+ }
+ }
+}
diff --git a/stockton-render/src/draw/ui/mod.rs b/stockton-render/src/draw/ui/mod.rs
index bcf5f38..4caec7a 100644
--- a/stockton-render/src/draw/ui/mod.rs
+++ b/stockton-render/src/draw/ui/mod.rs
@@ -21,10 +21,10 @@ pub mod pipeline;
pub mod render;
pub mod texture;
-pub use pipeline::UIPipeline;
+pub use pipeline::UiPipeline;
pub use render::do_render;
use stockton_types::Vector2;
-pub use texture::ensure_textures;
+pub use texture::{ensure_textures, UiTextures};
#[derive(Debug)]
-pub struct UIPoint(pub Vector2, pub Vector2, pub Srgba);
+pub struct UiPoint(pub Vector2, pub Vector2, pub Srgba);
diff --git a/stockton-render/src/draw/ui/pipeline.rs b/stockton-render/src/draw/ui/pipeline.rs
index f608173..1261ea7 100644
--- a/stockton-render/src/draw/ui/pipeline.rs
+++ b/stockton-render/src/draw/ui/pipeline.rs
@@ -37,7 +37,7 @@ use crate::error;
use crate::types::*;
/// A complete 2D graphics pipeline and associated resources
-pub struct UIPipeline {
+pub struct UiPipeline {
/// Our main render pass
pub(crate) renderpass: ManuallyDrop<RenderPass>,
@@ -54,7 +54,7 @@ pub struct UIPipeline {
pub(crate) fs_module: ManuallyDrop<ShaderModule>,
}
-impl UIPipeline {
+impl UiPipeline {
pub fn new<T>(
device: &mut Device,
extent: hal::image::Extent,
@@ -271,7 +271,7 @@ impl UIPipeline {
let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) }
.map_err(error::CreationError::PipelineError)?;
- Ok(UIPipeline {
+ Ok(UiPipeline {
renderpass: ManuallyDrop::new(renderpass),
pipeline_layout: ManuallyDrop::new(layout),
pipeline: ManuallyDrop::new(pipeline),
diff --git a/stockton-render/src/draw/ui/render.rs b/stockton-render/src/draw/ui/render.rs
index 02135e5..77231b0 100644
--- a/stockton-render/src/draw/ui/render.rs
+++ b/stockton-render/src/draw/ui/render.rs
@@ -15,24 +15,24 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-use crate::draw::texture::TextureStore;
+use crate::draw::texture::TextureRepo;
use arrayvec::ArrayVec;
use hal::prelude::*;
use hal::pso::ShaderStageFlags;
-use super::UIPoint;
+use super::UiPoint;
use crate::draw::draw_buffers::DrawBuffers;
use crate::types::*;
-use crate::UIState;
+use crate::UiState;
use std::convert::TryInto;
use stockton_types::Vector2;
pub fn do_render(
cmd_buffer: &mut CommandBuffer,
pipeline_layout: &PipelineLayout,
- draw_buffers: &mut DrawBuffers<UIPoint>,
- texture_store: &mut TextureStore,
- ui: &mut UIState,
+ draw_buffers: &mut DrawBuffers<UiPoint>,
+ tex_repo: &mut TextureRepo,
+ ui: &mut UiState,
) {
// TODO: Actual UI Rendering
let (_out, paint) = ui.end_frame();
@@ -57,7 +57,7 @@ pub fn do_render(
);
}
for (i, vertex) in tris.vertices.iter().enumerate() {
- draw_buffers.vertex_buffer[i] = UIPoint(
+ draw_buffers.vertex_buffer[i] = UiPoint(
Vector2::new(vertex.pos.x, vertex.pos.y),
Vector2::new(vertex.uv.x, vertex.uv.y),
vertex.color,
@@ -65,13 +65,17 @@ pub fn do_render(
}
// TODO: *Properly* deal with textures
- let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
- descriptor_sets.push(texture_store.get_chunk_descriptor_set(0));
+ if let Some(ds) = tex_repo.attempt_get_descriptor_set(0) {
+ let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
+ descriptor_sets.push(ds);
- unsafe {
- cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, descriptor_sets, &[]);
- // Call draw
- cmd_buffer.draw_indexed(0..tris.indices.len() as u32, 0, 0..1);
+ unsafe {
+ cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, descriptor_sets, &[]);
+ // Call draw
+ cmd_buffer.draw_indexed(0..tris.indices.len() as u32, 0, 0..1);
+ }
+ } else {
+ // tex_repo.queue_load(0);
}
}
}
diff --git a/stockton-render/src/draw/ui/texture.rs b/stockton-render/src/draw/ui/texture.rs
index 98688de..439c3d7 100755
--- a/stockton-render/src/draw/ui/texture.rs
+++ b/stockton-render/src/draw/ui/texture.rs
@@ -14,10 +14,25 @@
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-use crate::draw::texture::{LoadableImage, TextureStore};
+use crate::draw::texture::{LoadableImage, TextureRepo};
use crate::types::*;
-use crate::UIState;
+use crate::UiState;
use egui::Texture;
+use stockton_levels::{prelude::HasTextures, traits::textures::Texture as LTexture};
+
+pub struct UiTextures;
+
+impl HasTextures for UiTextures {
+ type TexturesIter<'a> = std::slice::Iter<'a, LTexture>;
+
+ fn textures_iter(&self) -> Self::TexturesIter<'_> {
+ (&[]).iter()
+ }
+
+ fn get_texture(&self, _idx: u32) -> Option<&stockton_levels::prelude::textures::Texture> {
+ None
+ }
+}
impl LoadableImage for &Texture {
fn width(&self) -> u32 {
@@ -38,31 +53,25 @@ impl LoadableImage for &Texture {
}
}
}
+
+ unsafe fn copy_into(&self, _ptr: *mut u8, _row_size: usize) {
+ todo!()
+ }
}
pub fn ensure_textures(
- texture_store: &mut TextureStore,
- ui: &mut UIState,
- device: &mut Device,
- adapter: &mut Adapter,
- allocator: &mut DynamicAllocator,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool,
+ _tex_repo: &mut TextureRepo,
+ ui: &mut UiState,
+ _device: &mut Device,
+ _adapter: &mut Adapter,
+ _allocator: &mut DynamicAllocator,
+ _command_queue: &mut CommandQueue,
+ _command_pool: &mut CommandPool,
) {
let tex = ui.ctx.texture();
if tex.version != ui.last_tex_ver {
- texture_store
- .put_texture(
- 0,
- &*tex,
- device,
- adapter,
- allocator,
- command_queue,
- command_pool,
- )
- .unwrap(); // TODO
+ // tex_repo.force_queue_load(0).unwrap(); // TODO
ui.last_tex_ver = tex.version;
}
}
diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs
index fbbb329..98ee817 100644
--- a/stockton-render/src/lib.rs
+++ b/stockton-render/src/lib.rs
@@ -14,7 +14,8 @@
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
+#![allow(incomplete_features)]
+#![feature(generic_associated_types)]
#[cfg(feature = "vulkan")]
extern crate gfx_backend_vulkan as back;
extern crate gfx_hal as hal;
@@ -37,7 +38,7 @@ use legion::IntoQuery;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::Arc;
use std::sync::RwLock;
-pub use window::{UIState, WindowEvent};
+pub use window::{UiState, WindowEvent};
use stockton_levels::prelude::*;
use stockton_types::components::{CameraSettings, Transform};
@@ -49,9 +50,9 @@ use std::sync::mpsc::channel;
/// Renders a world to a window when you tell it to.
/// Also takes ownership of the window and channels window events to be processed outside winit's event loop.
-pub struct Renderer<'a> {
+pub struct Renderer<'a, M: 'static + MinBspFeatures<VulkanSystem>> {
/// All the vulkan stuff
- pub(crate) context: RenderingContext<'a>,
+ pub(crate) context: RenderingContext<'a, M>,
/// For getting events from the winit event loop
pub window_events: Receiver<WindowEvent>,
@@ -60,12 +61,9 @@ pub struct Renderer<'a> {
pub update_control_flow: Arc<RwLock<ControlFlow>>,
}
-impl<'a> Renderer<'a> {
+impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> Renderer<'a, M> {
/// Create a new Renderer.
- pub fn new<T: MinBSPFeatures<VulkanSystem>>(
- window: &Window,
- file: &T,
- ) -> (Self, Sender<WindowEvent>) {
+ pub fn new(window: &Window, file: M) -> (Self, Sender<WindowEvent>) {
let (tx, rx) = channel();
let update_control_flow = Arc::new(RwLock::new(ControlFlow::Poll));
@@ -80,16 +78,16 @@ impl<'a> Renderer<'a> {
}
/// Render a single frame of the given map.
- fn render<T: MinBSPFeatures<VulkanSystem>>(&mut self, map: &T, ui: &mut UIState, pos: Vector3) {
+ fn render(&mut self, ui: &mut UiState, pos: Vector3) {
// Get visible faces
- let faces = get_visible_faces(pos, map);
+ let faces = get_visible_faces(pos, &*self.context.map);
// Then draw them
- if self.context.draw_vertices(map, ui, &faces).is_err() {
+ if self.context.draw_vertices(ui, &faces).is_err() {
unsafe { self.context.handle_surface_change().unwrap() };
// If it fails twice, then error
- self.context.draw_vertices(map, ui, &faces).unwrap();
+ self.context.draw_vertices(ui, &faces).unwrap();
}
}
@@ -102,14 +100,13 @@ impl<'a> Renderer<'a> {
#[system]
#[read_component(Transform)]
#[read_component(CameraSettings)]
-pub fn do_render<T: 'static + MinBSPFeatures<VulkanSystem>>(
- #[resource] renderer: &mut Renderer<'static>,
- #[resource] ui: &mut UIState,
- #[resource] map: &T,
+pub fn do_render<T: 'static + MinBspFeatures<VulkanSystem>>(
+ #[resource] renderer: &mut Renderer<'static, T>,
+ #[resource] ui: &mut UiState,
world: &SubWorld,
) {
let mut query = <(&Transform, &CameraSettings)>::query();
for (transform, _) in query.iter(world) {
- renderer.render(map, ui, transform.position);
+ renderer.render(ui, transform.position);
}
}
diff --git a/stockton-render/src/types.rs b/stockton-render/src/types.rs
index cf0b025..c461fde 100644
--- a/stockton-render/src/types.rs
+++ b/stockton-render/src/types.rs
@@ -18,6 +18,7 @@
//! Convenience module to reference types that are stored in the backend's enum
pub type Device = <back::Backend as hal::Backend>::Device;
+pub type Gpu = hal::adapter::Gpu<back::Backend>;
pub type Buffer = <back::Backend as hal::Backend>::Buffer;
pub type Memory = <back::Backend as hal::Backend>::Memory;
pub type Swapchain = <back::Backend as hal::Backend>::Swapchain;
@@ -28,7 +29,6 @@ pub type CommandPool = <back::Backend as hal::Backend>::CommandPool;
pub type CommandBuffer = <back::Backend as hal::Backend>::CommandBuffer;
pub type CommandQueue = <back::Backend as hal::Backend>::CommandQueue;
pub type DescriptorSetLayout = <back::Backend as hal::Backend>::DescriptorSetLayout;
-pub type DescriptorPool = <back::Backend as hal::Backend>::DescriptorPool;
pub type DescriptorSet = <back::Backend as hal::Backend>::DescriptorSet;
pub type PipelineLayout = <back::Backend as hal::Backend>::PipelineLayout;
pub type GraphicsPipeline = <back::Backend as hal::Backend>::GraphicsPipeline;
@@ -42,5 +42,8 @@ pub type RenderPass = <back::Backend as hal::Backend>::RenderPass;
pub type Adapter = hal::adapter::Adapter<back::Backend>;
pub type QueueGroup = hal::queue::QueueGroup<back::Backend>;
+pub type DescriptorAllocator = rendy_descriptor::DescriptorAllocator<back::Backend>;
pub type DynamicAllocator = rendy_memory::DynamicAllocator<back::Backend>;
pub type DynamicBlock = rendy_memory::DynamicBlock<back::Backend>;
+
+pub type RDescriptorSet = rendy_descriptor::DescriptorSet<back::Backend>;
diff --git a/stockton-render/src/window.rs b/stockton-render/src/window.rs
index e6bd5b0..4a1628d 100644
--- a/stockton-render/src/window.rs
+++ b/stockton-render/src/window.rs
@@ -20,6 +20,7 @@ use egui::Context;
use legion::systems::Runnable;
use log::debug;
use std::sync::Arc;
+use stockton_levels::prelude::{MinBspFeatures, VulkanSystem};
use egui::{Output, PaintJobs, Pos2, RawInput, Ui};
@@ -86,7 +87,7 @@ impl WindowEvent {
}
}
-pub struct UIState {
+pub struct UiState {
pub(crate) ctx: Arc<Context>,
pub(crate) raw_input: RawInput,
ui: Option<Ui>,
@@ -94,7 +95,7 @@ pub struct UIState {
pub(crate) last_tex_ver: u64,
}
-impl UIState {
+impl UiState {
pub fn ui(&mut self) -> &mut Ui {
if self.ui.is_none() {
self.ui = Some(self.begin_frame());
@@ -144,8 +145,8 @@ impl UIState {
}
}
- pub fn new(renderer: &Renderer) -> Self {
- let mut state = UIState {
+ pub fn new<T: MinBspFeatures<VulkanSystem>>(renderer: &Renderer<T>) -> Self {
+ let mut state = UiState {
ctx: Context::new(),
raw_input: RawInput::default(),
ui: None,
@@ -162,11 +163,14 @@ impl UIState {
#[system]
/// A system to process the window events sent to renderer by the winit event loop.
-pub fn _process_window_events<T: 'static + InputManager>(
- #[resource] renderer: &mut Renderer<'static>,
+pub fn _process_window_events<
+ T: 'static + InputManager,
+ M: 'static + MinBspFeatures<VulkanSystem>,
+>(
+ #[resource] renderer: &mut Renderer<'static, M>,
#[resource] manager: &mut T,
#[resource] mouse: &mut Mouse,
- #[resource] ui_state: &mut UIState,
+ #[resource] ui_state: &mut UiState,
#[state] actions_buf: &mut Vec<KBAction>,
) {
let mut actions_buf_cursor = 0;
@@ -220,6 +224,9 @@ pub fn _process_window_events<T: 'static + InputManager>(
manager.handle_frame(&actions_buf[0..actions_buf_cursor]);
}
-pub fn process_window_events_system<T: 'static + InputManager>() -> impl Runnable {
- _process_window_events_system::<T>(Vec::with_capacity(4))
+pub fn process_window_events_system<
+ T: 'static + InputManager,
+ M: 'static + MinBspFeatures<VulkanSystem>,
+>() -> impl Runnable {
+ _process_window_events_system::<T, M>(Vec::with_capacity(4))
}