use failure::Fallible;
use gfx_hal::format::Format;
use gfx_hal::image::{self, Kind, Level, Tiling, ViewCapabilities};
use gfx_hal::memory::Properties;
use gfx_hal::{buffer, Adapter, Backend, Device, Graphics, QueueGroup};
use crate::allocator::{
Allocation as _, AllocationSpec, Allocator, Hint, HybridAllocation as Allocation,
HybridAllocator,
};
use crate::resources::Resource;
pub struct Factory<B: Backend> {
alloc: HybridAllocator<B>,
pub device: B::Device,
pub physical_device: B::PhysicalDevice,
pub queue_group: QueueGroup<B, Graphics>,
}
impl<B: Backend> Factory<B> {
pub fn new(adapter: Adapter<B>) -> Fallible<Factory<B>> {
let (device, queue_group) = adapter.open_with(1, |_| true)?;
let alloc = HybridAllocator::new(&adapter.physical_device);
Ok(Factory {
alloc,
device,
queue_group,
physical_device: adapter.physical_device,
})
}
}
pub struct Buffer<B: Backend> {
pub buffer: B::Buffer,
memory: Allocation<B>,
}
impl<B: Backend> Buffer<B> {
pub fn plan(size: u64, usage: buffer::Usage) -> BufferPlan {
BufferPlan {
size,
usage,
hint: Hint::ShortTerm,
required_flags: Properties::empty(),
preferred_flags: Properties::empty(),
}
}
}
impl<B: Backend> Resource<B> for Buffer<B> {
type Plan = BufferPlan;
fn create(factory: &mut Factory<B>, plan: &Self::Plan) -> Buffer<B> {
plan.create(factory).unwrap()
}
fn destroy(self, factory: &mut Factory<B>) {
unsafe { factory.device.destroy_buffer(self.buffer) };
factory.alloc.free(&factory.device, self.memory);
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct BufferPlan {
pub size: u64,
pub usage: buffer::Usage,
pub hint: Hint,
pub required_flags: Properties,
pub preferred_flags: Properties,
}
impl BufferPlan {
pub fn coherent(&mut self) -> &mut BufferPlan {
self.preferred_flags |= Properties::COHERENT;
self
}
pub fn cached(&mut self) -> &mut BufferPlan {
self.preferred_flags |= Properties::CPU_CACHED;
self
}
pub fn device_local(&mut self) -> &mut BufferPlan {
self.preferred_flags |= Properties::DEVICE_LOCAL;
self
}
pub fn cpu_visible(&mut self) -> &mut BufferPlan {
self.required_flags |= Properties::CPU_VISIBLE;
self
}
pub fn permanent(&mut self) -> &mut BufferPlan {
self.hint = Hint::ShortTerm;
self
}
pub fn create<B: Backend>(&self, factory: &mut Factory<B>) -> Fallible<Buffer<B>> {
let device = &factory.device;
unsafe {
let mut buffer = device.create_buffer(self.size, self.usage)?;
let req = device.get_buffer_requirements(&buffer);
let spec = AllocationSpec {
hint: self.hint,
required_flags: self.required_flags,
preferred_flags: self.preferred_flags,
};
let memory = factory.alloc.alloc(device, req, spec)?;
device.bind_buffer_memory(memory.memory(), memory.range().start, &mut buffer)?;
Ok(Buffer { buffer, memory })
}
}
}
pub struct Image<B: Backend> {
pub image: B::Image,
memory: Allocation<B>,
}
impl<B: Backend> Image<B> {
pub fn plan(kind: Kind, usage: image::Usage) -> ImagePlan {
ImagePlan {
kind,
mip_levels: 0,
format: Format::Bgra8Srgb,
tiling: Tiling::Optimal,
usage,
view_caps: ViewCapabilities::empty(),
hint: Hint::ShortTerm,
required_flags: Properties::empty(),
preferred_flags: Properties::empty(),
}
}
}
impl<B: Backend> Resource<B> for Image<B> {
type Plan = ImagePlan;
fn create(factory: &mut Factory<B>, plan: &Self::Plan) -> Image<B> {
plan.create(factory).unwrap()
}
fn destroy(self, factory: &mut Factory<B>) {
unsafe { factory.device.destroy_image(self.image) };
factory.alloc.free(&factory.device, self.memory);
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ImagePlan {
pub kind: Kind,
pub mip_levels: Level,
pub format: Format,
pub tiling: Tiling,
pub usage: image::Usage,
pub view_caps: ViewCapabilities,
pub hint: Hint,
pub required_flags: Properties,
pub preferred_flags: Properties,
}
impl ImagePlan {
pub fn coherent(&mut self) -> &mut ImagePlan {
self.preferred_flags |= Properties::COHERENT;
self
}
pub fn cached(&mut self) -> &mut ImagePlan {
self.preferred_flags |= Properties::CPU_CACHED;
self
}
pub fn device_local(&mut self) -> &mut ImagePlan {
self.preferred_flags |= Properties::DEVICE_LOCAL;
self
}
pub fn cpu_visible(&mut self) -> &mut ImagePlan {
self.required_flags |= Properties::CPU_VISIBLE;
self
}
pub fn permanent(&mut self) -> &mut ImagePlan {
self.hint = Hint::ShortTerm;
self
}
pub fn create<B: Backend>(&self, factory: &mut Factory<B>) -> Fallible<Image<B>> {
let device = &factory.device;
unsafe {
let mut image = device.create_image(
self.kind,
self.mip_levels,
self.format,
self.tiling,
self.usage,
self.view_caps,
)?;
let req = device.get_image_requirements(&image);
let spec = AllocationSpec {
hint: self.hint,
required_flags: self.required_flags,
preferred_flags: self.preferred_flags,
};
let memory = factory.alloc.alloc(device, req, spec)?;
device.bind_image_memory(memory.memory(), memory.range().start, &mut image)?;
Ok(Image { image, memory })
}
}
}