use std::any::Any;
use std::borrow::Borrow;
use std::ops::Range;
use std::{fmt, iter, mem, slice};
use crate::{buffer, format, image, mapping, pass, pso, query};
use crate::{Backend, MemoryTypeId};
use crate::error::HostExecutionError;
use crate::memory::Requirements;
use crate::pool::{CommandPool, CommandPoolCreateFlags};
use crate::pso::DescriptorPoolCreateFlags;
use crate::queue::{QueueFamilyId, QueueGroup};
use crate::range::RangeArg;
use crate::window::{self, SwapchainConfig};
#[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)]
#[fail(display = "Device is lost")]
pub struct DeviceLost;
#[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)]
#[fail(display = "Surface is lost")]
pub struct SurfaceLost;
#[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)]
#[fail(display = "Native window in use")]
pub struct WindowInUse;
#[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)]
pub enum OutOfMemory {
#[fail(display = "Out of host memory")]
OutOfHostMemory,
#[fail(display = "Out of device memory")]
OutOfDeviceMemory,
}
#[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)]
pub enum OomOrDeviceLost {
#[fail(display = "{}", _0)]
OutOfMemory(OutOfMemory),
#[fail(display = "{}", _0)]
DeviceLost(DeviceLost),
}
impl From<OutOfMemory> for OomOrDeviceLost {
fn from(error: OutOfMemory) -> Self {
OomOrDeviceLost::OutOfMemory(error)
}
}
impl From<DeviceLost> for OomOrDeviceLost {
fn from(error: DeviceLost) -> Self {
OomOrDeviceLost::DeviceLost(error)
}
}
#[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)]
pub enum AllocationError {
#[fail(display = "{}", _0)]
OutOfMemory(OutOfMemory),
#[fail(display = "Can't allocate more memory objects")]
TooManyObjects,
}
impl From<OutOfMemory> for AllocationError {
fn from(error: OutOfMemory) -> Self {
AllocationError::OutOfMemory(error)
}
}
#[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)]
pub enum BindError {
#[fail(display = "{}", _0)]
OutOfMemory(OutOfMemory),
#[fail(display = "Unsupported memory allocation for the requirements")]
WrongMemory,
#[fail(display = "Not enough space in the memory allocation")]
OutOfBounds,
}
impl From<OutOfMemory> for BindError {
fn from(error: OutOfMemory) -> Self {
BindError::OutOfMemory(error)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum WaitFor {
Any,
All,
}
#[derive(Clone, Debug, Fail, PartialEq, Eq)]
pub enum ShaderError {
#[fail(display = "shader compilation failed: {}", _0)]
CompilationFailed(String),
#[fail(display = "shader is missing an entry point: {}", _0)]
MissingEntryPoint(String),
#[fail(display = "shader interface mismatch: {}", _0)]
InterfaceMismatch(String),
#[fail(display = "shader stage \"{}\" is unsupported", _0)]
UnsupportedStage(pso::Stage),
#[fail(display = "{}", _0)]
OutOfMemory(OutOfMemory),
}
impl From<OutOfMemory> for ShaderError {
fn from(error: OutOfMemory) -> Self {
ShaderError::OutOfMemory(error)
}
}
pub trait Device<B: Backend>: fmt::Debug + Any + Send + Sync {
unsafe fn allocate_memory(
&self,
memory_type: MemoryTypeId,
size: u64,
) -> Result<B::Memory, AllocationError>;
unsafe fn free_memory(&self, memory: B::Memory);
unsafe fn create_command_pool(
&self,
family: QueueFamilyId,
create_flags: CommandPoolCreateFlags,
) -> Result<B::CommandPool, OutOfMemory>;
unsafe fn create_command_pool_typed<C>(
&self,
group: &QueueGroup<B, C>,
flags: CommandPoolCreateFlags,
) -> Result<CommandPool<B, C>, OutOfMemory> {
let raw = self.create_command_pool(group.family(), flags)?;
Ok(CommandPool::new(raw))
}
unsafe fn destroy_command_pool(&self, pool: B::CommandPool);
unsafe fn create_render_pass<'a, IA, IS, ID>(
&self,
attachments: IA,
subpasses: IS,
dependencies: ID,
) -> Result<B::RenderPass, OutOfMemory>
where
IA: IntoIterator,
IA::Item: Borrow<pass::Attachment>,
IS: IntoIterator,
IS::Item: Borrow<pass::SubpassDesc<'a>>,
ID: IntoIterator,
ID::Item: Borrow<pass::SubpassDependency>;
unsafe fn destroy_render_pass(&self, rp: B::RenderPass);
unsafe fn create_pipeline_layout<IS, IR>(
&self,
set_layouts: IS,
push_constant: IR,
) -> Result<B::PipelineLayout, OutOfMemory>
where
IS: IntoIterator,
IS::Item: Borrow<B::DescriptorSetLayout>,
IR: IntoIterator,
IR::Item: Borrow<(pso::ShaderStageFlags, Range<u32>)>;
unsafe fn destroy_pipeline_layout(&self, layout: B::PipelineLayout);
unsafe fn create_pipeline_cache(
&self,
data: Option<&[u8]>,
) -> Result<B::PipelineCache, OutOfMemory>;
unsafe fn get_pipeline_cache_data(
&self,
cache: &B::PipelineCache,
) -> Result<Vec<u8>, OutOfMemory>;
unsafe fn merge_pipeline_caches<I>(
&self,
target: &B::PipelineCache,
sources: I,
) -> Result<(), OutOfMemory>
where
I: IntoIterator,
I::Item: Borrow<B::PipelineCache>;
unsafe fn destroy_pipeline_cache(&self, cache: B::PipelineCache);
unsafe fn create_graphics_pipeline<'a>(
&self,
desc: &pso::GraphicsPipelineDesc<'a, B>,
cache: Option<&B::PipelineCache>,
) -> Result<B::GraphicsPipeline, pso::CreationError> {
self.create_graphics_pipelines(iter::once(desc), cache)
.remove(0)
}
unsafe fn create_graphics_pipelines<'a, I>(
&self,
descs: I,
cache: Option<&B::PipelineCache>,
) -> Vec<Result<B::GraphicsPipeline, pso::CreationError>>
where
I: IntoIterator,
I::Item: Borrow<pso::GraphicsPipelineDesc<'a, B>>,
{
descs
.into_iter()
.map(|desc| self.create_graphics_pipeline(desc.borrow(), cache))
.collect()
}
unsafe fn destroy_graphics_pipeline(&self, pipeline: B::GraphicsPipeline);
unsafe fn create_compute_pipeline<'a>(
&self,
desc: &pso::ComputePipelineDesc<'a, B>,
cache: Option<&B::PipelineCache>,
) -> Result<B::ComputePipeline, pso::CreationError> {
self.create_compute_pipelines(iter::once(desc), cache)
.remove(0)
}
unsafe fn create_compute_pipelines<'a, I>(
&self,
descs: I,
cache: Option<&B::PipelineCache>,
) -> Vec<Result<B::ComputePipeline, pso::CreationError>>
where
I: IntoIterator,
I::Item: Borrow<pso::ComputePipelineDesc<'a, B>>,
{
descs
.into_iter()
.map(|desc| self.create_compute_pipeline(desc.borrow(), cache))
.collect()
}
unsafe fn destroy_compute_pipeline(&self, pipeline: B::ComputePipeline);
unsafe fn create_framebuffer<I>(
&self,
pass: &B::RenderPass,
attachments: I,
extent: image::Extent,
) -> Result<B::Framebuffer, OutOfMemory>
where
I: IntoIterator,
I::Item: Borrow<B::ImageView>;
unsafe fn destroy_framebuffer(&self, buf: B::Framebuffer);
unsafe fn create_shader_module(
&self,
spirv_data: &[u8],
) -> Result<B::ShaderModule, ShaderError>;
unsafe fn destroy_shader_module(&self, shader: B::ShaderModule);
unsafe fn create_buffer(
&self,
size: u64,
usage: buffer::Usage,
) -> Result<B::Buffer, buffer::CreationError>;
unsafe fn get_buffer_requirements(&self, buf: &B::Buffer) -> Requirements;
unsafe fn bind_buffer_memory(
&self,
memory: &B::Memory,
offset: u64,
buf: &mut B::Buffer,
) -> Result<(), BindError>;
unsafe fn destroy_buffer(&self, buffer: B::Buffer);
unsafe fn create_buffer_view<R: RangeArg<u64>>(
&self,
buf: &B::Buffer,
fmt: Option<format::Format>,
range: R,
) -> Result<B::BufferView, buffer::ViewCreationError>;
unsafe fn destroy_buffer_view(&self, view: B::BufferView);
unsafe fn create_image(
&self,
kind: image::Kind,
mip_levels: image::Level,
format: format::Format,
tiling: image::Tiling,
usage: image::Usage,
view_caps: image::ViewCapabilities,
) -> Result<B::Image, image::CreationError>;
unsafe fn get_image_requirements(&self, image: &B::Image) -> Requirements;
unsafe fn get_image_subresource_footprint(
&self,
image: &B::Image,
subresource: image::Subresource,
) -> image::SubresourceFootprint;
unsafe fn bind_image_memory(
&self,
memory: &B::Memory,
offset: u64,
image: &mut B::Image,
) -> Result<(), BindError>;
unsafe fn destroy_image(&self, image: B::Image);
unsafe fn create_image_view(
&self,
image: &B::Image,
view_kind: image::ViewKind,
format: format::Format,
swizzle: format::Swizzle,
range: image::SubresourceRange,
) -> Result<B::ImageView, image::ViewError>;
unsafe fn destroy_image_view(&self, view: B::ImageView);
unsafe fn create_sampler(
&self,
info: image::SamplerInfo,
) -> Result<B::Sampler, AllocationError>;
unsafe fn destroy_sampler(&self, sampler: B::Sampler);
unsafe fn create_descriptor_pool<I>(
&self,
max_sets: usize,
descriptor_ranges: I,
flags: DescriptorPoolCreateFlags,
) -> Result<B::DescriptorPool, OutOfMemory>
where
I: IntoIterator,
I::Item: Borrow<pso::DescriptorRangeDesc>;
unsafe fn destroy_descriptor_pool(&self, pool: B::DescriptorPool);
unsafe fn create_descriptor_set_layout<I, J>(
&self,
bindings: I,
immutable_samplers: J,
) -> Result<B::DescriptorSetLayout, OutOfMemory>
where
I: IntoIterator,
I::Item: Borrow<pso::DescriptorSetLayoutBinding>,
J: IntoIterator,
J::Item: Borrow<B::Sampler>;
unsafe fn destroy_descriptor_set_layout(&self, layout: B::DescriptorSetLayout);
unsafe fn write_descriptor_sets<'a, I, J>(&self, write_iter: I)
where
I: IntoIterator<Item = pso::DescriptorSetWrite<'a, B, J>>,
J: IntoIterator,
J::Item: Borrow<pso::Descriptor<'a, B>>;
unsafe fn copy_descriptor_sets<'a, I>(&self, copy_iter: I)
where
I: IntoIterator,
I::Item: Borrow<pso::DescriptorSetCopy<'a, B>>;
unsafe fn map_memory<R>(&self, memory: &B::Memory, range: R) -> Result<*mut u8, mapping::Error>
where
R: RangeArg<u64>;
unsafe fn flush_mapped_memory_ranges<'a, I, R>(&self, ranges: I) -> Result<(), OutOfMemory>
where
I: IntoIterator,
I::Item: Borrow<(&'a B::Memory, R)>,
R: RangeArg<u64>;
unsafe fn invalidate_mapped_memory_ranges<'a, I, R>(
&self,
ranges: I,
) -> Result<(), OutOfMemory>
where
I: IntoIterator,
I::Item: Borrow<(&'a B::Memory, R)>,
R: RangeArg<u64>;
unsafe fn unmap_memory(&self, memory: &B::Memory);
unsafe fn acquire_mapping_reader<'a, T>(
&self,
memory: &'a B::Memory,
range: Range<u64>,
) -> Result<mapping::Reader<'a, B, T>, mapping::Error>
where
T: Copy,
{
let len = range.end - range.start;
let count = len as usize / mem::size_of::<T>();
self.map_memory(memory, range.clone()).and_then(|ptr| {
let start_ptr = ptr as *const _;
self.invalidate_mapped_memory_ranges(iter::once((memory, range.clone())))?;
Ok(mapping::Reader {
slice: slice::from_raw_parts(start_ptr, count),
memory,
released: false,
})
})
}
unsafe fn release_mapping_reader<'a, T>(&self, mut reader: mapping::Reader<'a, B, T>) {
reader.released = true;
self.unmap_memory(reader.memory);
}
unsafe fn acquire_mapping_writer<'a, T>(
&self,
memory: &'a B::Memory,
range: Range<u64>,
) -> Result<mapping::Writer<'a, B, T>, mapping::Error>
where
T: Copy,
{
let count = (range.end - range.start) as usize / mem::size_of::<T>();
self.map_memory(memory, range.clone()).map(|ptr| {
let start_ptr = ptr as *mut _;
mapping::Writer {
slice: slice::from_raw_parts_mut(start_ptr, count),
memory,
range,
released: false,
}
})
}
unsafe fn release_mapping_writer<'a, T>(
&self,
mut writer: mapping::Writer<'a, B, T>,
) -> Result<(), OutOfMemory> {
writer.released = true;
self.flush_mapped_memory_ranges(iter::once((writer.memory, writer.range.clone())))?;
self.unmap_memory(writer.memory);
Ok(())
}
fn create_semaphore(&self) -> Result<B::Semaphore, OutOfMemory>;
unsafe fn destroy_semaphore(&self, semaphore: B::Semaphore);
fn create_fence(&self, signaled: bool) -> Result<B::Fence, OutOfMemory>;
unsafe fn reset_fence(&self, fence: &B::Fence) -> Result<(), OutOfMemory> {
self.reset_fences(iter::once(fence))
}
unsafe fn reset_fences<I>(&self, fences: I) -> Result<(), OutOfMemory>
where
I: IntoIterator,
I::Item: Borrow<B::Fence>,
{
for fence in fences {
self.reset_fence(fence.borrow())?;
}
Ok(())
}
unsafe fn wait_for_fence(
&self,
fence: &B::Fence,
timeout_ns: u64,
) -> Result<bool, OomOrDeviceLost> {
self.wait_for_fences(iter::once(fence), WaitFor::All, timeout_ns)
}
unsafe fn wait_for_fences<I>(
&self,
fences: I,
wait: WaitFor,
timeout_ns: u64,
) -> Result<bool, OomOrDeviceLost>
where
I: IntoIterator,
I::Item: Borrow<B::Fence>,
{
use std::{thread, time};
fn to_ns(duration: time::Duration) -> u64 {
duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64
}
let start = time::Instant::now();
match wait {
WaitFor::All => {
for fence in fences {
if !self.wait_for_fence(fence.borrow(), 0)? {
let elapsed_ns = to_ns(start.elapsed());
if elapsed_ns > timeout_ns {
return Ok(false);
}
if !self.wait_for_fence(fence.borrow(), timeout_ns - elapsed_ns)? {
return Ok(false);
}
}
}
Ok(true)
}
WaitFor::Any => {
let fences: Vec<_> = fences.into_iter().collect();
loop {
for fence in &fences {
if self.wait_for_fence(fence.borrow(), 0)? {
return Ok(true);
}
}
if to_ns(start.elapsed()) >= timeout_ns {
return Ok(false);
}
thread::sleep(time::Duration::from_millis(1));
}
}
}
}
unsafe fn get_fence_status(&self, fence: &B::Fence) -> Result<bool, DeviceLost>;
unsafe fn destroy_fence(&self, fence: B::Fence);
unsafe fn create_query_pool(
&self,
ty: query::Type,
count: query::Id,
) -> Result<B::QueryPool, query::CreationError>;
unsafe fn destroy_query_pool(&self, pool: B::QueryPool);
unsafe fn get_query_pool_results(
&self,
pool: &B::QueryPool,
queries: Range<query::Id>,
data: &mut [u8],
stride: buffer::Offset,
flags: query::ResultFlags,
) -> Result<bool, OomOrDeviceLost>;
unsafe fn create_swapchain(
&self,
surface: &mut B::Surface,
config: SwapchainConfig,
old_swapchain: Option<B::Swapchain>,
) -> Result<(B::Swapchain, Vec<B::Image>), window::CreationError>;
unsafe fn destroy_swapchain(&self, swapchain: B::Swapchain);
fn wait_idle(&self) -> Result<(), HostExecutionError>;
}