aboutsummaryrefslogtreecommitdiff
path: root/stockton-render
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:21 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:21 +0100
commit2f112ab34ac1458b038598f4d7ef6638df463dc6 (patch)
treec0100341b31276297fc9d2992797c5ca18cd2d99 /stockton-render
parent27760ec1ca7a93877b2b015a0a2e9db87de4204c (diff)
feat(render): depth buffer and refactors
Diffstat (limited to 'stockton-render')
-rw-r--r--stockton-render/src/draw/context.rs577
-rw-r--r--stockton-render/src/draw/mod.rs2
-rw-r--r--stockton-render/src/draw/target.rs407
-rw-r--r--stockton-render/src/draw/texture/chunk.rs20
-rw-r--r--stockton-render/src/draw/texture/image.rs216
-rw-r--r--stockton-render/src/draw/texture/loader.rs5
-rw-r--r--stockton-render/src/draw/texture/mod.rs4
-rw-r--r--stockton-render/src/error.rs3
8 files changed, 714 insertions, 520 deletions
diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs
index eacfc44..9da5789 100644
--- a/stockton-render/src/draw/context.rs
+++ b/stockton-render/src/draw/context.rs
@@ -17,20 +17,22 @@
//! 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, size_of},
ops::Deref,
borrow::Borrow,
convert::TryInto
};
-use winit::window::Window;
-use arrayvec::ArrayVec;
+use arrayvec::ArrayVec;
use hal::{
prelude::*,
- queue::{Submission},
- window::SwapchainConfig
+ pool::CommandPoolCreateFlags
};
+use log::debug;
+use winit::window::Window;
+
use stockton_types::{Vector2, Vector3};
use stockton_levels::prelude::*;
use stockton_levels::traits::faces::FaceType;
@@ -40,6 +42,7 @@ use crate::{
error
};
use super::{
+ target::{TargetChain, SwapchainProperties},
camera::WorkingCamera,
texture::TextureStore,
buffer::{StagedBuffer, ModifiableBuffer}
@@ -48,13 +51,6 @@ use super::{
/// Entry point name for shaders
const ENTRY_NAME: &str = "main";
-/// Defines the colour range we use.
-const COLOR_RANGE: hal::image::SubresourceRange = hal::image::SubresourceRange {
- aspects: hal::format::Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
-};
-
/// Initial size of vertex buffer. TODO: Way of overriding this
const INITIAL_VERT_SIZE: u64 = 3 * 3000;
@@ -91,35 +87,8 @@ pub struct RenderingContext<'a> {
/// Surface to draw to
surface: ManuallyDrop<Surface>,
- /// Swapchain we're targeting
- swapchain: ManuallyDrop<Swapchain>,
-
- /// Viewport of surface
- viewport: hal::pso::Viewport,
-
- /// The imageviews in our swapchain
- imageviews: Vec<ImageView>,
-
- /// The framebuffers of imageviews in our swapchain
- framebuffers: Vec<Framebuffer>,
-
- /// The frame we will draw to next
- current_frame: usize,
-
- /// The number of frames in our swapchain, ie max pre-rendered frames possible
- frames_in_flight: usize,
-
- // Sync objects
- // TODO: Collect these together?
-
- /// Triggered when the image is ready to draw to
- get_image: Vec<Semaphore>,
-
- /// Triggered when rendering is done
- render_complete: Vec<Semaphore>,
-
- /// Triggered when the image is on screen
- present_complete: Vec<Fence>,
+ /// Swapchain and stuff
+ target_chain: ManuallyDrop<TargetChain>,
// Pipeline
@@ -137,9 +106,6 @@ pub struct RenderingContext<'a> {
/// The command pool used for our buffers
cmd_pool: ManuallyDrop<CommandPool>,
- /// The buffers used to draw to our frames
- cmd_buffers: Vec<CommandBuffer>,
-
/// The queue group our buffers belong to
queue_group: QueueGroup,
@@ -193,8 +159,15 @@ impl<'a> RenderingContext<'a> {
(gpu.device, gpu.queue_groups.pop().unwrap())
};
- // Swapchain
- let (format, viewport, extent, swapchain, backbuffer) = RenderingContext::create_swapchain(&mut surface, &mut device, &adapter, None)?;
+ // Figure out what our swapchain will look like
+ let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface).map_err(|_| error::CreationError::BadSurface)?;
+
+ debug!("Detected following swapchain properties: {:?}", swapchain_properties);
+
+ // Command pool
+ let mut cmd_pool = unsafe {
+ device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL)
+ }.map_err(|_| error::CreationError::OutOfMemoryError)?;
// Renderpass
let renderpass = {
@@ -205,31 +178,55 @@ impl<'a> RenderingContext<'a> {
memory::Dependencies
};
- let attachment = Attachment {
- format: Some(format),
+ let img_attachment = Attachment {
+ format: Some(swapchain_properties.format),
samples: 1,
ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store),
- stencil_ops: AttachmentOps::new(AttachmentLoadOp::DontCare, AttachmentStoreOp::DontCare),
+ stencil_ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare),
layouts: Layout::Undefined..Layout::Present
};
+ let depth_attachment = Attachment {
+ format: Some(swapchain_properties.depth_format),
+ samples: 1,
+ ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare),
+ stencil_ops: AttachmentOps::new(AttachmentLoadOp::DontCare, AttachmentStoreOp::DontCare),
+ layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal
+ };
+
let subpass = SubpassDesc {
colors: &[(0, Layout::ColorAttachmentOptimal)],
- depth_stencil: None,
+ depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)),
inputs: &[],
resolves: &[],
preserves: &[]
};
- let dependency = SubpassDependency {
+ let in_dependency = SubpassDependency {
flags: Dependencies::empty(),
passes: None..Some(0),
- stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT..PipelineStage::COLOR_ATTACHMENT_OUTPUT,
+ stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT..
+ (PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS),
accesses: Access::empty()
- ..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE)
+ ..(Access::COLOR_ATTACHMENT_READ
+ | Access::COLOR_ATTACHMENT_WRITE
+ | Access::DEPTH_STENCIL_ATTACHMENT_READ
+ | Access::DEPTH_STENCIL_ATTACHMENT_WRITE)
};
- unsafe { device.create_render_pass(&[attachment], &[subpass], &[dependency]) }
+ let out_dependency = SubpassDependency {
+ flags: Dependencies::empty(),
+ passes: Some(0)..None,
+ stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS..
+ PipelineStage::COLOR_ATTACHMENT_OUTPUT,
+ accesses: (Access::COLOR_ATTACHMENT_READ
+ | Access::COLOR_ATTACHMENT_WRITE
+ | Access::DEPTH_STENCIL_ATTACHMENT_READ
+ | Access::DEPTH_STENCIL_ATTACHMENT_WRITE)..
+ Access::empty()
+ };
+
+ unsafe { device.create_render_pass(&[img_attachment, depth_attachment], &[subpass], &[in_dependency, out_dependency]) }
.map_err(|_| error::CreationError::OutOfMemoryError)?
};
@@ -239,6 +236,12 @@ impl<'a> RenderingContext<'a> {
main_pass: &renderpass
};
+ // Camera
+ // TODO: Settings
+ let ratio = swapchain_properties.extent.width as f32 / swapchain_properties.extent.height as f32;
+ let camera = WorkingCamera::defaults(ratio);
+
+
// Vertex and index buffers
let (vert_buffer, index_buffer) = {
use hal::buffer::Usage;
@@ -249,70 +252,20 @@ impl<'a> RenderingContext<'a> {
(vert, index)
};
- // Command Pool, Buffers, imageviews, framebuffers & Sync objects
- let frames_in_flight = backbuffer.len();
- let (mut cmd_pool, cmd_buffers, get_image, render_complete, present_complete, imageviews, framebuffers) = {
- use hal::pool::CommandPoolCreateFlags;
- use hal::command::Level;
-
- let mut cmd_pool = ManuallyDrop::new(unsafe {
- device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL)
- }.map_err(|_| error::CreationError::OutOfMemoryError)?);
-
- let mut cmd_buffers = Vec::with_capacity(frames_in_flight);
- let mut get_image = Vec::with_capacity(frames_in_flight);
- let mut render_complete = Vec::with_capacity(frames_in_flight);
- let mut present_complete = Vec::with_capacity(frames_in_flight);
- let mut imageviews = Vec::with_capacity(frames_in_flight);
- let mut framebuffers = Vec::with_capacity(frames_in_flight);
-
- for i in 0..frames_in_flight {
- unsafe {
- cmd_buffers.push(cmd_pool.allocate_one(Level::Primary)); // TODO: We can do this all at once outside the loop
- }
-
- get_image.push(device.create_semaphore().map_err(|_| error::CreationError::SyncObjectError)?);
- render_complete.push(device.create_semaphore().map_err(|_| error::CreationError::SyncObjectError)?);
- present_complete.push(device.create_fence(true).map_err(|_| error::CreationError::SyncObjectError)?);
-
- unsafe {
- use hal::image::ViewKind;
- use hal::format::Swizzle;
-
- imageviews.push(device.create_image_view(
- &backbuffer[i],
- ViewKind::D2,
- format,
- Swizzle::NO,
- COLOR_RANGE.clone(),
- ).map_err(|e| error::CreationError::ImageViewError (e))?);
- framebuffers.push(device.create_framebuffer(
- &renderpass,
- Some(&imageviews[i]),
- extent
- ).map_err(|_| error::CreationError::OutOfMemoryError)?);
- }
- }
-
- (cmd_pool, cmd_buffers, get_image, render_complete, present_complete, imageviews, framebuffers)
- };
-
// Texture store
let texture_store = TextureStore::new(&mut device,
&mut adapter,
&mut queue_group.queues[0],
&mut cmd_pool, file)?;
- // Camera
- // TODO: Settings
- let ratio = extent.width as f32 / extent.height as f32;
- let camera = WorkingCamera::defaults(ratio);
-
let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new();
descriptor_set_layouts.push(texture_store.descriptor_set_layout.deref());
// Graphics pipeline
- let (pipeline_layout, pipeline) = Self::create_pipeline(&mut device, extent, &subpass, descriptor_set_layouts)?;
+ let (pipeline_layout, pipeline) = Self::create_pipeline(&mut device, swapchain_properties.extent, &subpass, descriptor_set_layouts)?;
+
+ // Swapchain and associated resources
+ let target_chain = TargetChain::new(&mut device, &adapter, &mut surface, &renderpass, &mut cmd_pool, swapchain_properties, None).map_err(|e| error::CreationError::TargetChainCreationError (e) )?;
Ok(RenderingContext {
instance: ManuallyDrop::new(instance),
@@ -321,21 +274,10 @@ impl<'a> RenderingContext<'a> {
device: ManuallyDrop::new(device),
adapter,
queue_group,
- swapchain: ManuallyDrop::new(swapchain),
- viewport,
-
- imageviews,
- framebuffers,
renderpass: ManuallyDrop::new(renderpass),
- current_frame: 0,
-
- get_image,
- render_complete,
- present_complete,
- frames_in_flight,
- cmd_pool,
- cmd_buffers,
+ target_chain: ManuallyDrop::new(target_chain),
+ cmd_pool: ManuallyDrop::new(cmd_pool),
pipeline_layout: ManuallyDrop::new(pipeline_layout),
pipeline: ManuallyDrop::new(pipeline),
@@ -352,20 +294,16 @@ impl<'a> RenderingContext<'a> {
/// If this function fails the whole context is probably dead
/// The context must not be used while this is being called
pub unsafe fn handle_surface_change(&mut self) -> Result<(), error::CreationError> {
- use core::ptr::read;
-
self.device.wait_idle().unwrap();
- // Swapchain itself
- let old_swapchain = ManuallyDrop::into_inner(read(&self.swapchain));
-
- let (format, viewport, extent, swapchain, backbuffer) = RenderingContext::create_swapchain(&mut self.surface, &mut self.device, &self.adapter, Some(old_swapchain))?;
-
- self.swapchain = ManuallyDrop::new(swapchain);
- self.viewport = viewport;
+ let properties = SwapchainProperties::find_best(&self.adapter, &self.surface).map_err(|_| error::CreationError::BadSurface)?;
// Camera settings (aspect ratio)
- self.camera.update_aspect_ratio(extent.width as f32 / extent.height as f32);
+ self.camera.update_aspect_ratio(
+ properties.extent.width as f32 /
+ properties.extent.height as f32);
+
+ use core::ptr::read;
// Graphics pipeline
self.device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline)));
@@ -373,7 +311,6 @@ impl<'a> RenderingContext<'a> {
self.device
.destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout)));
-
let (pipeline_layout, pipeline) = {
let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new();
descriptor_set_layouts.push(self.texture_store.descriptor_set_layout.deref());
@@ -383,126 +320,17 @@ impl<'a> RenderingContext<'a> {
main_pass: &(*self.renderpass)
};
- Self::create_pipeline(&mut self.device, extent, &subpass, descriptor_set_layouts)?
+ Self::create_pipeline(&mut self.device, properties.extent, &subpass, descriptor_set_layouts)?
};
self.pipeline_layout = ManuallyDrop::new(pipeline_layout);
self.pipeline = ManuallyDrop::new(pipeline);
- // Imageviews, framebuffers
- // Destroy old objects
- for framebuffer in self.framebuffers.drain(..) {
- self.device.destroy_framebuffer(framebuffer);
- }
- for image_view in self.imageviews.drain(..) {
- self.device.destroy_image_view(image_view);
- }
- // Make new ones
- for i in 0..backbuffer.len() {
- use hal::image::ViewKind;
- use hal::format::Swizzle;
-
- self.imageviews.push(self.device.create_image_view(
- &backbuffer[i],
- ViewKind::D2,
- format,
- Swizzle::NO,
- COLOR_RANGE.clone(),
- ).map_err(|e| error::CreationError::ImageViewError (e))?);
-
- self.framebuffers.push(self.device.create_framebuffer(
- &self.renderpass,
- Some(&self.imageviews[i]),
- extent
- ).map_err(|_| error::CreationError::OutOfMemoryError)?);
- }
+ let old_swapchain = ManuallyDrop::into_inner(read(&self.target_chain)).deactivate_with_recyling(&mut self.device, &mut self.cmd_pool);
+ self.target_chain = ManuallyDrop::new(TargetChain::new(&mut self.device, &self.adapter, &mut self.surface, &self.renderpass, &mut self.cmd_pool, properties, Some(old_swapchain))
+ .map_err(|e| error::CreationError::TargetChainCreationError (e) )?);
Ok(())
- }
-
- fn create_swapchain(surface: &mut Surface, device: &mut Device, adapter: &Adapter, old_swapchain: Option<Swapchain>) -> Result<(
- hal::format::Format,
- hal::pso::Viewport,
- hal::image::Extent,
- Swapchain,
- Vec<Image>
- ), error::CreationError> {
- use hal::{
- window::{PresentMode, CompositeAlphaMode},
- format::{Format, ChannelType},
- image::Usage,
- pso::Viewport
- };
-
- // Figure out what the surface supports
- let caps = surface.capabilities(&adapter.physical_device);
- let formats = surface.supported_formats(&adapter.physical_device);
-
- // Find which settings we'll actually use based on preset preferences
- let format = formats.map_or(Format::Rgba8Srgb, |formats| {
- formats.iter()
- .find(|format| format.base_format().1 == ChannelType::Srgb)
- .map(|format| *format)
- .unwrap_or(formats[0])
- });
-
- let present_mode = {
- [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE]
- .iter()
- .cloned()
- .find(|pm| caps.present_modes.contains(*pm))
- .ok_or(error::CreationError::BadSurface)?
- };
- let composite_alpha = {
- [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED]
- .iter()
- .cloned()
- .find(|ca| caps.composite_alpha_modes.contains(*ca))
- .ok_or(error::CreationError::BadSurface)?
- };
-
- // Figure out properties for our swapchain
- let extent = caps.extents.end(); // Size
-
- // Number of frames to pre-render
- let image_count = if present_mode == PresentMode::MAILBOX {
- ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3))
- } else {
- ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2))
- };
-
- let image_layers = 1; // Don't support 3D
- let image_usage = if caps.usage.contains(Usage::COLOR_ATTACHMENT) {
- Usage::COLOR_ATTACHMENT
- } else {
- Err(error::CreationError::BadSurface)?
- };
-
- // Swap config
- let swap_config = SwapchainConfig {
- present_mode,
- composite_alpha_mode: composite_alpha,
- format,
- extent: *extent,
- image_count,
- image_layers,
- image_usage,
- };
-
- // Viewport
- let extent = extent.to_extent();
- let viewport = Viewport {
- rect: extent.rect(),
- depth: 0.0..1.0
- };
-
- // Swapchain
- let (swapchain, backbuffer) = unsafe {
- device.create_swapchain(surface, swap_config, old_swapchain)
- .map_err(|e| error::CreationError::SwapchainError (e))?
- };
-
- Ok((format, viewport, extent, swapchain, backbuffer))
}
#[allow(clippy::type_complexity)]
@@ -586,7 +414,10 @@ impl<'a> RenderingContext<'a> {
// Depth stencil
let depth_stencil = DepthStencilDesc {
- depth: None,
+ depth: Some(DepthTest {
+ fun: Comparison::Less,
+ write: true
+ }),
depth_bounds: false,
stencil: None,
};
@@ -660,167 +491,94 @@ impl<'a> RenderingContext<'a> {
/// Draw all vertices in the buffer
pub fn draw_vertices<M: MinBSPFeatures<VulkanSystem>>(&mut self, file: &M,faces: &Vec<u32>) -> Result<(), &'static str> {
- let get_image = &self.get_image[self.current_frame];
- let render_complete = &self.render_complete[self.current_frame];
-
- // Advance the frame _before_ we start using the `?` operator
- self.current_frame = (self.current_frame + 1) % self.frames_in_flight;
-
- // Get the image
- let (image_index, _) = unsafe {
- self
- .swapchain
- .acquire_image(core::u64::MAX, Some(get_image), None)
- .map_err(|_| "FrameError::AcquireError")?
- };
- let image_index = image_index as usize;
-
- // Make sure whatever was last using this has finished
- let present_complete = &self.present_complete[image_index];
- unsafe {
- self.device
- .wait_for_fence(present_complete, core::u64::MAX)
- .map_err(|_| "FrameError::SyncObjectError")?;
- self.device
- .reset_fence(present_complete)
- .map_err(|_| "FrameError::SyncObjectError")?;
- };
-
- // Record commands
- unsafe {
- use hal::buffer::{IndexBufferView, SubRange};
- use hal::command::{SubpassContents, CommandBufferFlags, ClearValue, ClearColor};
- use hal::pso::ShaderStageFlags;
-
- // Command buffer to use
- let buffer = &mut self.cmd_buffers[image_index];
+ // Prepare command buffer
+ let cmd_buffer = self.target_chain.prep_next_target(
+ &mut self.device,
+ self.vert_buffer.get_buffer(),
+ self.index_buffer.get_buffer(),
+ &self.renderpass,
+ &self.pipeline,
+ &self.pipeline_layout,
+ &mut self.camera
+ )?;
+
+ // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once.
+ let mut current_chunk = file.get_face(0).texture_idx as usize / 8;
+ let mut chunk_start = 0;
+
+ let mut curr_vert_idx: usize = 0;
+ let mut curr_idx_idx: usize = 0;
+
+ for face in faces.into_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(self.texture_store.get_chunk_descriptor_set(current_chunk));
+ unsafe {
- // Colour to clear window to
- let clear_values = [ClearValue {
- color: ClearColor {
- float32: [0.0, 0.0, 0.0, 1.0]
+ cmd_buffer.bind_graphics_descriptor_sets(
+ &self.pipeline_layout,
+ 0,
+ descriptor_sets,
+ &[]
+ );
+ cmd_buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1);
}
- }];
-
- // Get references to our buffers
- let (vbufs, ibuf) = {
- let vbufref: &<back::Backend as hal::Backend>::Buffer = self.vert_buffer.get_buffer();
-
- let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into();
- let ibuf = self.index_buffer.get_buffer();
+
+ // Next group of same-chunked faces starts here.
+ chunk_start = curr_idx_idx;
+ current_chunk = face.texture_idx as usize / 8;
+ }
- (vbufs, ibuf)
- };
+ if face.face_type == FaceType::Polygon || face.face_type == FaceType::Mesh {
+ // 2 layers of indirection
+ let base = face.vertices_idx.start;
- buffer.begin_primary(CommandBufferFlags::EMPTY);
- {
- // Main render pass / pipeline
- buffer.begin_render_pass(
- &self.renderpass,
- &self.framebuffers[image_index],
- self.viewport.rect,
- clear_values.iter(),
- SubpassContents::Inline
- );
- buffer.bind_graphics_pipeline(&self.pipeline);
+ for idx in face.meshverts_idx.clone().step_by(3) {
+ let start_idx: u16 = curr_vert_idx.try_into().unwrap();
- // VP Matrix
- let vp = self.camera.get_matrix().as_slice();
- let vp = std::mem::transmute::<&[f32], &[u32]>(vp);
+ for idx2 in idx..idx+3 {
+ let vert = &file.resolve_meshvert(idx2 as u32, base);
+ let uv = Vector2::new(vert.tex.u[0], vert.tex.v[0]);
- buffer.push_graphics_constants(
- &self.pipeline_layout,
- ShaderStageFlags::VERTEX,
- 0,
- vp);
-
- // Bind buffers
- buffer.bind_vertex_buffers(0, vbufs);
- buffer.bind_index_buffer(IndexBufferView {
- buffer: ibuf,
- range: SubRange::WHOLE,
- index_type: hal::IndexType::U16
- });
-
- // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once.
- let mut current_chunk = file.get_face(0).texture_idx as usize / 8;
- let mut chunk_start = 0;
-
- let mut curr_vert_idx: usize = 0;
- let mut curr_idx_idx: usize = 0;
-
- for face in faces.into_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(self.texture_store.get_chunk_descriptor_set(current_chunk));
-
- buffer.bind_graphics_descriptor_sets(
- &self.pipeline_layout,
- 0,
- descriptor_sets,
- &[]
- );
-
- buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1);
+ let uvp = UVPoint (vert.position, face.texture_idx.try_into().unwrap(), uv);
+ self.vert_buffer[curr_vert_idx] = uvp;
- // Next group of same-chunked faces starts here.
- chunk_start = curr_idx_idx;
- current_chunk = face.texture_idx as usize / 8;
+ curr_vert_idx += 1;
}
- if face.face_type == FaceType::Polygon || face.face_type == FaceType::Mesh {
- // 2 layers of indirection
- let base = face.vertices_idx.start;
-
- for idx in face.meshverts_idx.clone().step_by(3) {
- let start_idx: u16 = curr_vert_idx.try_into().unwrap();
-
- for idx2 in idx..idx+3 {
- 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);
- self.vert_buffer[curr_vert_idx] = uvp;
-
- curr_vert_idx += 1;
- }
-
- self.index_buffer[curr_idx_idx] = (start_idx, start_idx + 1, start_idx + 2);
+ self.index_buffer[curr_idx_idx] = (start_idx, start_idx + 1, start_idx + 2);
- curr_idx_idx += 1;
-
- if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() {
- println!("out of vertex buffer space!");
- break;
- }
- }
- } else {
- // TODO: Other types of faces
- }
+ curr_idx_idx += 1;
if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() {
println!("out of vertex buffer space!");
break;
}
}
+ } else {
+ // TODO: Other types of faces
+ }
- // Draw the final group of chunks
- let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
- descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk));
- buffer.bind_graphics_descriptor_sets(
- &self.pipeline_layout,
- 0,
- descriptor_sets,
- &[]
- );
- buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1);
-
- buffer.end_render_pass();
+ if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() {
+ println!("out of vertex buffer space!");
+ break;
}
- buffer.finish();
- };
+ }
+
+ // Draw the final group of chunks
+ let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new();
+ descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk));
+ unsafe {
+ cmd_buffer.bind_graphics_descriptor_sets(
+ &self.pipeline_layout,
+ 0,
+ descriptor_sets,
+ &[]
+ );
+ cmd_buffer.draw_indexed(chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, 0, 0..1);
+ }
// Update our buffers before we actually start drawing
self.vert_buffer.commit(
@@ -835,27 +593,8 @@ impl<'a> RenderingContext<'a> {
&mut self.cmd_pool
);
- // Make submission object
- let command_buffers = &self.cmd_buffers[image_index..=image_index];
- let wait_semaphores: ArrayVec<[_; 1]> = [(get_image, hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT)].into();
- let signal_semaphores: ArrayVec<[_; 1]> = [render_complete].into();
-
- let present_wait_semaphores: ArrayVec<[_; 1]> = [render_complete].into();
-
- let submission = Submission {
- command_buffers,
- wait_semaphores,
- signal_semaphores,
- };
-
- // Submit it
- let command_queue = &mut self.queue_group.queues[0];
- unsafe {
- command_queue.submit(submission, Some(present_complete));
- self.swapchain
- .present(command_queue, image_index as u32, present_wait_semaphores)
- .map_err(|_| "FrameError::PresentError")?
- };
+ // Send commands off to GPU
+ self.target_chain.finish_and_submit_target(&mut self.queue_group.queues[0])?;
Ok(())
}
@@ -883,35 +622,19 @@ impl<'a> core::ops::Drop for RenderingContext<'a> {
self.device.wait_idle().unwrap();
unsafe {
- for fence in self.present_complete.drain(..) {
- self.device.destroy_fence(fence)
- }
- for semaphore in self.render_complete.drain(..) {
- self.device.destroy_semaphore(semaphore)
- }
- for semaphore in self.get_image.drain(..) {
- self.device.destroy_semaphore(semaphore)
- }
- for framebuffer in self.framebuffers.drain(..) {
- self.device.destroy_framebuffer(framebuffer);
- }
- for image_view in self.imageviews.drain(..) {
- self.device.destroy_image_view(image_view);
- }
-
use core::ptr::read;
+
ManuallyDrop::into_inner(read(&self.vert_buffer)).deactivate(&mut self.device);
ManuallyDrop::into_inner(read(&self.index_buffer)).deactivate(&mut self.device);
ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device);
+ ManuallyDrop::into_inner(read(&self.target_chain)).deactivate(&mut self.device, &mut self.cmd_pool);
+
self.device.destroy_command_pool(
ManuallyDrop::into_inner(read(&self.cmd_pool)),
);
-
self.device
.destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass)));
- self.device
- .destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain)));
self.device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline)));
diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs
index 2834aa4..af86b46 100644
--- a/stockton-render/src/draw/mod.rs
+++ b/stockton-render/src/draw/mod.rs
@@ -15,6 +15,8 @@
//! Given 3D points and some camera information, renders to the screen.
+pub mod target;
+
#[macro_use]
mod macros;
mod context;
diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs
new file mode 100644
index 0000000..3d0ecdf
--- /dev/null
+++ b/stockton-render/src/draw/target.rs
@@ -0,0 +1,407 @@
+// Copyright (C) Oscar Shrimpton 2019
+
+// 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/>.
+
+//! Resources needed for drawing on the screen, including sync objects
+use crate::types::*;
+use super::{
+ camera::WorkingCamera,
+ texture::image::LoadedImage
+};
+
+use core::{
+ iter::once,
+ mem::ManuallyDrop
+};
+
+use arrayvec::ArrayVec;
+use hal::{
+ prelude::*,
+ queue::Submission,
+ pso::Viewport,
+ format::{ChannelType, Format, Swizzle},
+ image::{Extent, ViewKind, Usage as ImgUsage},
+ window::{CompositeAlphaMode, PresentMode, SwapchainConfig, Extent2D}
+};
+
+/// Defines the colour range we use.
+const COLOR_RANGE: hal::image::SubresourceRange = hal::image::SubresourceRange {
+ aspects: hal::format::Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+};
+
+#[derive(Debug, Clone)]
+pub struct SwapchainProperties {
+ pub format: Format,
+ pub depth_format: Format,
+ pub present_mode: PresentMode,
+ pub composite_alpha_mode: CompositeAlphaMode,
+ pub viewport: Viewport,
+ pub extent: Extent
+}
+
+impl SwapchainProperties {
+ pub fn find_best(adapter: &Adapter, surface: &Surface) -> Result<SwapchainProperties, ()> {
+ let caps = surface.capabilities(&adapter.physical_device);
+ let formats = surface.supported_formats(&adapter.physical_device);
+
+ // Find which settings we'll actually use based on preset preferences
+ let format = formats.clone().map_or(Format::Rgba8Srgb, |formats| {
+ formats.iter()
+ .find(|format| format.base_format().1 == ChannelType::Srgb)
+ .map(|format| *format)
+ .unwrap_or(formats[0])
+ });
+
+ let depth_format = *[Format::D32SfloatS8Uint, Format::D24UnormS8Uint, Format::D32Sfloat].iter().find(|format| {
+ use hal::format::ImageFeature;
+
+ format.is_depth() && adapter.physical_device.format_properties(Some(**format)).optimal_tiling.contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT)
+ }).ok_or_else(|| ())?;
+
+ let present_mode = {
+ [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE]
+ .iter()
+ .cloned()
+ .find(|pm| caps.present_modes.contains(*pm))
+ .ok_or(())?
+ };
+ let composite_alpha_mode = {
+ [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED]
+ .iter()
+ .cloned()
+ .find(|ca| caps.composite_alpha_modes.contains(*ca))
+ .ok_or(())?
+ };
+
+ let extent = caps.extents.end().to_extent(); // Size
+ let viewport = Viewport {
+ rect: extent.rect(),
+ depth: 0.0..1.0
+ };
+
+ Ok(SwapchainProperties {format, depth_format, present_mode, composite_alpha_mode, extent, viewport})
+ }
+}
+
+pub struct TargetChain {
+ /// Swapchain we're targeting
+ pub swapchain: ManuallyDrop<Swapchain>,
+
+ pub properties: SwapchainProperties,
+
+ /// The depth buffer/image used for drawing
+ pub depth_buffer: ManuallyDrop<LoadedImage>,
+
+ /// Resources tied to each target frame in the swapchain
+ pub targets: Box<[TargetResources]>,
+
+ /// The last target drawn to
+ last_drawn: usize,
+
+ /// Last image index of the swapchain drawn to
+ last_image_index: u32,
+}
+
+impl TargetChain {
+ pub fn new(device: &mut Device, adapter: &Adapter, surface: &mut Surface, renderpass: &RenderPass, cmd_pool: &mut CommandPool, properties: SwapchainProperties, old_swapchain: Option<Swapchain>) -> Result<TargetChain, TargetChainCreationError> {
+ let caps = surface.capabilities(&adapter.physical_device);
+
+ // Number of frames to pre-render
+ let image_count = if properties.present_mode == PresentMode::MAILBOX {
+ ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3))
+ } else {
+ ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2))
+ };
+
+ // Swap config
+ let swap_config = SwapchainConfig {
+ present_mode: properties.present_mode,
+ composite_alpha_mode: properties.composite_alpha_mode,
+ format: properties.format,
+ extent: Extent2D { width: properties.extent.width, height: properties.extent.height },
+ image_count,
+ image_layers: 1,
+ image_usage: ImgUsage::COLOR_ATTACHMENT,
+ };
+
+ // Swapchain
+ let (swapchain, mut backbuffer) = unsafe {
+ device.create_swapchain(surface, swap_config, old_swapchain)
+ .map_err(|_| TargetChainCreationError::Todo)?
+ };
+
+ let depth_buffer: LoadedImage = {
+ use hal::image::SubresourceRange;
+ use hal::format::Aspects;
+
+ LoadedImage::new(device, adapter, properties.depth_format, ImgUsage::DEPTH_STENCIL_ATTACHMENT, SubresourceRange {
+ aspects: Aspects::DEPTH,
+ levels: 0..1,
+ layers: 0..1,
+ },properties.extent.width as usize, properties.extent.height as usize).map_err(|_| TargetChainCreationError::Todo)
+ }?;
+
+ let mut targets: Vec<TargetResources> = Vec::with_capacity(backbuffer.len());
+ for image in backbuffer.drain(..) {
+ targets.push(TargetResources::new(device, cmd_pool, renderpass, image, &(*depth_buffer.image_view), properties.extent, properties.format)
+ .map_err(|_| TargetChainCreationError::Todo)?);
+ }
+
+ Ok(TargetChain {
+ swapchain: ManuallyDrop::new(swapchain),
+ targets: targets.into_boxed_slice(),
+ depth_buffer: ManuallyDrop::new(depth_buffer),
+ properties,
+ last_drawn: (image_count - 1) as usize, // This means the next one to be used is index 0
+ last_image_index: 0
+ })
+ }
+
+ pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) -> () {
+ use core::ptr::read;
+ unsafe {
+ ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device);
+
+ for i in 0..self.targets.len() {
+ read(&self.targets[i]).deactivate(device, cmd_pool);
+ }
+
+ device.destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain)));
+ }
+ }
+
+ pub fn deactivate_with_recyling(self, device: &mut Device, cmd_pool: &mut CommandPool) -> Swapchain {
+ use core::ptr::read;
+ unsafe {
+ ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device);
+
+ for i in 0..self.targets.len() {
+ read(&self.targets[i]).deactivate(device, cmd_pool);
+ }
+ }
+
+ return unsafe { ManuallyDrop::into_inner(read(&self.swapchain)) };
+ }
+
+ pub fn prep_next_target<'a>(&'a mut self, device: &mut Device, vert_buffer: &Buffer, index_buffer: &Buffer, renderpass: &RenderPass, pipeline: &GraphicsPipeline, pipeline_layout: &PipelineLayout, camera: &mut WorkingCamera) -> Result<&'a mut crate::types::CommandBuffer, &'static str> {
+ self.last_drawn = (self.last_drawn + 1) % self.targets.len();
+
+ let target = &mut self.targets[self.last_drawn];
+
+ // Get the image
+ let (image_index, _) = unsafe {
+ self.swapchain
+ .acquire_image(core::u64::MAX, Some(&target.get_image), None)
+ .map_err(|_| "FrameError::AcquireError")?
+ };
+
+ self.last_image_index = image_index;
+
+ // Make sure whatever was last using this has finished
+ unsafe {
+ device
+ .wait_for_fence(&target.present_complete, core::u64::MAX)
+ .map_err(|_| "FrameError::SyncObjectError")?;
+ device
+ .reset_fence(&target.present_complete)
+ .map_err(|_| "FrameError::SyncObjectError")?;
+ };
+
+ // Record commands
+ unsafe {
+ use hal::buffer::{IndexBufferView, SubRange};
+ use hal::command::{SubpassContents, CommandBufferFlags, ClearValue, ClearColor, ClearDepthStencil};
+ use hal::pso::ShaderStageFlags;
+
+ // Colour to clear window to
+ let clear_values = [ClearValue {
+ color: ClearColor {
+ float32: [0.0, 0.0, 0.0, 1.0]
+ }
+ }, ClearValue {
+ depth_stencil: ClearDepthStencil {
+ depth: 1.0,
+ stencil: 0
+ }
+ }];
+
+ // Get references to our buffers
+ let (vbufs, ibuf) = {
+ let vbufref: &<back::Backend as hal::Backend>::Buffer = vert_buffer;
+
+ let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into();
+ let ibuf = index_buffer;
+
+ (vbufs, ibuf)
+ };
+
+ target.cmd_buffer.begin_primary(CommandBufferFlags::EMPTY);
+ // Main render pass / pipeline
+ target.cmd_buffer.begin_render_pass(
+ renderpass,
+ &target.framebuffer,
+ self.properties.viewport.rect,
+ clear_values.iter(),
+ SubpassContents::Inline
+ );
+ target.cmd_buffer.bind_graphics_pipeline(&pipeline);
+
+ // VP Matrix
+ let vp = camera.get_matrix().as_slice();
+ let vp = std::mem::transmute::<&[f32], &[u32]>(vp);
+
+ target.cmd_buffer.push_graphics_constants(
+ &pipeline_layout,
+ ShaderStageFlags::VERTEX,
+ 0,
+ vp);
+
+ // Bind buffers
+ target.cmd_buffer.bind_vertex_buffers(0, vbufs);
+ target.cmd_buffer.bind_index_buffer(IndexBufferView {
+ buffer: ibuf,
+ range: SubRange::WHOLE,
+ index_type: hal::IndexType::U16
+ });
+ };
+
+ Ok(&mut target.cmd_buffer)
+ }
+
+ pub fn finish_and_submit_target(&mut self, command_queue: &mut CommandQueue) -> Result<(), &'static str> {
+ let target = &mut self.targets[self.last_drawn];
+
+ unsafe {
+ target.cmd_buffer.end_render_pass();
+ target.cmd_buffer.finish();
+ }
+
+ // Make submission object
+ let command_buffers: std::iter::Once<&CommandBuffer> = once(&target.cmd_buffer);
+ let wait_semaphores: std::iter::Once<(&Semaphore, hal::pso::PipelineStage)> = once((&target.get_image, hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT));
+ let signal_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete);
+
+ let present_wait_semaphores: std::iter::Once<&Semaphore> = once(&target.render_complete);
+
+ let submission = Submission {
+ command_buffers,
+ wait_semaphores,
+ signal_semaphores,
+ };
+
+ // Submit it
+ unsafe {
+ command_queue.submit(submission, Some(&target.present_complete));
+ self.swapchain
+ .present(command_queue, self.last_image_index as u32, present_wait_semaphores)
+ .map_err(|_| "FrameError::PresentError")?;
+ };
+
+ // TODO
+ Ok(())
+ }
+}
+
+/// Resources for a single target frame, including sync objects
+pub struct TargetResources {
+ /// Command buffer to use when drawing
+ pub cmd_buffer: ManuallyDrop<CommandBuffer>,
+
+ /// The image for this frame
+ pub image: ManuallyDrop<Image>,
+
+ /// Imageviews for this frame
+ pub imageview: ManuallyDrop<ImageView>,
+
+ /// Framebuffer for this frame
+ pub framebuffer: ManuallyDrop<Framebuffer>,
+
+ // Sync objects
+
+ /// Triggered when the image is ready to draw to
+ pub get_image: ManuallyDrop<Semaphore>,
+
+ /// Triggered when rendering is done
+ pub render_complete: ManuallyDrop<Semaphore>,
+
+ /// Triggered when the image is on screen
+ pub present_complete: ManuallyDrop<Fence>
+}
+
+
+impl TargetResources {
+ pub fn new(device: &mut Device, cmd_pool: &mut CommandPool, renderpass: &RenderPass, image: Image, depth_pass: &ImageView, extent: Extent, format: Format) -> Result<TargetResources, TargetResourcesCreationError> {
+ // Command Buffer
+ let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) };
+
+ // ImageView
+ let imageview = unsafe { device.create_image_view(
+ &image,
+ ViewKind::D2,
+ format,
+ Swizzle::NO,
+ COLOR_RANGE.clone(),
+ ).map_err(|e| TargetResourcesCreationError::ImageViewError (e))? };
+
+ // Framebuffer
+ let framebuffer = unsafe { device.create_framebuffer(
+ &renderpass,
+ once(&imageview).chain(once(depth_pass)),
+ extent
+ ).map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? };
+
+ // Sync objects
+ let get_image = device.create_semaphore().map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?;
+ let render_complete = device.create_semaphore().map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?;
+ let present_complete = device.create_fence(true).map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?;
+
+ Ok(TargetResources {
+ cmd_buffer: ManuallyDrop::new(cmd_buffer),
+ image: ManuallyDrop::new(image),
+ imageview: ManuallyDrop::new(imageview),
+ framebuffer: ManuallyDrop::new(framebuffer),
+ get_image: ManuallyDrop::new(get_image),
+ render_complete: ManuallyDrop::new(render_complete),
+ present_complete: ManuallyDrop::new(present_complete)
+ })
+ }
+
+ pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) -> () {
+ use core::ptr::read;
+ unsafe {
+ cmd_pool.free(once(ManuallyDrop::into_inner(read(&self.cmd_buffer))));
+
+ device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer)));
+ device.destroy_image_view(ManuallyDrop::into_inner(read(&self.imageview)));
+
+ device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.get_image)));
+ device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.render_complete)));
+ device.destroy_fence(ManuallyDrop::into_inner(read(&self.present_complete)));
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum TargetChainCreationError {
+ Todo
+}
+
+#[derive(Debug)]
+pub enum TargetResourcesCreationError {
+ ImageViewError (hal::image::ViewCreationError),
+ FrameBufferNoMemory,
+ SyncObjectsNoMemory
+}
diff --git a/stockton-render/src/draw/texture/chunk.rs b/stockton-render/src/draw/texture/chunk.rs
index 22541a5..40eb21e 100644
--- a/stockton-render/src/draw/texture/chunk.rs
+++ b/stockton-render/src/draw/texture/chunk.rs
@@ -31,7 +31,7 @@ use crate::{
use log::debug;
use super::resolver::TextureResolver;
-use super::image::LoadedImage;
+use super::image::SampledImage;
use stockton_levels::prelude::*;
/// The size of a chunk. Needs to match up with the fragment shader
@@ -40,7 +40,7 @@ pub const CHUNK_SIZE: usize = 8;
/// An array of textures
pub struct TextureChunk {
pub(crate) descriptor_set: DescriptorSet,
- loaded_images: Vec<LoadedImage>,
+ sampled_images: Vec<SampledImage>,
}
impl TextureChunk {
@@ -65,7 +65,7 @@ impl TextureChunk {
let mut store = TextureChunk {
descriptor_set: descriptor_set,
- loaded_images: Vec::with_capacity(CHUNK_SIZE),
+ sampled_images: Vec::with_capacity(CHUNK_SIZE),
};
let mut local_idx = 0;
@@ -110,12 +110,14 @@ impl TextureChunk {
command_pool: &mut CommandPool) -> Result<(), &'static str>{
// Load the image
- let texture = LoadedImage::load(
+ let texture = SampledImage::load_into_new(
image,
device,
adapter,
command_queue,
command_pool,
+ hal::format::Format::Rgba8Srgb, // TODO
+ hal::image::Usage::empty()
)?;
// Write it to the descriptor set
@@ -129,7 +131,7 @@ impl TextureChunk {
binding: 0,
array_offset: idx,
descriptors: Some(Descriptor::Image(
- texture.image_view.deref(),
+ texture.image.image_view.deref(),
Layout::ShaderReadOnlyOptimal
)),
},
@@ -144,17 +146,17 @@ impl TextureChunk {
// Store it so we can safely deactivate it when we need to
// Deactivate the old image if we need to
- if idx < self.loaded_images.len() {
- replace(&mut self.loaded_images[idx], texture).deactivate(device);
+ if idx < self.sampled_images.len() {
+ replace(&mut self.sampled_images[idx], texture).deactivate(device);
} else {
- self.loaded_images.push(texture);
+ self.sampled_images.push(texture);
}
Ok(())
}
pub fn deactivate(mut self, device: &mut Device) -> () {
- for img in self.loaded_images.drain(..) {
+ for img in self.sampled_images.drain(..) {
img.deactivate(device);
}
}
diff --git a/stockton-render/src/draw/texture/image.rs b/stockton-render/src/draw/texture/image.rs
index 583c2d9..530628a 100644
--- a/stockton-render/src/draw/texture/image.rs
+++ b/stockton-render/src/draw/texture/image.rs
@@ -26,7 +26,7 @@ use hal::{
MemoryTypeId,
buffer::Usage as BufUsage,
format::{Format, Swizzle, Aspects},
- image::{ViewKind, SubresourceRange},
+ image::{ViewKind, SubresourceRange, Usage as ImgUsage},
queue::Submission,
memory::{Properties as MemProperties, Dependencies as MemDependencies, Segment},
prelude::*,
@@ -46,28 +46,91 @@ pub struct LoadedImage {
/// The full view of the image
pub image_view: ManuallyDrop<ImageView>,
- /// A sampler for the image
- pub sampler: ManuallyDrop<Sampler>,
-
/// The memory backing the image
memory: ManuallyDrop<Memory>
}
+pub fn create_image_view(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, width: usize, height: usize) -> Result<(Memory, 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 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))
+
+}
+
impl LoadedImage {
- /// Load the given image into a new buffer
- pub fn load(img: RgbaImage, device: &mut Device, adapter: &Adapter,
- command_queue: &mut CommandQueue,
- command_pool: &mut CommandPool) -> Result<LoadedImage, &'static str> {
- // Round up the size to align properly
- let initial_row_size = PIXEL_SIZE * (img.width() as usize);
+ pub fn new(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, resources: SubresourceRange, width: usize, height: usize) -> Result<LoadedImage, &'static str> {
+ let (memory, image_ref) = create_image_view(device, adapter, 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(&mut self, img: RgbaImage, 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);
- let total_size = (row_size * img.height() as usize) as u64;
-
// Make a staging buffer
let (staging_buffer, staging_memory) = create_buffer(device, adapter, BufUsage::TRANSFER_SRC, MemProperties::CPU_VISIBLE, total_size)
.map_err(|_| "Couldn't create staging buffer")?;
@@ -86,43 +149,6 @@ impl LoadedImage {
device.unmap_memory(&staging_memory);
}
- // Make the image
- let mut image_ref = unsafe {
- use hal::image::{Kind, Tiling, Usage, ViewCapabilities};
-
- device.create_image(
- Kind::D2(img.width(), img.height(), 1, 1),
- 1,
- Format::Rgba8Srgb,
- Tiling::Optimal,
- Usage::TRANSFER_DST | Usage::SAMPLED,
- ViewCapabilities::empty()
- )
- }.map_err(|_| "Couldn't create image")?;
-
- // 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)
- }?;
-
// Copy from staging to image memory
let buf = unsafe {
use hal::command::{CommandBufferFlags, BufferImageCopy};
@@ -141,7 +167,7 @@ impl LoadedImage {
Access::TRANSFER_WRITE,
Layout::TransferDstOptimal,
),
- target: &image_ref,
+ target: &(*self.image),
families: None,
range: SubresourceRange {
aspects: Aspects::COLOR,
@@ -156,7 +182,7 @@ impl LoadedImage {
);
// Copy from buffer to image
- buf.copy_buffer_to_image(&staging_buffer, &image_ref,
+ buf.copy_buffer_to_image(&staging_buffer, &(*self.image),
Layout::TransferDstOptimal, &[
BufferImageCopy {
buffer_offset: 0,
@@ -187,7 +213,7 @@ impl LoadedImage {
Access::SHADER_READ,
Layout::ShaderReadOnlyOptimal,
),
- target: &image_ref,
+ target: &(*self.image),
families: None,
range: SubresourceRange {
aspects: Aspects::COLOR,
@@ -229,18 +255,49 @@ impl LoadedImage {
device.destroy_buffer(staging_buffer);
}
- // Create ImageView and sampler
- let image_view = unsafe { device.create_image_view(
- &image_ref,
- ViewKind::D2,
- Format::Rgba8Srgb,
- Swizzle::NO,
- SubresourceRange {
- aspects: Aspects::COLOR,
- levels: 0..1,
- layers: 0..1,
- },
- )}.map_err(|_| "Couldn't create the image view!")?;
+ Ok(())
+ }
+
+ /// Load the given image into a new buffer
+ pub fn load_into_new(img: RgbaImage, device: &mut Device, adapter: &Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool, format: Format, usage: ImgUsage) -> Result<LoadedImage, &'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: &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)));
+ }
+ }
+}
+
+
+pub struct SampledImage {
+ pub image: ManuallyDrop<LoadedImage>,
+ pub sampler: ManuallyDrop<Sampler>
+}
+
+impl SampledImage {
+ pub fn new(device: &mut Device, adapter: &Adapter, format: Format, usage: ImgUsage, width: usize, height: usize) -> Result<SampledImage, &'static str> {
+ let image = LoadedImage::new(device, adapter, format, usage | ImgUsage::SAMPLED, SubresourceRange {
+ aspects: Aspects::COLOR,
+ levels: 0..1,
+ layers: 0..1,
+ }, width, height)?;
let sampler = unsafe {
use hal::image::{SamplerDesc, Filter, WrapMode};
@@ -251,24 +308,29 @@ impl LoadedImage {
))
}.map_err(|_| "Couldn't create the sampler!")?;
- Ok(LoadedImage {
- image: ManuallyDrop::new(image_ref),
- image_view: ManuallyDrop::new(image_view),
- sampler: ManuallyDrop::new(sampler),
- memory: ManuallyDrop::new(memory)
+ Ok(SampledImage {
+ image: ManuallyDrop::new(image),
+ sampler: ManuallyDrop::new(sampler)
})
}
- /// Properly frees/destroys all the objects in this struct
- /// Dropping without doing this is a bad idea
- pub fn deactivate(self, device: &Device) -> () {
+ pub fn load_into_new(img: RgbaImage, device: &mut Device, adapter: &Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool, format: Format, usage: ImgUsage) -> Result<SampledImage, &'static str> {
+ let mut sampled_image = SampledImage::new(device, adapter, 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) -> () {
unsafe {
use core::ptr::read;
device.destroy_sampler(ManuallyDrop::into_inner(read(&self.sampler)));
- 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)));
+
+ ManuallyDrop::into_inner(read(&self.image)).deactivate(device);
}
}
-} \ No newline at end of file
+}
diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs
index 483fb7d..1ac0961 100644
--- a/stockton-render/src/draw/texture/loader.rs
+++ b/stockton-render/src/draw/texture/loader.rs
@@ -165,11 +165,6 @@ impl TextureStore {
}
}
- /// Get number of chunks being used
- pub fn get_n_chunks(&self) -> usize {
- self.chunks.len()
- }
-
/// Get the descriptor set for a given chunk
pub fn get_chunk_descriptor_set<'a>(&'a self, idx: usize) -> &'a DescriptorSet {
&self.chunks[idx].descriptor_set
diff --git a/stockton-render/src/draw/texture/mod.rs b/stockton-render/src/draw/texture/mod.rs
index 9951eeb..05dfe38 100644
--- a/stockton-render/src/draw/texture/mod.rs
+++ b/stockton-render/src/draw/texture/mod.rs
@@ -16,9 +16,9 @@
//! Everything related to loading textures into GPU memory
mod resolver;
-mod image;
+pub mod image;
mod chunk;
pub mod loader;
pub use self::loader::TextureStore;
-pub use self::image::LoadedImage; \ No newline at end of file
+pub use self::image::{LoadedImage, SampledImage}; \ No newline at end of file
diff --git a/stockton-render/src/error.rs b/stockton-render/src/error.rs
index 5ab0822..8922423 100644
--- a/stockton-render/src/error.rs
+++ b/stockton-render/src/error.rs
@@ -15,6 +15,8 @@
//! Error types
+use super::draw::target::TargetChainCreationError;
+
/// An error encountered creating a rendering context.
/// Falls into 3 main types:
/// - Hardware - No suitable card usually
@@ -23,6 +25,7 @@
/// - Runtime - Things caused by runtime conditions, usually resource constraints. Could also be caused by corrupt files
#[derive(Debug)]
pub enum CreationError {
+ TargetChainCreationError (TargetChainCreationError),
WindowError,
BadSurface,