use std::any::Any;
use std::borrow::Borrow;
use std::ops::Range;
use std::{fmt, iter};
use crate::{
buffer,
format,
image,
memory::{Requirements, Segment},
pass,
pool::CommandPoolCreateFlags,
pso,
pso::DescriptorPoolCreateFlags,
query,
queue::QueueFamilyId,
window::{self, SwapchainConfig},
Backend,
MemoryTypeId,
};
#[derive(Clone, Debug, PartialEq)]
pub struct DeviceLost;
impl std::fmt::Display for DeviceLost {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.write_str("Device lost")
}
}
impl std::error::Error for DeviceLost {}
#[derive(Clone, Debug, PartialEq)]
pub struct SurfaceLost;
impl std::fmt::Display for SurfaceLost {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.write_str("Surface lost")
}
}
impl std::error::Error for SurfaceLost {}
#[derive(Clone, Debug, PartialEq)]
pub struct WindowInUse;
impl std::fmt::Display for WindowInUse {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.write_str("Window is in use")
}
}
impl std::error::Error for WindowInUse {}
#[derive(Clone, Debug, PartialEq)]
pub enum OutOfMemory {
Host,
Device,
}
impl std::fmt::Display for OutOfMemory {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OutOfMemory::Host => write!(fmt, "Out of host memory"),
OutOfMemory::Device => write!(fmt, "Out of device memory"),
}
}
}
impl std::error::Error for OutOfMemory {}
#[derive(Clone, Debug, PartialEq)]
pub enum OomOrDeviceLost {
OutOfMemory(OutOfMemory),
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)
}
}
impl std::fmt::Display for OomOrDeviceLost {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OomOrDeviceLost::DeviceLost(err) => write!(fmt, "Failed querying device: {}", err),
OomOrDeviceLost::OutOfMemory(err) => write!(fmt, "Failed querying device: {}", err),
}
}
}
impl std::error::Error for OomOrDeviceLost {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
OomOrDeviceLost::DeviceLost(err) => Some(err),
OomOrDeviceLost::OutOfMemory(err) => Some(err),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum AllocationError {
OutOfMemory(OutOfMemory),
TooManyObjects,
}
impl From<OutOfMemory> for AllocationError {
fn from(error: OutOfMemory) -> Self {
AllocationError::OutOfMemory(error)
}
}
impl std::fmt::Display for AllocationError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AllocationError::OutOfMemory(err) => write!(fmt, "Failed to allocate object: {}", err),
AllocationError::TooManyObjects => {
write!(fmt, "Failed to allocate object: Too many objects")
}
}
}
}
impl std::error::Error for AllocationError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
AllocationError::OutOfMemory(err) => Some(err),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum CreationError {
OutOfMemory(OutOfMemory),
InitializationFailed,
MissingExtension,
MissingFeature,
TooManyObjects,
DeviceLost,
}
impl std::fmt::Display for CreationError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CreationError::OutOfMemory(err) => write!(fmt, "Failed to create device: {}", err),
CreationError::InitializationFailed => write!(
fmt,
"Failed to create device: Implementation specific error occurred"
),
CreationError::MissingExtension => write!(
fmt,
"Failed to create device: Requested extension is missing"
),
CreationError::MissingFeature => {
write!(fmt, "Failed to create device: Requested feature is missing")
}
CreationError::TooManyObjects => {
write!(fmt, "Failed to create device: Too many objects")
}
CreationError::DeviceLost => write!(
fmt,
"Failed to create device: Logical or Physical device was lost during creation"
),
}
}
}
impl std::error::Error for CreationError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
CreationError::OutOfMemory(err) => Some(err),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum MapError {
OutOfMemory(OutOfMemory),
OutOfBounds,
MappingFailed,
}
impl From<OutOfMemory> for MapError {
fn from(error: OutOfMemory) -> Self {
MapError::OutOfMemory(error)
}
}
impl std::fmt::Display for MapError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MapError::OutOfMemory(err) => write!(fmt, "Failed to map memory: {}", err),
MapError::OutOfBounds => write!(fmt, "Failed to map memory: Requested range is outside the resource"),
MapError::MappingFailed => write!(fmt, "Failed to map memory: Unable to allocate an appropriately sized contiguous virtual address range"),
}
}
}
impl std::error::Error for MapError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
MapError::OutOfMemory(err) => Some(err),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum BindError {
OutOfMemory(OutOfMemory),
WrongMemory,
OutOfBounds,
}
impl From<OutOfMemory> for BindError {
fn from(error: OutOfMemory) -> Self {
BindError::OutOfMemory(error)
}
}
impl std::fmt::Display for BindError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BindError::OutOfMemory(err) => {
write!(fmt, "Failed to bind object to memory range: {}", err)
}
BindError::OutOfBounds => write!(
fmt,
"Failed to bind object to memory range: Requested range is outside the resource"
),
BindError::WrongMemory => {
write!(fmt, "Failed to bind object to memory range: Wrong memory")
}
}
}
}
impl std::error::Error for BindError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
BindError::OutOfMemory(err) => Some(err),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum WaitFor {
Any,
All,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ShaderError {
CompilationFailed(String),
MissingEntryPoint(String),
InterfaceMismatch(String),
UnsupportedStage(pso::Stage),
OutOfMemory(OutOfMemory),
}
impl From<OutOfMemory> for ShaderError {
fn from(error: OutOfMemory) -> Self {
ShaderError::OutOfMemory(error)
}
}
impl std::fmt::Display for ShaderError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ShaderError::OutOfMemory(err) => write!(fmt, "Shader error: {}", err),
ShaderError::CompilationFailed(string) => {
write!(fmt, "Shader error: Compilation failed: {}", string)
}
ShaderError::MissingEntryPoint(string) => {
write!(fmt, "Shader error: Missing entry point: {}", string)
}
ShaderError::InterfaceMismatch(string) => {
write!(fmt, "Shader error: Interface mismatch: {}", string)
}
ShaderError::UnsupportedStage(stage) => {
write!(fmt, "Shader error: Unsupported stage: {:?}", stage)
}
}
}
}
impl std::error::Error for ShaderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ShaderError::OutOfMemory(err) => Some(err),
_ => None,
}
}
}
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 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>;
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>;
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: &[u32],
) -> 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(
&self,
buf: &B::Buffer,
fmt: Option<format::Format>,
range: buffer::SubRange,
) -> 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::ViewCreationError>;
unsafe fn destroy_image_view(&self, view: B::ImageView);
unsafe fn create_sampler(
&self,
desc: &image::SamplerDesc,
) -> 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(&self, memory: &B::Memory, segment: Segment) -> Result<*mut u8, MapError>;
unsafe fn flush_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), OutOfMemory>
where
I: IntoIterator,
I::Item: Borrow<(&'a B::Memory, Segment)>;
unsafe fn invalidate_mapped_memory_ranges<'a, I>(&self, ranges: I) -> Result<(), OutOfMemory>
where
I: IntoIterator,
I::Item: Borrow<(&'a B::Memory, Segment)>;
unsafe fn unmap_memory(&self, memory: &B::Memory);
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);
fn create_event(&self) -> Result<B::Event, OutOfMemory>;
unsafe fn destroy_event(&self, event: B::Event);
unsafe fn get_event_status(&self, event: &B::Event) -> Result<bool, OomOrDeviceLost>;
unsafe fn set_event(&self, event: &B::Event) -> Result<(), OutOfMemory>;
unsafe fn reset_event(&self, event: &B::Event) -> Result<(), OutOfMemory>;
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<(), OutOfMemory>;
unsafe fn set_image_name(&self, image: &mut B::Image, name: &str);
unsafe fn set_buffer_name(&self, buffer: &mut B::Buffer, name: &str);
unsafe fn set_command_buffer_name(&self, command_buffer: &mut B::CommandBuffer, name: &str);
unsafe fn set_semaphore_name(&self, semaphore: &mut B::Semaphore, name: &str);
unsafe fn set_fence_name(&self, fence: &mut B::Fence, name: &str);
unsafe fn set_framebuffer_name(&self, framebuffer: &mut B::Framebuffer, name: &str);
unsafe fn set_render_pass_name(&self, render_pass: &mut B::RenderPass, name: &str);
unsafe fn set_descriptor_set_name(&self, descriptor_set: &mut B::DescriptorSet, name: &str);
unsafe fn set_descriptor_set_layout_name(
&self,
descriptor_set_layout: &mut B::DescriptorSetLayout,
name: &str,
);
}