aboutsummaryrefslogtreecommitdiff
path: root/stockton-render/src/draw/texture/image.rs
diff options
context:
space:
mode:
Diffstat (limited to 'stockton-render/src/draw/texture/image.rs')
-rw-r--r--stockton-render/src/draw/texture/image.rs655
1 files changed, 361 insertions, 294 deletions
diff --git a/stockton-render/src/draw/texture/image.rs b/stockton-render/src/draw/texture/image.rs
index 530628a..0b8c64e 100644
--- a/stockton-render/src/draw/texture/image.rs
+++ b/stockton-render/src/draw/texture/image.rs
@@ -14,23 +14,20 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
use core::{
- ptr::copy_nonoverlapping,
- mem::{size_of, ManuallyDrop}
+ mem::{size_of, ManuallyDrop},
+ ptr::copy_nonoverlapping,
};
-use std::{
- iter::once,
- convert::TryInto
-};
-use image::RgbaImage;
use hal::{
- MemoryTypeId,
- buffer::Usage as BufUsage,
- format::{Format, Swizzle, Aspects},
- image::{ViewKind, SubresourceRange, Usage as ImgUsage},
- queue::Submission,
- memory::{Properties as MemProperties, Dependencies as MemDependencies, Segment},
- prelude::*,
+ buffer::Usage as BufUsage,
+ format::{Aspects, Format, Swizzle},
+ image::{SubresourceRange, Usage as ImgUsage, ViewKind},
+ memory::{Dependencies as MemDependencies, Properties as MemProperties, Segment},
+ prelude::*,
+ queue::Submission,
+ MemoryTypeId,
};
+use image::RgbaImage;
+use std::{convert::TryInto, iter::once};
use crate::types::*;
use draw::buffer::create_buffer;
@@ -40,297 +37,367 @@ const PIXEL_SIZE: usize = size_of::<image::Rgba<u8>>();
/// 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 GPU Image handle
+ image: ManuallyDrop<Image>,
- /// The full view of the image
- pub image_view: ManuallyDrop<ImageView>,
+ /// The full view of the image
+ pub image_view: ManuallyDrop<ImageView>,
- /// The memory backing the image
- memory: ManuallyDrop<Memory>
+ /// 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))
-
+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 {
- 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);
-
- // 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")?;
-
- // Copy everything into it
- unsafe {
- let mapped_memory: *mut u8 = device.map_memory(&staging_memory, Segment::ALL).map_err(|_| "Couldn't map buffer memory")?;
-
- for y in 0..img.height() as usize {
- let row = &(*img)[y * initial_row_size..(y + 1) * initial_row_size];
- let dest_base: isize = (y * row_size).try_into().unwrap();
-
- copy_nonoverlapping(row.as_ptr(), mapped_memory.offset(dest_base), row.len());
- }
- device.flush_mapped_memory_ranges(once((&staging_memory, Segment::ALL))).map_err(|_| "Couldn't write buffer memory")?;
- device.unmap_memory(&staging_memory);
- }
-
- // Copy from staging to image memory
- let buf = unsafe {
- use hal::command::{CommandBufferFlags, BufferImageCopy};
- use hal::pso::PipelineStage;
- use hal::memory::Barrier;
- use hal::image::{Access, Layout, SubresourceLayers, Offset, Extent};
-
- // 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(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 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);
+
+ // 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")?;
+
+ // Copy everything into it
+ unsafe {
+ let mapped_memory: *mut u8 = device
+ .map_memory(&staging_memory, Segment::ALL)
+ .map_err(|_| "Couldn't map buffer memory")?;
+
+ for y in 0..img.height() as usize {
+ let row = &(*img)[y * initial_row_size..(y + 1) * initial_row_size];
+ let dest_base: isize = (y * row_size).try_into().unwrap();
+
+ copy_nonoverlapping(row.as_ptr(), mapped_memory.offset(dest_base), row.len());
+ }
+ device
+ .flush_mapped_memory_ranges(once((&staging_memory, Segment::ALL)))
+ .map_err(|_| "Couldn't write buffer memory")?;
+ 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(
+ 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>
+ 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};
-
- 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(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)));
-
- ManuallyDrop::into_inner(read(&self.image)).deactivate(device);
- }
- }
+ 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::{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(
+ 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)));
+
+ ManuallyDrop::into_inner(read(&self.image)).deactivate(device);
+ }
+ }
}