diff --git a/examples/src/bin/debug.rs b/examples/src/bin/debug.rs index 7c2cc569..c03f648e 100644 --- a/examples/src/bin/debug.rs +++ b/examples/src/bin/debug.rs @@ -100,7 +100,7 @@ fn main() { // Create an image in order to generate some additional logging: let pixel_format = Format::R8G8B8A8Uint; let dimensions = Dimensions::Dim2d { width: 4096, height: 4096 }; - ImmutableImage::new(&device, dimensions, pixel_format, Some(queue.family())).unwrap(); + ImmutableImage::new(device.clone(), dimensions, pixel_format, Some(queue.family())).unwrap(); // (At this point you should see a bunch of messages printed to the terminal window - have fun debugging!) } diff --git a/examples/src/bin/image.rs b/examples/src/bin/image.rs index a4823065..85963c2e 100644 --- a/examples/src/bin/image.rs +++ b/examples/src/bin/image.rs @@ -34,9 +34,9 @@ fn main() { println!("Using device: {} (type: {:?})", physical.name(), physical.ty()); let events_loop = winit::EventsLoop::new(); - let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, &instance).unwrap(); + let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); - let queue = physical.queue_families().find(|q| q.supports_graphics() && + let queue = physical.queue_families().find(|&q| q.supports_graphics() && window.surface().is_supported(q).unwrap_or(false)) .expect("couldn't find a graphical queue family"); @@ -50,15 +50,15 @@ fn main() { let queue = queues.next().unwrap(); let (swapchain, images) = { - let caps = window.surface().get_capabilities(&physical).expect("failed to get surface capabilities"); + let caps = window.surface().capabilities(physical).expect("failed to get surface capabilities"); let dimensions = caps.current_extent.unwrap_or([1280, 1024]); let present = caps.present_modes.iter().next().unwrap(); let usage = caps.supported_usage_flags; - vulkano::swapchain::Swapchain::new(&device, &window.surface(), caps.min_image_count, + vulkano::swapchain::Swapchain::new(device.clone(), window.surface().clone(), caps.min_image_count, vulkano::format::B8G8R8A8Srgb, dimensions, 1, - &usage, &queue, vulkano::swapchain::SurfaceTransform::Identity, + usage, &queue, vulkano::swapchain::SurfaceTransform::Identity, vulkano::swapchain::CompositeAlpha::Opaque, present, true, None).expect("failed to create swapchain") }; @@ -69,7 +69,7 @@ fn main() { impl_vertex!(Vertex, position); let vertex_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer::<[Vertex]> - ::from_iter(&device, &vulkano::buffer::BufferUsage::all(), + ::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()), [ Vertex { position: [-0.5, -0.5 ] }, Vertex { position: [-0.5, 0.5 ] }, @@ -88,7 +88,7 @@ fn main() { color: { load: Clear, store: Store, - format: images[0].format(), + format: swapchain.format(), samples: 1, } }, @@ -99,7 +99,7 @@ fn main() { ).unwrap() ); - let texture = vulkano::image::immutable::ImmutableImage::new(&device, vulkano::image::Dimensions::Dim2d { width: 93, height: 93 }, + let texture = vulkano::image::immutable::ImmutableImage::new(device.clone(), vulkano::image::Dimensions::Dim2d { width: 93, height: 93 }, vulkano::format::R8G8B8A8Unorm, Some(queue.family())).unwrap(); @@ -112,20 +112,20 @@ fn main() { // TODO: staging buffer instead vulkano::buffer::cpu_access::CpuAccessibleBuffer::<[[u8; 4]]> - ::from_iter(&device, &vulkano::buffer::BufferUsage::all(), + ::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()), image_data_chunks) .expect("failed to create buffer") }; - let sampler = vulkano::sampler::Sampler::new(&device, vulkano::sampler::Filter::Linear, + let sampler = vulkano::sampler::Sampler::new(device.clone(), vulkano::sampler::Filter::Linear, vulkano::sampler::Filter::Linear, vulkano::sampler::MipmapMode::Nearest, vulkano::sampler::SamplerAddressMode::Repeat, vulkano::sampler::SamplerAddressMode::Repeat, vulkano::sampler::SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap(); - let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::new(&device, vulkano::pipeline::GraphicsPipelineParams { + let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::new(device.clone(), vulkano::pipeline::GraphicsPipelineParams { vertex_input: vulkano::pipeline::vertex::SingleBufferDefinition::new(), vertex_shader: vs.main_entry_point(), input_assembly: vulkano::pipeline::input_assembly::InputAssembly { @@ -157,21 +157,16 @@ fn main() { })); let framebuffers = images.iter().map(|image| { - let attachments = renderpass.desc().start_attachments() - .color(image.clone()); - let dimensions = [image.dimensions()[0], image.dimensions()[1], 1]; - - vulkano::framebuffer::Framebuffer::new(renderpass.clone(), dimensions, attachments).unwrap() + Arc::new(vulkano::framebuffer::Framebuffer::start(renderpass.clone()) + .add(image.clone()).unwrap().build().unwrap()) }).collect::>(); - let mut submissions: Vec> = Vec::new(); + let mut previous_frame_end = Box::new(vulkano::sync::now(device.clone())) as Box; loop { - while submissions.len() >= 4 { - submissions.remove(0); - } + previous_frame_end.cleanup_finished(); - let (image_num, future) = swapchain.acquire_next_image(Duration::new(10, 0)).unwrap(); + let (image_num, future) = vulkano::swapchain::acquire_next_image(swapchain.clone(), Duration::new(10, 0)).unwrap(); let cb = vulkano::command_buffer::AutoCommandBufferBuilder::new(device.clone(), queue.family()) .unwrap() @@ -180,18 +175,17 @@ fn main() { //.clear_color_image(&texture, [0.0, 1.0, 0.0, 1.0]) .begin_render_pass( framebuffers[image_num].clone(), false, - renderpass.desc().start_clear_values() - .color([0.0, 0.0, 1.0, 1.0])).unwrap() + vec![[0.0, 0.0, 1.0, 1.0].into()]).unwrap() .draw(pipeline.clone(), vulkano::command_buffer::DynamicState::none(), vertex_buffer.clone(), set.clone(), ()).unwrap() .end_render_pass().unwrap() .build().unwrap(); - let future = future + let future = previous_frame_end.join(future) .then_execute(queue.clone(), cb).unwrap() .then_swapchain_present(queue.clone(), swapchain.clone(), image_num) .then_signal_fence_and_flush().unwrap(); - submissions.push(Box::new(future) as Box<_>); + previous_frame_end = Box::new(future) as Box<_>; let mut done = false; events_loop.poll_events(|ev| { diff --git a/examples/src/bin/teapot.rs b/examples/src/bin/teapot.rs index c42d3521..82096518 100644 --- a/examples/src/bin/teapot.rs +++ b/examples/src/bin/teapot.rs @@ -38,9 +38,9 @@ fn main() { println!("Using device: {} (type: {:?})", physical.name(), physical.ty()); let events_loop = winit::EventsLoop::new(); - let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, &instance).unwrap(); + let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); - let queue = physical.queue_families().find(|q| q.supports_graphics() && + let queue = physical.queue_families().find(|&q| q.supports_graphics() && window.surface().is_supported(q).unwrap_or(false)) .expect("couldn't find a graphical queue family"); @@ -55,32 +55,32 @@ fn main() { let queue = queues.next().unwrap(); let (swapchain, images) = { - let caps = window.surface().get_capabilities(&physical).expect("failed to get surface capabilities"); + let caps = window.surface().capabilities(physical).expect("failed to get surface capabilities"); let dimensions = caps.current_extent.unwrap_or([1280, 1024]); let present = caps.present_modes.iter().next().unwrap(); let usage = caps.supported_usage_flags; let format = caps.supported_formats[0].0; - vulkano::swapchain::Swapchain::new(&device, &window.surface(), caps.min_image_count, format, dimensions, 1, - &usage, &queue, vulkano::swapchain::SurfaceTransform::Identity, + vulkano::swapchain::Swapchain::new(device.clone(), window.surface().clone(), caps.min_image_count, format, dimensions, 1, + usage, &queue, vulkano::swapchain::SurfaceTransform::Identity, vulkano::swapchain::CompositeAlpha::Opaque, present, true, None).expect("failed to create swapchain") }; - let depth_buffer = vulkano::image::attachment::AttachmentImage::transient(&device, images[0].dimensions(), vulkano::format::D16Unorm).unwrap().access(); + let depth_buffer = vulkano::image::attachment::AttachmentImage::transient(device.clone(), images[0].dimensions(), vulkano::format::D16Unorm).unwrap(); let vertex_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer - ::from_iter(&device, &vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::VERTICES.iter().cloned()) + ::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::VERTICES.iter().cloned()) .expect("failed to create buffer"); let normals_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer - ::from_iter(&device, &vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::NORMALS.iter().cloned()) + ::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::NORMALS.iter().cloned()) .expect("failed to create buffer"); let index_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer - ::from_iter(&device, &vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::INDICES.iter().cloned()) + ::from_iter(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()), examples::INDICES.iter().cloned()) .expect("failed to create buffer"); // note: this teapot was meant for OpenGL where the origin is at the lower left @@ -90,7 +90,7 @@ fn main() { let scale = cgmath::Matrix4::from_scale(0.01); let uniform_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer:: - ::from_data(&device, &vulkano::buffer::BufferUsage::all(), Some(queue.family()), + ::from_data(device.clone(), vulkano::buffer::BufferUsage::all(), Some(queue.family()), vs::ty::Data { world : as cgmath::SquareMatrix>::identity().into(), view : (view * scale).into(), @@ -107,13 +107,13 @@ fn main() { color: { load: Clear, store: Store, - format: images[0].format(), + format: swapchain.format(), samples: 1, }, depth: { load: Clear, store: DontCare, - format: vulkano::image::ImageAccess::format(&depth_buffer), + format: vulkano::format::Format::D16Unorm, samples: 1, } }, @@ -124,7 +124,7 @@ fn main() { ).unwrap() ); - let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::new(&device, vulkano::pipeline::GraphicsPipelineParams { + let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::new(device.clone(), vulkano::pipeline::GraphicsPipelineParams { vertex_input: vulkano::pipeline::vertex::TwoBuffersDefinition::new(), vertex_shader: vs.main_entry_point(), input_assembly: vulkano::pipeline::input_assembly::InputAssembly::triangle_list(), @@ -153,20 +153,17 @@ fn main() { })); let framebuffers = images.iter().map(|image| { - let attachments = renderpass.desc().start_attachments() - .color(image.clone()).depth(depth_buffer.clone()); - let dimensions = [image.dimensions()[0], image.dimensions()[1], 1]; - - vulkano::framebuffer::Framebuffer::new(renderpass.clone(), dimensions, attachments).unwrap() + Arc::new(vulkano::framebuffer::Framebuffer::start(renderpass.clone()) + .add(image.clone()).unwrap() + .add(depth_buffer.clone()).unwrap() + .build().unwrap()) }).collect::>(); - let mut submissions: Vec> = Vec::new(); + let mut previous_frame = Box::new(vulkano::sync::now(device.clone())) as Box; loop { - while submissions.len() >= 4 { - submissions.remove(0); - } + previous_frame.cleanup_finished(); { // aquiring write lock for the uniform buffer @@ -179,13 +176,15 @@ fn main() { buffer_content.world = cgmath::Matrix4::from(rotation).into(); } - let (image_num, future) = swapchain.acquire_next_image(std::time::Duration::new(1, 0)).unwrap(); + let (image_num, acquire_future) = vulkano::swapchain::acquire_next_image(swapchain.clone(), std::time::Duration::new(1, 0)).unwrap(); let command_buffer = vulkano::command_buffer::AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap() .begin_render_pass( framebuffers[image_num].clone(), false, - renderpass.desc().start_clear_values() - .color([0.0, 0.0, 1.0, 1.0]).depth((1f32))).unwrap() + vec![ + [0.0, 0.0, 1.0, 1.0].into(), + 1f32.into() + ]).unwrap() .draw_indexed( pipeline.clone(), vulkano::command_buffer::DynamicState::none(), (vertex_buffer.clone(), normals_buffer.clone()), @@ -193,11 +192,11 @@ fn main() { .end_render_pass().unwrap() .build().unwrap(); - let future = future + let future = previous_frame.join(acquire_future) .then_execute(queue.clone(), command_buffer).unwrap() .then_swapchain_present(queue.clone(), swapchain.clone(), image_num) .then_signal_fence_and_flush().unwrap(); - submissions.push(Box::new(future) as Box<_>); + previous_frame = Box::new(future) as Box<_>; let mut done = false; events_loop.poll_events(|ev| { diff --git a/examples/src/bin/triangle.rs b/examples/src/bin/triangle.rs index 8dcdc14f..24e29a86 100644 --- a/examples/src/bin/triangle.rs +++ b/examples/src/bin/triangle.rs @@ -53,8 +53,10 @@ use vulkano::pipeline::vertex::SingleBufferDefinition; use vulkano::pipeline::viewport::ViewportsState; use vulkano::pipeline::viewport::Viewport; use vulkano::pipeline::viewport::Scissor; +use vulkano::swapchain; use vulkano::swapchain::SurfaceTransform; use vulkano::swapchain::Swapchain; +use vulkano::sync::now; use vulkano::sync::GpuFuture; use std::sync::Arc; @@ -105,7 +107,7 @@ fn main() { // This returns a `vulkano_win::Window` object that contains both a cross-platform winit // window and a cross-platform Vulkan surface that represents the surface of the window. let events_loop = winit::EventsLoop::new(); - let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, &instance).unwrap(); + let window = winit::WindowBuilder::new().build_vk_surface(&events_loop, instance.clone()).unwrap(); // The next step is to choose which GPU queue will execute our draw commands. // @@ -117,7 +119,7 @@ fn main() { // queue to handle data transfers in parallel. In this example we only use one queue. // // We have to choose which queues to use early on, because we will need this info very soon. - let queue = physical.queue_families().find(|q| { + let queue = physical.queue_families().find(|&q| { // We take the first queue that supports drawing to our window. q.supports_graphics() && window.surface().is_supported(q).unwrap_or(false) }).expect("couldn't find a graphical queue family"); @@ -162,7 +164,7 @@ fn main() { let (swapchain, images) = { // Querying the capabilities of the surface. When we create the swapchain we can only // pass values that are allowed by the capabilities. - let caps = window.surface().get_capabilities(&physical) + let caps = window.surface().capabilities(physical) .expect("failed to get surface capabilities"); // We choose the dimensions of the swapchain to match the current dimensions of the window. @@ -183,8 +185,8 @@ fn main() { let format = caps.supported_formats[0].0; // Please take a look at the docs for the meaning of the parameters we didn't mention. - Swapchain::new(&device, &window.surface(), caps.min_image_count, format, dimensions, 1, - &caps.supported_usage_flags, &queue, SurfaceTransform::Identity, alpha, + Swapchain::new(device.clone(), window.surface().clone(), caps.min_image_count, format, dimensions, 1, + caps.supported_usage_flags, &queue, SurfaceTransform::Identity, alpha, present, true, None).expect("failed to create swapchain") }; @@ -194,7 +196,7 @@ fn main() { struct Vertex { position: [f32; 2] } impl_vertex!(Vertex, position); - CpuAccessibleBuffer::from_iter(&device, &BufferUsage::all(), Some(queue.family()), [ + CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), Some(queue.family()), [ Vertex { position: [-0.5, -0.25] }, Vertex { position: [0.0, 0.5] }, Vertex { position: [0.25, -0.1] } @@ -244,7 +246,7 @@ fn main() { // of your structs that implements the `FormatDesc` trait). Here we use the // generic `vulkano::format::Format` enum because we don't know the format in // advance. - format: images[0].format(), + format: swapchain.format(), // TODO: samples: 1, } @@ -259,7 +261,7 @@ fn main() { // Before we draw we have to create what is called a pipeline. This is similar to an OpenGL // program, but much more specific. - let pipeline = Arc::new(GraphicsPipeline::new(&device, GraphicsPipelineParams { + let pipeline = Arc::new(GraphicsPipeline::new(device.clone(), GraphicsPipelineParams { // We need to indicate the layout of the vertices. // The type `SingleBufferDefinition` actually contains a template parameter corresponding // to the type of each vertex. But in this code it is automatically inferred. @@ -323,22 +325,9 @@ fn main() { // Since we need to draw to multiple images, we are going to create a different framebuffer for // each image. let framebuffers = images.iter().map(|image| { - // When we create the framebuffer we need to pass the actual list of images for the - // framebuffer's attachments. - // - // The type of data that corresponds to this list depends on the way you created the - // render pass. With the `single_pass_renderpass!` macro you need to call - // `.desc().start_attachments()`. The returned object will have a method whose name is the - // name of the first attachment. When called, it returns an object that will have a method - // whose name is the name of the second attachment. And so on. Only the object returned - // by the method of the last attachment can be passed to `Framebuffer::new`. - let attachments = render_pass.desc().start_attachments().color(image.clone()); - - // Actually creating the framebuffer. Note that we have to pass the dimensions of the - // framebuffer. These dimensions must be inferior or equal to the intersection of the - // dimensions of all the attachments. - let dimensions = [image.dimensions()[0], image.dimensions()[1], 1]; - Framebuffer::new(render_pass.clone(), dimensions, attachments).unwrap() + Arc::new(Framebuffer::start(render_pass.clone()) + .add(image.clone()).unwrap() + .build().unwrap()) }).collect::>(); // Initialization is finally finished! @@ -348,15 +337,15 @@ fn main() { // they are in use by the GPU. // // Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid - // that, we store them in a `Vec` and clean them from time to time. - let mut submissions: Vec> = Vec::new(); + // that, we store the submission of the previous frame here. + let mut previous_frame_end = Box::new(now(device.clone())) as Box; loop { - // Clearing the old submissions by keeping alive only the ones which probably aren't - // finished. - while submissions.len() >= 4 { - submissions.remove(0); - } + // It is important to call this function from time to time, otherwise resources will keep + // accumulating and you will eventually reach an out of memory error. + // Calling this function polls various fences in order to determine what the GPU has + // already processed, and frees the resources that are no longer needed. + previous_frame_end.cleanup_finished(); // Before we can draw on the output, we have to *acquire* an image from the swapchain. If // no image is available (which happens if you submit draw commands too quickly), then the @@ -365,7 +354,8 @@ fn main() { // // This function can block if no image is available. The parameter is a timeout after // which the function call will return an error. - let (image_num, future) = swapchain.acquire_next_image(Duration::new(1, 0)).unwrap(); + let (image_num, acquire_future) = swapchain::acquire_next_image(swapchain.clone(), + Duration::new(1, 0)).unwrap(); // In order to draw, we have to build a *command buffer*. The command buffer object holds // the list of commands that are going to be executed. @@ -385,7 +375,7 @@ fn main() { // is similar to the list of attachments when building the framebuffers, except that // only the attachments that use `load: Clear` appear in the list. .begin_render_pass(framebuffers[image_num].clone(), false, - render_pass.desc().start_clear_values().color([0.0, 0.0, 1.0, 1.0])) + vec![[0.0, 0.0, 1.0, 1.0].into()]) .unwrap() // We are now inside the first subpass of the render pass. We add a draw command. @@ -404,7 +394,7 @@ fn main() { // Finish building the command buffer by calling `build`. .build().unwrap(); - let future = future + let future = previous_frame_end.join(acquire_future) .then_execute(queue.clone(), command_buffer).unwrap() // The color output is now expected to contain our triangle. But in order to show it on @@ -415,7 +405,7 @@ fn main() { // the GPU has finished executing the command buffer that draws the triangle. .then_swapchain_present(queue.clone(), swapchain.clone(), image_num) .then_signal_fence_and_flush().unwrap(); - submissions.push(Box::new(future) as Box<_>); + previous_frame_end = Box::new(future) as Box<_>; // Note that in more complex programs it is likely that one of `acquire_next_image`, // `command_buffer::submit`, or `present` will block for some time. This happens when the diff --git a/vulkano-win/src/lib.rs b/vulkano-win/src/lib.rs index 0469795a..b3b701fb 100644 --- a/vulkano-win/src/lib.rs +++ b/vulkano-win/src/lib.rs @@ -52,11 +52,11 @@ pub fn required_extensions() -> InstanceExtensions { } pub trait VkSurfaceBuild { - fn build_vk_surface(self, events_loop: &EventsLoop, instance: &Arc) -> Result; + fn build_vk_surface(self, events_loop: &EventsLoop, instance: Arc) -> Result; } impl VkSurfaceBuild for WindowBuilder { - fn build_vk_surface(self, events_loop: &EventsLoop, instance: &Arc) -> Result { + fn build_vk_surface(self, events_loop: &EventsLoop, instance: Arc) -> Result { let window = try!(self.build(events_loop)); let surface = try!(unsafe { winit_to_surface(instance, &window) }); @@ -133,7 +133,7 @@ impl From for CreationError { } #[cfg(target_os = "android")] -unsafe fn winit_to_surface(instance: &Arc, +unsafe fn winit_to_surface(instance: Arc, win: &winit::Window) -> Result, SurfaceCreationError> { use winit::os::android::WindowExt; @@ -141,7 +141,7 @@ unsafe fn winit_to_surface(instance: &Arc, } #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] -unsafe fn winit_to_surface(instance: &Arc, +unsafe fn winit_to_surface(instance: Arc, win: &winit::Window) -> Result, SurfaceCreationError> { use winit::os::unix::WindowExt; @@ -164,7 +164,7 @@ unsafe fn winit_to_surface(instance: &Arc, } #[cfg(target_os = "windows")] -unsafe fn winit_to_surface(instance: &Arc, +unsafe fn winit_to_surface(instance: Arc, win: &winit::Window) -> Result, SurfaceCreationError> { use winit::os::windows::WindowExt; @@ -174,7 +174,7 @@ unsafe fn winit_to_surface(instance: &Arc, } #[cfg(target_os = "macos")] -unsafe fn winit_to_surface(instance: &Arc, win: &winit::Window) +unsafe fn winit_to_surface(instance: Arc, win: &winit::Window) -> Result, SurfaceCreationError> { use winit::os::macos::WindowExt; diff --git a/vulkano/src/buffer/cpu_access.rs b/vulkano/src/buffer/cpu_access.rs index d3f67411..66503313 100644 --- a/vulkano/src/buffer/cpu_access.rs +++ b/vulkano/src/buffer/cpu_access.rs @@ -31,7 +31,7 @@ use smallvec::SmallVec; use buffer::sys::BufferCreationError; use buffer::sys::SparseLevel; use buffer::sys::UnsafeBuffer; -use buffer::sys::Usage; +use buffer::BufferUsage; use buffer::traits::BufferAccess; use buffer::traits::BufferInner; use buffer::traits::Buffer; @@ -47,6 +47,7 @@ use memory::pool::AllocLayout; use memory::pool::MemoryPool; use memory::pool::MemoryPoolAlloc; use memory::pool::StdMemoryPool; +use sync::AccessError; use sync::Sharing; use sync::AccessFlagBits; use sync::PipelineStages; @@ -77,7 +78,7 @@ impl CpuAccessibleBuffer { /// Deprecated. Use `from_data` instead. #[deprecated] #[inline] - pub fn new<'a, I>(device: &Arc, usage: &Usage, queue_families: I) + pub fn new<'a, I>(device: Arc, usage: BufferUsage, queue_families: I) -> Result>, OomError> where I: IntoIterator> { @@ -87,7 +88,7 @@ impl CpuAccessibleBuffer { } /// Builds a new buffer with some data in it. Only allowed for sized data. - pub fn from_data<'a, I>(device: &Arc, usage: &Usage, queue_families: I, data: T) + pub fn from_data<'a, I>(device: Arc, usage: BufferUsage, queue_families: I, data: T) -> Result>, OomError> where I: IntoIterator>, T: Content + 'static, @@ -112,7 +113,7 @@ impl CpuAccessibleBuffer { /// Builds a new uninitialized buffer. Only allowed for sized data. #[inline] - pub unsafe fn uninitialized<'a, I>(device: &Arc, usage: &Usage, queue_families: I) + pub unsafe fn uninitialized<'a, I>(device: Arc, usage: BufferUsage, queue_families: I) -> Result>, OomError> where I: IntoIterator> { @@ -123,7 +124,7 @@ impl CpuAccessibleBuffer { impl CpuAccessibleBuffer<[T]> { /// Builds a new buffer that contains an array `T`. The initial data comes from an iterator /// that produces that list of Ts. - pub fn from_iter<'a, I, Q>(device: &Arc, usage: &Usage, queue_families: Q, data: I) + pub fn from_iter<'a, I, Q>(device: Arc, usage: BufferUsage, queue_families: Q, data: I) -> Result>, OomError> where I: ExactSizeIterator, T: Content + 'static, @@ -154,7 +155,7 @@ impl CpuAccessibleBuffer<[T]> { // TODO: remove #[inline] #[deprecated] - pub fn array<'a, I>(device: &Arc, len: usize, usage: &Usage, queue_families: I) + pub fn array<'a, I>(device: Arc, len: usize, usage: BufferUsage, queue_families: I) -> Result>, OomError> where I: IntoIterator> { @@ -165,7 +166,7 @@ impl CpuAccessibleBuffer<[T]> { /// Builds a new buffer. Can be used for arrays. #[inline] - pub unsafe fn uninitialized_array<'a, I>(device: &Arc, len: usize, usage: &Usage, + pub unsafe fn uninitialized_array<'a, I>(device: Arc, len: usize, usage: BufferUsage, queue_families: I) -> Result>, OomError> where I: IntoIterator> @@ -181,7 +182,7 @@ impl CpuAccessibleBuffer { /// /// You must ensure that the size that you pass is correct for `T`. /// - pub unsafe fn raw<'a, I>(device: &Arc, size: usize, usage: &Usage, queue_families: I) + pub unsafe fn raw<'a, I>(device: Arc, size: usize, usage: BufferUsage, queue_families: I) -> Result>, OomError> where I: IntoIterator> { @@ -195,7 +196,7 @@ impl CpuAccessibleBuffer { Sharing::Exclusive }; - match UnsafeBuffer::new(device, size, &usage, sharing, SparseLevel::none()) { + match UnsafeBuffer::new(device.clone(), size, usage, sharing, SparseLevel::none()) { Ok(b) => b, Err(BufferCreationError::OomError(err)) => return Err(err), Err(_) => unreachable!() // We don't use sparse binding, therefore the other @@ -208,7 +209,7 @@ impl CpuAccessibleBuffer { .filter(|t| t.is_host_visible()) .next().unwrap(); // Vk specs guarantee that this can't fail - let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty, + let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty, mem_reqs.size, mem_reqs.alignment, AllocLayout::Linear)); debug_assert!((mem.offset() % mem_reqs.alignment) == 0); debug_assert!(mem.mapped_memory().is_some()); @@ -325,8 +326,8 @@ unsafe impl BufferAccess for CpuAccessibleBuffer } #[inline] - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool { - true // FIXME: + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> { + Ok(()) // FIXME: } #[inline] diff --git a/vulkano/src/buffer/cpu_pool.rs b/vulkano/src/buffer/cpu_pool.rs index 89e480b0..0fe4309f 100644 --- a/vulkano/src/buffer/cpu_pool.rs +++ b/vulkano/src/buffer/cpu_pool.rs @@ -21,7 +21,7 @@ use smallvec::SmallVec; use buffer::sys::BufferCreationError; use buffer::sys::SparseLevel; use buffer::sys::UnsafeBuffer; -use buffer::sys::Usage; +use buffer::BufferUsage; use buffer::traits::BufferAccess; use buffer::traits::BufferInner; use buffer::traits::Buffer; @@ -35,6 +35,7 @@ use memory::pool::AllocLayout; use memory::pool::MemoryPool; use memory::pool::MemoryPoolAlloc; use memory::pool::StdMemoryPool; +use sync::AccessError; use sync::Sharing; use OomError; @@ -43,7 +44,7 @@ use OomError; /// /// This buffer is especially suitable when you want to upload or download some data at each frame. /// -/// # Usage +/// # BufferUsage /// /// A `CpuBufferPool` is a bit similar to a `Vec`. You start by creating an empty pool, then you /// grab elements from the pool and use them, and if the pool is full it will automatically grow @@ -71,7 +72,7 @@ pub struct CpuBufferPool> where A: MemoryPool one_size: usize, // Buffer usage. - usage: Usage, + usage: BufferUsage, // Queue families allowed to access this buffer. queue_families: SmallVec<[u32; 4]>, @@ -131,7 +132,7 @@ pub struct CpuBufferPoolSubbuffer where A: MemoryPool { impl CpuBufferPool { #[inline] - pub fn new<'a, I>(device: Arc, usage: &Usage, queue_families: I) + pub fn new<'a, I>(device: Arc, usage: BufferUsage, queue_families: I) -> CpuBufferPool where I: IntoIterator> { @@ -146,13 +147,13 @@ impl CpuBufferPool { /// family accesses. #[inline] pub fn upload(device: Arc) -> CpuBufferPool { - CpuBufferPool::new(device, &Usage::transfer_source(), iter::empty()) + CpuBufferPool::new(device, BufferUsage::transfer_source(), iter::empty()) } } impl CpuBufferPool<[T]> { #[inline] - pub fn array<'a, I>(device: Arc, len: usize, usage: &Usage, queue_families: I) + pub fn array<'a, I>(device: Arc, len: usize, usage: BufferUsage, queue_families: I) -> CpuBufferPool<[T]> where I: IntoIterator> { @@ -164,7 +165,7 @@ impl CpuBufferPool<[T]> { impl CpuBufferPool { pub unsafe fn raw<'a, I>(device: Arc, one_size: usize, - usage: &Usage, queue_families: I) -> CpuBufferPool + usage: BufferUsage, queue_families: I) -> CpuBufferPool where I: IntoIterator> { let queue_families = queue_families.into_iter().map(|f| f.id()) @@ -265,7 +266,7 @@ impl CpuBufferPool where A: MemoryPool { None => return Err(OomError::OutOfDeviceMemory), }; - match UnsafeBuffer::new(&self.device, total_size, &self.usage, sharing, SparseLevel::none()) { + match UnsafeBuffer::new(self.device.clone(), total_size, self.usage, sharing, SparseLevel::none()) { Ok(b) => b, Err(BufferCreationError::OomError(err)) => return Err(err), Err(_) => unreachable!() // We don't use sparse binding, therefore the other @@ -436,15 +437,15 @@ unsafe impl BufferAccess for CpuBufferPoolSubbuffer } #[inline] - fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool { + fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> { let in_use = &self.buffer.subbuffers[self.subbuffer_index].num_gpu_accesses; if in_use.compare_and_swap(0, 1, Ordering::SeqCst) != 0 { - return false; + return Err(AccessError::AlreadyInUse); } let was_locked = self.gpu_locked.swap(true, Ordering::SeqCst); debug_assert!(!was_locked); - true + Ok(()) } #[inline] diff --git a/vulkano/src/buffer/device_local.rs b/vulkano/src/buffer/device_local.rs index 03a7b637..aca843c0 100644 --- a/vulkano/src/buffer/device_local.rs +++ b/vulkano/src/buffer/device_local.rs @@ -22,7 +22,7 @@ use smallvec::SmallVec; use buffer::sys::BufferCreationError; use buffer::sys::SparseLevel; use buffer::sys::UnsafeBuffer; -use buffer::sys::Usage; +use buffer::BufferUsage; use buffer::traits::BufferAccess; use buffer::traits::BufferInner; use buffer::traits::Buffer; @@ -36,6 +36,7 @@ use memory::pool::AllocLayout; use memory::pool::MemoryPool; use memory::pool::MemoryPoolAlloc; use memory::pool::StdMemoryPool; +use sync::AccessError; use sync::Sharing; use OomError; @@ -63,7 +64,7 @@ pub struct DeviceLocalBuffer> where A: MemoryP impl DeviceLocalBuffer { /// Builds a new buffer. Only allowed for sized data. #[inline] - pub fn new<'a, I>(device: &Arc, usage: &Usage, queue_families: I) + pub fn new<'a, I>(device: Arc, usage: BufferUsage, queue_families: I) -> Result>, OomError> where I: IntoIterator> { @@ -76,7 +77,7 @@ impl DeviceLocalBuffer { impl DeviceLocalBuffer<[T]> { /// Builds a new buffer. Can be used for arrays. #[inline] - pub fn array<'a, I>(device: &Arc, len: usize, usage: &Usage, queue_families: I) + pub fn array<'a, I>(device: Arc, len: usize, usage: BufferUsage, queue_families: I) -> Result>, OomError> where I: IntoIterator> { @@ -93,7 +94,7 @@ impl DeviceLocalBuffer { /// /// You must ensure that the size that you pass is correct for `T`. /// - pub unsafe fn raw<'a, I>(device: &Arc, size: usize, usage: &Usage, queue_families: I) + pub unsafe fn raw<'a, I>(device: Arc, size: usize, usage: BufferUsage, queue_families: I) -> Result>, OomError> where I: IntoIterator> { @@ -107,7 +108,7 @@ impl DeviceLocalBuffer { Sharing::Exclusive }; - match UnsafeBuffer::new(device, size, &usage, sharing, SparseLevel::none()) { + match UnsafeBuffer::new(device.clone(), size, usage, sharing, SparseLevel::none()) { Ok(b) => b, Err(BufferCreationError::OomError(err)) => return Err(err), Err(_) => unreachable!() // We don't use sparse binding, therefore the other @@ -124,7 +125,7 @@ impl DeviceLocalBuffer { device_local.chain(any).next().unwrap() }; - let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty, + let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty, mem_reqs.size, mem_reqs.alignment, AllocLayout::Linear)); debug_assert!((mem.offset() % mem_reqs.alignment) == 0); try!(buffer.bind_memory(mem.memory(), mem.offset())); @@ -204,7 +205,7 @@ unsafe impl BufferAccess for DeviceLocalBufferAccess

} #[inline] - fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool { + fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> { // FIXME: not implemented correctly /*let val = self.0.gpu_lock.fetch_add(1, Ordering::SeqCst); if val == 1 { @@ -213,7 +214,7 @@ unsafe impl BufferAccess for DeviceLocalBufferAccess

self.0.gpu_lock.fetch_sub(1, Ordering::SeqCst); false }*/ - true + Ok(()) } #[inline] diff --git a/vulkano/src/buffer/immutable.rs b/vulkano/src/buffer/immutable.rs index 79834262..dbff81e7 100644 --- a/vulkano/src/buffer/immutable.rs +++ b/vulkano/src/buffer/immutable.rs @@ -32,7 +32,7 @@ use buffer::CpuAccessibleBuffer; use buffer::sys::BufferCreationError; use buffer::sys::SparseLevel; use buffer::sys::UnsafeBuffer; -use buffer::sys::Usage; +use buffer::BufferUsage; use buffer::traits::BufferAccess; use buffer::traits::BufferInner; use buffer::traits::Buffer; @@ -54,6 +54,7 @@ use memory::pool::AllocLayout; use memory::pool::MemoryPool; use memory::pool::MemoryPoolAlloc; use memory::pool::StdMemoryPoolAlloc; +use sync::AccessError; use sync::NowFuture; use sync::Sharing; @@ -93,12 +94,12 @@ impl ImmutableBuffer { /// the initial upload operation. In order to be allowed to use the `ImmutableBuffer`, you must /// either submit your operation after this future, or execute this future and wait for it to /// be finished before submitting your own operation. - pub fn from_data<'a, I>(data: T, usage: &Usage, queue_families: I, queue: Arc) + pub fn from_data<'a, I>(data: T, usage: BufferUsage, queue_families: I, queue: Arc) -> Result<(Arc>, ImmutableBufferFromBufferFuture), OomError> where I: IntoIterator>, T: 'static + Send + Sync + Sized, { - let source = CpuAccessibleBuffer::from_data(queue.device(), &Usage::transfer_source(), + let source = CpuAccessibleBuffer::from_data(queue.device().clone(), BufferUsage::transfer_source(), iter::once(queue.family()), data)?; ImmutableBuffer::from_buffer(source, usage, queue_families, queue) } @@ -109,7 +110,7 @@ impl ImmutableBuffer { /// the initial upload operation. In order to be allowed to use the `ImmutableBuffer`, you must /// either submit your operation after this future, or execute this future and wait for it to /// be finished before submitting your own operation. - pub fn from_buffer<'a, B, I>(source: B, usage: &Usage, queue_families: I, queue: Arc) + pub fn from_buffer<'a, B, I>(source: B, usage: BufferUsage, queue_families: I, queue: Arc) -> Result<(Arc>, ImmutableBufferFromBufferFuture), OomError> where B: Buffer + TypedBuffer + DeviceOwned, // TODO: remove + DeviceOwned once Buffer requires it B::Access: 'static + Clone + Send + Sync, @@ -140,7 +141,7 @@ impl ImmutableBuffer { } /// Builds an `ImmutableBuffer` that copies its data from another buffer. - pub fn from_buffer_with_builder<'a, B, I, Cb, O>(source: B, usage: &Usage, queue_families: I, + pub fn from_buffer_with_builder<'a, B, I, Cb, O>(source: B, usage: BufferUsage, queue_families: I, builder: Cb) -> Result<(Arc>, O), ImmutableBufferFromBufferWithBuilderError> where B: Buffer + TypedBuffer + DeviceOwned, // TODO: remove + DeviceOwned once Buffer requires it @@ -150,13 +151,13 @@ impl ImmutableBuffer { { unsafe { // We automatically set `transfer_dest` to true in order to avoid annoying errors. - let actual_usage = Usage { + let actual_usage = BufferUsage { transfer_dest: true, - .. *usage + .. usage }; let (buffer, init) = ImmutableBuffer::raw(source.device().clone(), source.size(), - &actual_usage, queue_families)?; + actual_usage, queue_families)?; let builder = builder.copy_buffer(source, init)?; Ok((buffer, builder)) @@ -182,7 +183,7 @@ impl ImmutableBuffer { /// data, otherwise the content is undefined. /// #[inline] - pub unsafe fn uninitialized<'a, I>(device: Arc, usage: &Usage, queue_families: I) + pub unsafe fn uninitialized<'a, I>(device: Arc, usage: BufferUsage, queue_families: I) -> Result<(Arc>, ImmutableBufferInitialization), OomError> where I: IntoIterator> { @@ -191,13 +192,13 @@ impl ImmutableBuffer { } impl ImmutableBuffer<[T]> { - pub fn from_iter<'a, D, I>(data: D, usage: &Usage, queue_families: I, queue: Arc) + pub fn from_iter<'a, D, I>(data: D, usage: BufferUsage, queue_families: I, queue: Arc) -> Result<(Arc>, ImmutableBufferFromBufferFuture), OomError> where I: IntoIterator>, D: ExactSizeIterator, T: 'static + Send + Sync + Sized, { - let source = CpuAccessibleBuffer::from_iter(queue.device(), &Usage::transfer_source(), + let source = CpuAccessibleBuffer::from_iter(queue.device().clone(), BufferUsage::transfer_source(), iter::once(queue.family()), data)?; ImmutableBuffer::from_buffer(source, usage, queue_families, queue) } @@ -219,7 +220,7 @@ impl ImmutableBuffer<[T]> { /// data, otherwise the content is undefined. /// #[inline] - pub unsafe fn uninitialized_array<'a, I>(device: Arc, len: usize, usage: &Usage, + pub unsafe fn uninitialized_array<'a, I>(device: Arc, len: usize, usage: BufferUsage, queue_families: I) -> Result<(Arc>, ImmutableBufferInitialization<[T]>), OomError> where I: IntoIterator> @@ -245,7 +246,7 @@ impl ImmutableBuffer { /// data. /// #[inline] - pub unsafe fn raw<'a, I>(device: Arc, size: usize, usage: &Usage, queue_families: I) + pub unsafe fn raw<'a, I>(device: Arc, size: usize, usage: BufferUsage, queue_families: I) -> Result<(Arc>, ImmutableBufferInitialization), OomError> where I: IntoIterator> { @@ -255,7 +256,7 @@ impl ImmutableBuffer { // Internal implementation of `raw`. This is separated from `raw` so that it doesn't need to be // inlined. - unsafe fn raw_impl(device: Arc, size: usize, usage: &Usage, + unsafe fn raw_impl(device: Arc, size: usize, usage: BufferUsage, queue_families: SmallVec<[u32; 4]>) -> Result<(Arc>, ImmutableBufferInitialization), OomError> { @@ -266,7 +267,7 @@ impl ImmutableBuffer { Sharing::Exclusive }; - match UnsafeBuffer::new(&device, size, &usage, sharing, SparseLevel::none()) { + match UnsafeBuffer::new(device.clone(), size, usage, sharing, SparseLevel::none()) { Ok(b) => b, Err(BufferCreationError::OomError(err)) => return Err(err), Err(_) => unreachable!() // We don't use sparse binding, therefore the other @@ -355,16 +356,16 @@ unsafe impl BufferAccess for ImmutableBuffer { } #[inline] - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool { + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> { if exclusive_access { - return false; + return Err(AccessError::ExclusiveDenied); } if !self.initialized.load(Ordering::Relaxed) { - return false; + return Err(AccessError::BufferNotInitialized); } - true + Ok(()) } #[inline] @@ -402,8 +403,12 @@ unsafe impl BufferAccess for ImmutableBufferInitialization { } #[inline] - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool { - !self.used.compare_and_swap(false, true, Ordering::Relaxed) + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> { + if !self.used.compare_and_swap(false, true, Ordering::Relaxed) { + Ok(()) + } else { + Err(AccessError::AlreadyInUse) + } } #[inline] @@ -517,7 +522,7 @@ mod tests { use std::iter; use buffer::cpu_access::CpuAccessibleBuffer; use buffer::immutable::ImmutableBuffer; - use buffer::sys::Usage; + use buffer::BufferUsage; use command_buffer::AutoCommandBufferBuilder; use command_buffer::CommandBuffer; use command_buffer::CommandBufferBuilder; @@ -527,11 +532,11 @@ mod tests { fn from_data_working() { let (device, queue) = gfx_dev_and_queue!(); - let (buffer, _) = ImmutableBuffer::from_data(12u32, &Usage::all(), + let (buffer, _) = ImmutableBuffer::from_data(12u32, BufferUsage::all(), iter::once(queue.family()), queue.clone()).unwrap(); - let dest = CpuAccessibleBuffer::from_data(&device, &Usage::all(), + let dest = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), iter::once(queue.family()), 0).unwrap(); let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap() @@ -548,11 +553,11 @@ mod tests { fn from_iter_working() { let (device, queue) = gfx_dev_and_queue!(); - let (buffer, _) = ImmutableBuffer::from_iter((0 .. 512u32).map(|n| n * 2), &Usage::all(), + let (buffer, _) = ImmutableBuffer::from_iter((0 .. 512u32).map(|n| n * 2), BufferUsage::all(), iter::once(queue.family()), queue.clone()).unwrap(); - let dest = CpuAccessibleBuffer::from_iter(&device, &Usage::all(), + let dest = CpuAccessibleBuffer::from_iter(device.clone(), BufferUsage::all(), iter::once(queue.family()), (0 .. 512).map(|_| 0u32)).unwrap(); @@ -573,7 +578,7 @@ mod tests { fn writing_forbidden() { let (device, queue) = gfx_dev_and_queue!(); - let (buffer, _) = ImmutableBuffer::from_data(12u32, &Usage::all(), + let (buffer, _) = ImmutableBuffer::from_data(12u32, BufferUsage::all(), iter::once(queue.family()), queue.clone()).unwrap(); @@ -590,11 +595,11 @@ mod tests { let (device, queue) = gfx_dev_and_queue!(); let (buffer, _) = unsafe { - ImmutableBuffer::::uninitialized(device.clone(), &Usage::all(), + ImmutableBuffer::::uninitialized(device.clone(), BufferUsage::all(), iter::once(queue.family())).unwrap() }; - let src = CpuAccessibleBuffer::from_data(&device, &Usage::all(), + let src = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), iter::once(queue.family()), 0).unwrap(); let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap() @@ -609,11 +614,11 @@ mod tests { let (device, queue) = gfx_dev_and_queue!(); let (buffer, init) = unsafe { - ImmutableBuffer::::uninitialized(device.clone(), &Usage::all(), + ImmutableBuffer::::uninitialized(device.clone(), BufferUsage::all(), iter::once(queue.family())).unwrap() }; - let src = CpuAccessibleBuffer::from_data(&device, &Usage::all(), + let src = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), iter::once(queue.family()), 0).unwrap(); let _ = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap() @@ -630,11 +635,11 @@ mod tests { let (device, queue) = gfx_dev_and_queue!(); let (buffer, init) = unsafe { - ImmutableBuffer::::uninitialized(device.clone(), &Usage::all(), + ImmutableBuffer::::uninitialized(device.clone(), BufferUsage::all(), iter::once(queue.family())).unwrap() }; - let src = CpuAccessibleBuffer::from_data(&device, &Usage::all(), + let src = CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), iter::once(queue.family()), 0).unwrap(); let cb1 = AutoCommandBufferBuilder::new(device.clone(), queue.family()).unwrap() diff --git a/vulkano/src/buffer/mod.rs b/vulkano/src/buffer/mod.rs index 6aabba06..e038bb2f 100644 --- a/vulkano/src/buffer/mod.rs +++ b/vulkano/src/buffer/mod.rs @@ -68,12 +68,12 @@ pub use self::device_local::DeviceLocalBuffer; pub use self::immutable::ImmutableBuffer; pub use self::slice::BufferSlice; pub use self::sys::BufferCreationError; -pub use self::sys::Usage as BufferUsage; pub use self::traits::BufferAccess; pub use self::traits::BufferInner; pub use self::traits::Buffer; pub use self::traits::TypedBuffer; pub use self::traits::TypedBufferAccess; +pub use self::usage::BufferUsage; pub use self::view::BufferView; pub use self::view::BufferViewRef; @@ -86,3 +86,4 @@ pub mod view; mod slice; mod traits; +mod usage; diff --git a/vulkano/src/buffer/slice.rs b/vulkano/src/buffer/slice.rs index 30dc72cd..3b3cceaf 100644 --- a/vulkano/src/buffer/slice.rs +++ b/vulkano/src/buffer/slice.rs @@ -20,6 +20,7 @@ use buffer::traits::Buffer; use device::Device; use device::DeviceOwned; use device::Queue; +use sync::AccessError; /// A subpart of a buffer. /// @@ -241,7 +242,7 @@ unsafe impl BufferAccess for BufferSlice where B: BufferAcce } #[inline] - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool { + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> { self.resource.try_gpu_lock(exclusive_access, queue) } diff --git a/vulkano/src/buffer/sys.rs b/vulkano/src/buffer/sys.rs index 7eee5336..4783753b 100644 --- a/vulkano/src/buffer/sys.rs +++ b/vulkano/src/buffer/sys.rs @@ -31,6 +31,8 @@ use std::ptr; use std::sync::Arc; use smallvec::SmallVec; +use buffer::BufferUsage; +use buffer::usage::usage_to_bits; use device::Device; use device::DeviceOwned; use memory::DeviceMemory; @@ -62,14 +64,14 @@ impl UnsafeBuffer { /// Panics if `sparse.sparse` is false and `sparse.sparse_residency` or /// `sparse.sparse_aliased` is true. /// - pub unsafe fn new<'a, I>(device: &Arc, size: usize, usage: &Usage, sharing: Sharing, - sparse: SparseLevel) + pub unsafe fn new<'a, I>(device: Arc, size: usize, usage: BufferUsage, + sharing: Sharing, sparse: SparseLevel) -> Result<(UnsafeBuffer, MemoryRequirements), BufferCreationError> where I: Iterator { let vk = device.pointers(); - let usage_bits = usage.to_usage_bits(); + let usage_bits = usage_to_bits(usage); // Checking sparse features. assert!(sparse.sparse || !sparse.sparse_residency, "Can't enable sparse residency without \ @@ -301,170 +303,6 @@ impl SparseLevel { } } -/// Describes how a buffer is going to be used. This is **not** an optimization. -/// -/// If you try to use a buffer in a way that you didn't declare, a panic will happen. -/// -/// Some methods are provided to build `Usage` structs for some common situations. However -/// there is no restriction in the combination of usages that can be enabled. -#[derive(Debug, Copy, Clone)] -pub struct Usage { - pub transfer_source: bool, - pub transfer_dest: bool, - pub uniform_texel_buffer: bool, - pub storage_texel_buffer: bool, - pub uniform_buffer: bool, - pub storage_buffer: bool, - pub index_buffer: bool, - pub vertex_buffer: bool, - pub indirect_buffer: bool, -} - -impl Usage { - /// Builds a `Usage` with all values set to false. - #[inline] - pub fn none() -> Usage { - Usage { - transfer_source: false, - transfer_dest: false, - uniform_texel_buffer: false, - storage_texel_buffer: false, - uniform_buffer: false, - storage_buffer: false, - index_buffer: false, - vertex_buffer: false, - indirect_buffer: false, - } - } - - /// Builds a `Usage` with all values set to true. Can be used for quick prototyping. - #[inline] - pub fn all() -> Usage { - Usage { - transfer_source: true, - transfer_dest: true, - uniform_texel_buffer: true, - storage_texel_buffer: true, - uniform_buffer: true, - storage_buffer: true, - index_buffer: true, - vertex_buffer: true, - indirect_buffer: true, - } - } - - /// Builds a `Usage` with `transfer_source` set to true and the rest to false. - #[inline] - pub fn transfer_source() -> Usage { - Usage { - transfer_source: true, - .. Usage::none() - } - } - - /// Builds a `Usage` with `transfer_dest` set to true and the rest to false. - #[inline] - pub fn transfer_dest() -> Usage { - Usage { - transfer_dest: true, - .. Usage::none() - } - } - - /// Builds a `Usage` with `vertex_buffer` set to true and the rest to false. - #[inline] - pub fn vertex_buffer() -> Usage { - Usage { - vertex_buffer: true, - .. Usage::none() - } - } - - /// Builds a `Usage` with `vertex_buffer` and `transfer_dest` set to true and the rest to false. - #[inline] - pub fn vertex_buffer_transfer_dest() -> Usage { - Usage { - vertex_buffer: true, - transfer_dest: true, - .. Usage::none() - } - } - - /// Builds a `Usage` with `index_buffer` set to true and the rest to false. - #[inline] - pub fn index_buffer() -> Usage { - Usage { - index_buffer: true, - .. Usage::none() - } - } - - /// Builds a `Usage` with `index_buffer` and `transfer_dest` set to true and the rest to false. - #[inline] - pub fn index_buffer_transfer_dest() -> Usage { - Usage { - index_buffer: true, - transfer_dest: true, - .. Usage::none() - } - } - - /// Builds a `Usage` with `uniform_buffer` set to true and the rest to false. - #[inline] - pub fn uniform_buffer() -> Usage { - Usage { - uniform_buffer: true, - .. Usage::none() - } - } - - /// Builds a `Usage` with `uniform_buffer` and `transfer_dest` set to true and the rest - /// to false. - #[inline] - pub fn uniform_buffer_transfer_dest() -> Usage { - Usage { - uniform_buffer: true, - transfer_dest: true, - .. Usage::none() - } - } - - /// Builds a `Usage` with `indirect_buffer` set to true and the rest to false. - #[inline] - pub fn indirect_buffer() -> Usage { - Usage { - indirect_buffer: true, - .. Usage::none() - } - } - - /// Builds a `Usage` with `indirect_buffer` and `transfer_dest` set to true and the rest - /// to false. - #[inline] - pub fn indirect_buffer_transfer_dest() -> Usage { - Usage { - indirect_buffer: true, - transfer_dest: true, - .. Usage::none() - } - } - - #[inline] - fn to_usage_bits(&self) -> vk::BufferUsageFlagBits { - let mut result = 0; - if self.transfer_source { result |= vk::BUFFER_USAGE_TRANSFER_SRC_BIT; } - if self.transfer_dest { result |= vk::BUFFER_USAGE_TRANSFER_DST_BIT; } - if self.uniform_texel_buffer { result |= vk::BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; } - if self.storage_texel_buffer { result |= vk::BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; } - if self.uniform_buffer { result |= vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT; } - if self.storage_buffer { result |= vk::BUFFER_USAGE_STORAGE_BUFFER_BIT; } - if self.index_buffer { result |= vk::BUFFER_USAGE_INDEX_BUFFER_BIT; } - if self.vertex_buffer { result |= vk::BUFFER_USAGE_VERTEX_BUFFER_BIT; } - if self.indirect_buffer { result |= vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT; } - result - } -} - /// Error that can happen when creating a buffer. #[derive(Clone, Debug, PartialEq, Eq)] pub enum BufferCreationError { @@ -536,7 +374,7 @@ mod tests { use super::BufferCreationError; use super::SparseLevel; use super::UnsafeBuffer; - use super::Usage; + use super::BufferUsage; use device::Device; use device::DeviceOwned; @@ -546,7 +384,7 @@ mod tests { fn create() { let (device, _) = gfx_dev_and_queue!(); let (buf, reqs) = unsafe { - UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::>, + UnsafeBuffer::new(device.clone(), 128, BufferUsage::all(), Sharing::Exclusive::>, SparseLevel::none()) }.unwrap(); @@ -561,7 +399,7 @@ mod tests { let (device, _) = gfx_dev_and_queue!(); let sparse = SparseLevel { sparse: false, sparse_residency: true, sparse_aliased: false }; let _ = unsafe { - UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::>, + UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::>, sparse) }; } @@ -572,7 +410,7 @@ mod tests { let (device, _) = gfx_dev_and_queue!(); let sparse = SparseLevel { sparse: false, sparse_residency: false, sparse_aliased: true }; let _ = unsafe { - UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::>, + UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::>, sparse) }; } @@ -582,7 +420,7 @@ mod tests { let (device, _) = gfx_dev_and_queue!(); let sparse = SparseLevel { sparse: true, sparse_residency: false, sparse_aliased: false }; unsafe { - match UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::>, + match UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::>, sparse) { Err(BufferCreationError::SparseBindingFeatureNotEnabled) => (), @@ -596,7 +434,7 @@ mod tests { let (device, _) = gfx_dev_and_queue!(sparse_binding); let sparse = SparseLevel { sparse: true, sparse_residency: true, sparse_aliased: false }; unsafe { - match UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::>, + match UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::>, sparse) { Err(BufferCreationError::SparseResidencyBufferFeatureNotEnabled) => (), @@ -610,7 +448,7 @@ mod tests { let (device, _) = gfx_dev_and_queue!(sparse_binding); let sparse = SparseLevel { sparse: true, sparse_residency: false, sparse_aliased: true }; unsafe { - match UnsafeBuffer::new(&device, 128, &Usage::all(), Sharing::Exclusive::>, + match UnsafeBuffer::new(device, 128, BufferUsage::all(), Sharing::Exclusive::>, sparse) { Err(BufferCreationError::SparseResidencyAliasedFeatureNotEnabled) => (), diff --git a/vulkano/src/buffer/traits.rs b/vulkano/src/buffer/traits.rs index aa6d1f61..2a3ea5f4 100644 --- a/vulkano/src/buffer/traits.rs +++ b/vulkano/src/buffer/traits.rs @@ -15,6 +15,7 @@ use device::DeviceOwned; use device::Queue; use image::ImageAccess; use memory::Content; +use sync::AccessError; use SafeDeref; use VulkanObject; @@ -246,8 +247,7 @@ pub unsafe trait BufferAccess: DeviceOwned { /// The only way to know that the GPU has stopped accessing a queue is when the buffer object /// gets destroyed. Therefore you are encouraged to use temporary objects or handles (similar /// to a lock) in order to represent a GPU access. - // TODO: return Result? - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool; + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError>; /// Locks the resource for usage on the GPU. Supposes that the resource is already locked, and /// simply increases the lock by one. @@ -290,7 +290,7 @@ unsafe impl BufferAccess for T where T: SafeDeref, T::Target: BufferAccess { } #[inline] - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool { + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> { (**self).try_gpu_lock(exclusive_access, queue) } diff --git a/vulkano/src/buffer/usage.rs b/vulkano/src/buffer/usage.rs new file mode 100644 index 00000000..6e897004 --- /dev/null +++ b/vulkano/src/buffer/usage.rs @@ -0,0 +1,196 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::ops::BitOr; +use vk; + +/// Describes how a buffer is going to be used. This is **not** an optimization. +/// +/// If you try to use a buffer in a way that you didn't declare, a panic will happen. +/// +/// Some methods are provided to build `BufferUsage` structs for some common situations. However +/// there is no restriction in the combination of BufferUsages that can be enabled. +#[derive(Debug, Copy, Clone)] +pub struct BufferUsage { + pub transfer_source: bool, + pub transfer_dest: bool, + pub uniform_texel_buffer: bool, + pub storage_texel_buffer: bool, + pub uniform_buffer: bool, + pub storage_buffer: bool, + pub index_buffer: bool, + pub vertex_buffer: bool, + pub indirect_buffer: bool, +} + +impl BufferUsage { + /// Builds a `BufferUsage` with all values set to false. + #[inline] + pub fn none() -> BufferUsage { + BufferUsage { + transfer_source: false, + transfer_dest: false, + uniform_texel_buffer: false, + storage_texel_buffer: false, + uniform_buffer: false, + storage_buffer: false, + index_buffer: false, + vertex_buffer: false, + indirect_buffer: false, + } + } + + /// Builds a `BufferUsage` with all values set to true. Can be used for quick prototyping. + #[inline] + pub fn all() -> BufferUsage { + BufferUsage { + transfer_source: true, + transfer_dest: true, + uniform_texel_buffer: true, + storage_texel_buffer: true, + uniform_buffer: true, + storage_buffer: true, + index_buffer: true, + vertex_buffer: true, + indirect_buffer: true, + } + } + + /// Builds a `BufferUsage` with `transfer_source` set to true and the rest to false. + #[inline] + pub fn transfer_source() -> BufferUsage { + BufferUsage { + transfer_source: true, + .. BufferUsage::none() + } + } + + /// Builds a `BufferUsage` with `transfer_dest` set to true and the rest to false. + #[inline] + pub fn transfer_dest() -> BufferUsage { + BufferUsage { + transfer_dest: true, + .. BufferUsage::none() + } + } + + /// Builds a `BufferUsage` with `vertex_buffer` set to true and the rest to false. + #[inline] + pub fn vertex_buffer() -> BufferUsage { + BufferUsage { + vertex_buffer: true, + .. BufferUsage::none() + } + } + + /// Builds a `BufferUsage` with `vertex_buffer` and `transfer_dest` set to true and the rest + /// to false. + #[inline] + pub fn vertex_buffer_transfer_dest() -> BufferUsage { + BufferUsage { + vertex_buffer: true, + transfer_dest: true, + .. BufferUsage::none() + } + } + + /// Builds a `BufferUsage` with `index_buffer` set to true and the rest to false. + #[inline] + pub fn index_buffer() -> BufferUsage { + BufferUsage { + index_buffer: true, + .. BufferUsage::none() + } + } + + /// Builds a `BufferUsage` with `index_buffer` and `transfer_dest` set to true and the rest to false. + #[inline] + pub fn index_buffer_transfer_dest() -> BufferUsage { + BufferUsage { + index_buffer: true, + transfer_dest: true, + .. BufferUsage::none() + } + } + + /// Builds a `BufferUsage` with `uniform_buffer` set to true and the rest to false. + #[inline] + pub fn uniform_buffer() -> BufferUsage { + BufferUsage { + uniform_buffer: true, + .. BufferUsage::none() + } + } + + /// Builds a `BufferUsage` with `uniform_buffer` and `transfer_dest` set to true and the rest + /// to false. + #[inline] + pub fn uniform_buffer_transfer_dest() -> BufferUsage { + BufferUsage { + uniform_buffer: true, + transfer_dest: true, + .. BufferUsage::none() + } + } + + /// Builds a `BufferUsage` with `indirect_buffer` set to true and the rest to false. + #[inline] + pub fn indirect_buffer() -> BufferUsage { + BufferUsage { + indirect_buffer: true, + .. BufferUsage::none() + } + } + + /// Builds a `BufferUsage` with `indirect_buffer` and `transfer_dest` set to true and the rest + /// to false. + #[inline] + pub fn indirect_buffer_transfer_dest() -> BufferUsage { + BufferUsage { + indirect_buffer: true, + transfer_dest: true, + .. BufferUsage::none() + } + } +} + +impl BitOr for BufferUsage { + type Output = Self; + + #[inline] + fn bitor(self, rhs: Self) -> Self { + BufferUsage { + transfer_source: self.transfer_source || rhs.transfer_source, + transfer_dest: self.transfer_dest || rhs.transfer_dest, + uniform_texel_buffer: self.uniform_texel_buffer || rhs.uniform_texel_buffer, + storage_texel_buffer: self.storage_texel_buffer || rhs.storage_texel_buffer, + uniform_buffer: self.uniform_buffer || rhs.uniform_buffer, + storage_buffer: self.storage_buffer || rhs.storage_buffer, + index_buffer: self.index_buffer || rhs.index_buffer, + vertex_buffer: self.vertex_buffer || rhs.vertex_buffer, + indirect_buffer: self.indirect_buffer || rhs.indirect_buffer, + } + } +} + +/// Turns a `BufferUsage` into raw bits. +#[inline] +pub fn usage_to_bits(usage: BufferUsage) -> vk::BufferUsageFlagBits { + let mut result = 0; + if usage.transfer_source { result |= vk::BUFFER_USAGE_TRANSFER_SRC_BIT; } + if usage.transfer_dest { result |= vk::BUFFER_USAGE_TRANSFER_DST_BIT; } + if usage.uniform_texel_buffer { result |= vk::BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; } + if usage.storage_texel_buffer { result |= vk::BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; } + if usage.uniform_buffer { result |= vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT; } + if usage.storage_buffer { result |= vk::BUFFER_USAGE_STORAGE_BUFFER_BIT; } + if usage.index_buffer { result |= vk::BUFFER_USAGE_INDEX_BUFFER_BIT; } + if usage.vertex_buffer { result |= vk::BUFFER_USAGE_VERTEX_BUFFER_BIT; } + if usage.indirect_buffer { result |= vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT; } + result +} diff --git a/vulkano/src/buffer/view.rs b/vulkano/src/buffer/view.rs index 387b6436..82de760d 100644 --- a/vulkano/src/buffer/view.rs +++ b/vulkano/src/buffer/view.rs @@ -20,18 +20,18 @@ //! ``` //! # use std::sync::Arc; //! use vulkano::buffer::immutable::ImmutableBuffer; -//! use vulkano::buffer::sys::Usage; +//! use vulkano::buffer::BufferUsage; //! use vulkano::buffer::BufferView; //! use vulkano::format; //! //! # let device: Arc = return; //! # let queue: Arc = return; -//! let usage = Usage { +//! let usage = BufferUsage { //! storage_texel_buffer: true, -//! .. Usage::none() +//! .. BufferUsage::none() //! }; //! -//! let (buffer, _future) = ImmutableBuffer::<[u32]>::from_iter((0..128).map(|n| n), &usage, +//! let (buffer, _future) = ImmutableBuffer::<[u32]>::from_iter((0..128).map(|n| n), usage, //! Some(queue.family()), //! queue.clone()).unwrap(); //! let _view = BufferView::new(buffer, format::R32Uint).unwrap(); @@ -321,7 +321,7 @@ impl From for BufferViewCreationError { #[cfg(test)] mod tests { use buffer::BufferView; - use buffer::sys::Usage; + use buffer::BufferUsage; use buffer::view::BufferViewCreationError; use buffer::immutable::ImmutableBuffer; use format; @@ -331,12 +331,12 @@ mod tests { // `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format let (device, queue) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = BufferUsage { uniform_texel_buffer: true, - .. Usage::none() + .. BufferUsage::none() }; - let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), &usage, + let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), usage, Some(queue.family()), queue.clone()).unwrap(); let view = BufferView::new(buffer, format::R8G8B8A8Unorm).unwrap(); @@ -348,12 +348,12 @@ mod tests { // `VK_FORMAT_R8G8B8A8_UNORM` guaranteed to be a supported format let (device, queue) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = BufferUsage { storage_texel_buffer: true, - .. Usage::none() + .. BufferUsage::none() }; - let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), &usage, + let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), usage, Some(queue.family()), queue.clone()).unwrap(); let view = BufferView::new(buffer, format::R8G8B8A8Unorm).unwrap(); @@ -366,12 +366,12 @@ mod tests { // `VK_FORMAT_R32_UINT` guaranteed to be a supported format for atomics let (device, queue) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = BufferUsage { storage_texel_buffer: true, - .. Usage::none() + .. BufferUsage::none() }; - let (buffer, _) = ImmutableBuffer::<[u32]>::from_iter((0..128).map(|_| 0), &usage, + let (buffer, _) = ImmutableBuffer::<[u32]>::from_iter((0..128).map(|_| 0), usage, Some(queue.family()), queue.clone()).unwrap(); let view = BufferView::new(buffer, format::R32Uint).unwrap(); @@ -386,7 +386,7 @@ mod tests { let (device, queue) = gfx_dev_and_queue!(); let (buffer, _) = ImmutableBuffer::<[[u8; 4]]>::from_iter((0..128).map(|_| [0; 4]), - &Usage::none(), + BufferUsage::none(), Some(queue.family()), queue.clone()).unwrap(); @@ -400,14 +400,14 @@ mod tests { fn unsupported_format() { let (device, queue) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = BufferUsage { uniform_texel_buffer: true, storage_texel_buffer: true, - .. Usage::none() + .. BufferUsage::none() }; let (buffer, _) = ImmutableBuffer::<[[f64; 4]]>::from_iter((0..128).map(|_| [0.0; 4]), - &usage, Some(queue.family()), + usage, Some(queue.family()), queue.clone()).unwrap(); // TODO: what if R64G64B64A64Sfloat is supported? diff --git a/vulkano/src/command_buffer/auto.rs b/vulkano/src/command_buffer/auto.rs index 1d752d4f..1d1b9a1d 100644 --- a/vulkano/src/command_buffer/auto.rs +++ b/vulkano/src/command_buffer/auto.rs @@ -24,7 +24,7 @@ use command_buffer::pool::StandardCommandPool; use device::Device; use device::DeviceOwned; use device::Queue; -use image::Layout; +use image::ImageLayout; use image::ImageAccess; use instance::QueueFamily; use sync::AccessCheckError; @@ -108,7 +108,7 @@ unsafe impl

CommandBuffer for AutoCommandBufferBuilder

} #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { self.inner.check_image_access(image, layout, exclusive, queue) diff --git a/vulkano/src/command_buffer/cb/abstract_storage.rs b/vulkano/src/command_buffer/cb/abstract_storage.rs index e6ff3fc9..99b5dda2 100644 --- a/vulkano/src/command_buffer/cb/abstract_storage.rs +++ b/vulkano/src/command_buffer/cb/abstract_storage.rs @@ -22,7 +22,7 @@ use command_buffer::CommandBufferExecError; use device::Device; use device::DeviceOwned; use device::Queue; -use image::Layout; +use image::ImageLayout; use image::ImageAccess; use instance::QueueFamily; use sync::AccessCheckError; @@ -68,7 +68,7 @@ unsafe impl CommandBuffer for AbstractStorageLayer where I: CommandBuffer } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { self.inner.check_image_access(image, layout, exclusive, queue) diff --git a/vulkano/src/command_buffer/cb/submit_sync.rs b/vulkano/src/command_buffer/cb/submit_sync.rs index 2eef584f..9ea1e836 100644 --- a/vulkano/src/command_buffer/cb/submit_sync.rs +++ b/vulkano/src/command_buffer/cb/submit_sync.rs @@ -22,7 +22,7 @@ use command_buffer::CommandBufferBuilder; use command_buffer::CommandBufferExecError; use command_buffer::commands_raw; use framebuffer::FramebufferAbstract; -use image::Layout; +use image::ImageLayout; use image::ImageAccess; use instance::QueueFamily; use device::Device; @@ -159,8 +159,8 @@ struct ResourceEntry { final_stages: PipelineStages, final_access: AccessFlagBits, exclusive: bool, - initial_layout: Layout, - final_layout: Layout, + initial_layout: ImageLayout, + final_layout: ImageLayout, } impl SubmitSyncBuilderLayer { @@ -187,8 +187,8 @@ impl SubmitSyncBuilderLayer { final_stages: stages, final_access: access, exclusive: exclusive, - initial_layout: Layout::Undefined, - final_layout: Layout::Undefined, + initial_layout: ImageLayout::Undefined, + final_layout: ImageLayout::Undefined, }); }, @@ -198,7 +198,7 @@ impl SubmitSyncBuilderLayer { entry.final_stages = entry.final_stages | stages; entry.final_access = entry.final_access | access; entry.exclusive = entry.exclusive || exclusive; - entry.final_layout = Layout::Undefined; + entry.final_layout = ImageLayout::Undefined; }, } } @@ -250,14 +250,14 @@ impl SubmitSyncBuilderLayer { // TODO: slow for index in 0 .. FramebufferAbstract::attachments(framebuffer).len() { let key = Key::FramebufferAttachment(Box::new(framebuffer.clone()), index as u32); - let desc = framebuffer.attachment(index).expect("Wrong implementation of FramebufferAbstract trait"); + let desc = framebuffer.attachment_desc(index).expect("Wrong implementation of FramebufferAbstract trait"); let image = FramebufferAbstract::attachments(framebuffer)[index]; let initial_layout = match self.behavior { SubmitSyncBuilderLayerBehavior::Explicit => desc.initial_layout, SubmitSyncBuilderLayerBehavior::UseLayoutHint => { match desc.initial_layout { - Layout::Undefined | Layout::Preinitialized => desc.initial_layout, + ImageLayout::Undefined | ImageLayout::Preinitialized => desc.initial_layout, _ => image.parent().initial_layout_requirement(), } }, @@ -267,7 +267,7 @@ impl SubmitSyncBuilderLayer { SubmitSyncBuilderLayerBehavior::Explicit => desc.final_layout, SubmitSyncBuilderLayerBehavior::UseLayoutHint => { match desc.final_layout { - Layout::Undefined | Layout::Preinitialized => desc.final_layout, + ImageLayout::Undefined | ImageLayout::Preinitialized => desc.final_layout, _ => image.parent().final_layout_requirement(), } }, @@ -570,7 +570,7 @@ unsafe impl AddCommand> for SubmitS #[inline] fn add(mut self, command: commands_raw::CmdDrawIndirectRaw) -> Result { - self.add_buffer(command.buffer(), true, + self.add_buffer(command.buffer(), false, PipelineStages { draw_indirect: true, .. PipelineStages::none() }, AccessFlagBits { indirect_command_read: true, .. AccessFlagBits::none() }); @@ -741,7 +741,7 @@ unsafe impl CommandBuffer for SubmitSyncLayer where I: CommandBuffer { for (key, entry) in self.resources.iter() { match key { &Key::Buffer(ref buf) => { - let err = match future.check_buffer_access(&buf, entry.exclusive, queue) { + let prev_err = match future.check_buffer_access(&buf, entry.exclusive, queue) { Ok(_) => { unsafe { buf.increase_gpu_lock(); } continue; @@ -749,17 +749,16 @@ unsafe impl CommandBuffer for SubmitSyncLayer where I: CommandBuffer { Err(err) => err }; - if !buf.try_gpu_lock(entry.exclusive, queue) { - match err { - AccessCheckError::Unknown => panic!(), // TODO: use the err returned by try_gpu_lock - AccessCheckError::Denied(err) => return Err(err.into()), - } + match (buf.try_gpu_lock(entry.exclusive, queue), prev_err) { + (Ok(_), _) => (), + (Err(err), AccessCheckError::Unknown) => return Err(err.into()), + (_, AccessCheckError::Denied(err)) => return Err(err.into()), } }, &Key::Image(ref img) => { - let err = match future.check_image_access(img, entry.initial_layout, - entry.exclusive, queue) + let prev_err = match future.check_image_access(img, entry.initial_layout, + entry.exclusive, queue) { Ok(_) => { unsafe { img.increase_gpu_lock(); } @@ -768,19 +767,18 @@ unsafe impl CommandBuffer for SubmitSyncLayer where I: CommandBuffer { Err(err) => err }; - if !img.try_gpu_lock(entry.exclusive, queue) { - match err { - AccessCheckError::Unknown => panic!(), // TODO: use the err returned by try_gpu_lock - AccessCheckError::Denied(err) => return Err(err.into()), - } + match (img.try_gpu_lock(entry.exclusive, queue), prev_err) { + (Ok(_), _) => (), + (Err(err), AccessCheckError::Unknown) => return Err(err.into()), + (_, AccessCheckError::Denied(err)) => return Err(err.into()), } }, &Key::FramebufferAttachment(ref fb, idx) => { let img = fb.attachments()[idx as usize].parent(); - let err = match future.check_image_access(img, entry.initial_layout, - entry.exclusive, queue) + let prev_err = match future.check_image_access(img, entry.initial_layout, + entry.exclusive, queue) { Ok(_) => { unsafe { img.increase_gpu_lock(); } @@ -789,11 +787,12 @@ unsafe impl CommandBuffer for SubmitSyncLayer where I: CommandBuffer { Err(err) => err }; - if !img.try_gpu_lock(entry.exclusive, queue) { - match err { - AccessCheckError::Unknown => panic!(), // TODO: use the err returned by try_gpu_lock - AccessCheckError::Denied(err) => return Err(err.into()), - } + // FIXME: this is bad because dropping the submit sync layer doesn't drop the + // attachments of the framebuffer, meaning that they will stay locked + match (img.try_gpu_lock(entry.exclusive, queue), prev_err) { + (Ok(_), _) => (), + (Err(err), AccessCheckError::Unknown) => return Err(err.into()), + (_, AccessCheckError::Denied(err)) => return Err(err.into()), } }, } @@ -830,7 +829,7 @@ unsafe impl CommandBuffer for SubmitSyncLayer where I: CommandBuffer { } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { // TODO: check the queue family @@ -844,7 +843,7 @@ unsafe impl CommandBuffer for SubmitSyncLayer where I: CommandBuffer { continue; } - if layout != Layout::Undefined && value.final_layout != layout { + if layout != ImageLayout::Undefined && value.final_layout != layout { return Err(AccessCheckError::Denied(AccessError::UnexpectedImageLayout { allowed: value.final_layout, requested: layout, diff --git a/vulkano/src/command_buffer/cb/sys.rs b/vulkano/src/command_buffer/cb/sys.rs index 80d62fc2..98e9c5f6 100644 --- a/vulkano/src/command_buffer/cb/sys.rs +++ b/vulkano/src/command_buffer/cb/sys.rs @@ -28,7 +28,7 @@ use framebuffer::FramebufferAbstract; use framebuffer::RenderPass; use framebuffer::RenderPassAbstract; use framebuffer::Subpass; -use image::Layout; +use image::ImageLayout; use image::ImageAccess; use instance::QueueFamily; use sync::AccessCheckError; @@ -317,7 +317,7 @@ unsafe impl

CommandBuffer for UnsafeCommandBuffer

where P: CommandPool { } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { Err(AccessCheckError::Unknown) diff --git a/vulkano/src/command_buffer/commands_raw/fill_buffer.rs b/vulkano/src/command_buffer/commands_raw/fill_buffer.rs index da4efeee..b3348cb4 100644 --- a/vulkano/src/command_buffer/commands_raw/fill_buffer.rs +++ b/vulkano/src/command_buffer/commands_raw/fill_buffer.rs @@ -144,7 +144,7 @@ mod tests { fn basic() { let (device, queue) = gfx_dev_and_queue!(); - let buffer = CpuAccessibleBuffer::from_data(&device, &BufferUsage::transfer_dest(), + let buffer = CpuAccessibleBuffer::from_data(&device, BufferUsage::transfer_dest(), Some(queue.family()), 0u32).unwrap(); let _ = PrimaryCbBuilder::new(&device, queue.family()) diff --git a/vulkano/src/command_buffer/commands_raw/pipeline_barrier.rs b/vulkano/src/command_buffer/commands_raw/pipeline_barrier.rs index 8824e3c9..4971c64f 100644 --- a/vulkano/src/command_buffer/commands_raw/pipeline_barrier.rs +++ b/vulkano/src/command_buffer/commands_raw/pipeline_barrier.rs @@ -20,7 +20,7 @@ use command_buffer::cb::AddCommand; use command_buffer::cb::UnsafeCommandBufferBuilder; use command_buffer::pool::CommandPool; use image::ImageAccess; -use image::Layout; +use image::ImageLayout; use sync::AccessFlagBits; use sync::PipelineStages; @@ -102,8 +102,8 @@ impl<'a> CmdPipelineBarrier<'a> { self.dependency_flags = 0; } - self.src_stage_mask |= source.into(); - self.dst_stage_mask |= dest.into(); + self.src_stage_mask |= Into::::into(source); + self.dst_stage_mask |= Into::::into(dest); } /// Adds a memory barrier. This means that all the memory writes by the given source stages @@ -197,7 +197,7 @@ impl<'a> CmdPipelineBarrier<'a> { pub unsafe fn add_image_memory_barrier(&mut self, image: &'a I, mipmaps: Range, layers: Range, source_stage: PipelineStages, source_access: AccessFlagBits, dest_stage: PipelineStages, dest_access: AccessFlagBits, by_region: bool, - queue_transfer: Option<(u32, u32)>, current_layout: Layout, new_layout: Layout) + queue_transfer: Option<(u32, u32)>, current_layout: ImageLayout, new_layout: ImageLayout) where I: ImageAccess { self.add_execution_dependency(source_stage, dest_stage, by_region); diff --git a/vulkano/src/command_buffer/traits.rs b/vulkano/src/command_buffer/traits.rs index ce966c93..fcfebafd 100644 --- a/vulkano/src/command_buffer/traits.rs +++ b/vulkano/src/command_buffer/traits.rs @@ -22,7 +22,7 @@ use command_buffer::submit::SubmitCommandBufferBuilder; use device::Device; use device::DeviceOwned; use device::Queue; -use image::Layout; +use image::ImageLayout; use image::ImageAccess; use instance::QueueFamily; use sync::now; @@ -136,7 +136,7 @@ pub unsafe trait CommandBuffer: DeviceOwned { fn check_buffer_access(&self, buffer: &BufferAccess, exclusive: bool, queue: &Queue) -> Result, AccessCheckError>; - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError>; // FIXME: lots of other methods @@ -174,7 +174,7 @@ unsafe impl CommandBuffer for T where T: SafeDeref, T::Target: CommandBuffer } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { (**self).check_image_access(image, layout, exclusive, queue) @@ -284,7 +284,7 @@ unsafe impl GpuFuture for CommandBufferExecFuture } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { match self.command_buffer.check_image_access(image, layout, exclusive, queue) { diff --git a/vulkano/src/descriptor/descriptor_set/simple.rs b/vulkano/src/descriptor/descriptor_set/simple.rs index 9feaa851..02ac1611 100644 --- a/vulkano/src/descriptor/descriptor_set/simple.rs +++ b/vulkano/src/descriptor/descriptor_set/simple.rs @@ -27,7 +27,7 @@ use device::Device; use device::DeviceOwned; use image::ImageAccess; use image::ImageView; -use image::sys::Layout; +use image::ImageLayout; use sampler::Sampler; use sync::AccessFlagBits; use sync::PipelineStages; @@ -290,7 +290,7 @@ unsafe impl SimpleDescriptorSetImageExt for T num_mipmaps: 1, // FIXME: first_layer: 0, // FIXME: num_layers: 1, // FIXME: - layout: Layout::General, // FIXME: + layout: ImageLayout::General, // FIXME: stage: PipelineStages::none(), // FIXME: access: AccessFlagBits::none(), // FIXME: }) @@ -332,7 +332,7 @@ unsafe impl SimpleDescriptorSetImageExt for (T, Arc) num_mipmaps: 1, // FIXME: first_layer: 0, // FIXME: num_layers: 1, // FIXME: - layout: Layout::General, // FIXME: + layout: ImageLayout::General, // FIXME: stage: PipelineStages::none(), // FIXME: access: AccessFlagBits::none(), // FIXME: }) @@ -375,7 +375,7 @@ unsafe impl SimpleDescriptorSetImageExt for Vec<(T, Arc) num_mipmaps: 1, // FIXME: first_layer: 0, // FIXME: num_layers: 1, // FIXME: - layout: Layout::General, // FIXME: + layout: ImageLayout::General, // FIXME: stage: PipelineStages::none(), // FIXME: access: AccessFlagBits::none(), // FIXME: }); @@ -475,7 +475,7 @@ pub struct SimpleDescriptorSetImg { num_mipmaps: u32, first_layer: u32, num_layers: u32, - layout: Layout, + layout: ImageLayout, stage: PipelineStages, access: AccessFlagBits, } diff --git a/vulkano/src/descriptor/descriptor_set/sys.rs b/vulkano/src/descriptor/descriptor_set/sys.rs index 9a82dfcb..b5f15534 100644 --- a/vulkano/src/descriptor/descriptor_set/sys.rs +++ b/vulkano/src/descriptor/descriptor_set/sys.rs @@ -547,7 +547,7 @@ impl UnsafeDescriptorSet { /// command buffer contains a pointer/reference to a descriptor set, it is illegal to write /// to it. /// - pub unsafe fn write(&mut self, device: &Arc, writes: I) + pub unsafe fn write(&mut self, device: &Device, writes: I) where I: Iterator { let vk = device.pointers(); diff --git a/vulkano/src/descriptor/pipeline_layout/empty.rs b/vulkano/src/descriptor/pipeline_layout/empty.rs index b91c2c86..3db40a8b 100644 --- a/vulkano/src/descriptor/pipeline_layout/empty.rs +++ b/vulkano/src/descriptor/pipeline_layout/empty.rs @@ -23,7 +23,7 @@ use descriptor::pipeline_layout::PipelineLayoutDescPcRange; /// use vulkano::descriptor::pipeline_layout::PipelineLayoutDesc; /// /// # let device: Arc = return; -/// let pipeline_layout = EmptyPipelineDesc.build(&device).unwrap(); +/// let pipeline_layout = EmptyPipelineDesc.build(device.clone()).unwrap(); /// ``` #[derive(Debug, Copy, Clone)] pub struct EmptyPipelineDesc; diff --git a/vulkano/src/descriptor/pipeline_layout/sys.rs b/vulkano/src/descriptor/pipeline_layout/sys.rs index b79dfaee..067e539b 100644 --- a/vulkano/src/descriptor/pipeline_layout/sys.rs +++ b/vulkano/src/descriptor/pipeline_layout/sys.rs @@ -48,7 +48,7 @@ impl PipelineLayout where L: PipelineLayoutDesc { /// - Panics if one of the layout returned by `provided_set_layout()` belongs to a different /// device than the one passed as parameter. #[inline] - pub fn new(device: &Arc, desc: L) + pub fn new(device: Arc, desc: L) -> Result, PipelineLayoutCreationError> { let vk = device.pointers(); diff --git a/vulkano/src/descriptor/pipeline_layout/traits.rs b/vulkano/src/descriptor/pipeline_layout/traits.rs index c286ed98..c12e8201 100644 --- a/vulkano/src/descriptor/pipeline_layout/traits.rs +++ b/vulkano/src/descriptor/pipeline_layout/traits.rs @@ -99,7 +99,7 @@ pub unsafe trait PipelineLayoutDesc { /// /// > **Note**: This is just a shortcut for `PipelineLayout::new`. #[inline] - fn build(self, device: &Arc) + fn build(self, device: Arc) -> Result, PipelineLayoutCreationError> where Self: Sized { diff --git a/vulkano/src/device.rs b/vulkano/src/device.rs index e977da4b..8d8a42a0 100644 --- a/vulkano/src/device.rs +++ b/vulkano/src/device.rs @@ -299,29 +299,20 @@ impl Device { Ok((device, output_queues)) } - /// See the docs of wait(). - // FIXME: must synchronize all queuees - #[inline] - pub fn wait_raw(&self) -> Result<(), OomError> { - unsafe { - try!(check_errors(self.vk.DeviceWaitIdle(self.device))); - Ok(()) - } - } - /// Waits until all work on this device has finished. You should never need to call /// this function, but it can be useful for debugging or benchmarking purposes. /// - /// This is the Vulkan equivalent of `glFinish`. + /// > **Note**: This is the Vulkan equivalent of OpenGL's `glFinish`. /// - /// # Panic + /// # Safety /// - /// - Panics if the device or host ran out of memory. + /// This function is not thread-safe. You must not submit anything to any of the queue + /// of the device (either explicitely or implicitely, for example with a future's destructor) + /// while this function is waiting. /// - // FIXME: must synchronize all queuees - #[inline] - pub fn wait(&self) { - self.wait_raw().unwrap(); + pub unsafe fn wait(&self) -> Result<(), OomError> { + try!(check_errors(self.vk.DeviceWaitIdle(self.device))); + Ok(()) } /// Returns the instance used to create this device. @@ -357,7 +348,7 @@ impl Device { } // The weak pointer is empty, so we create the pool. - let new_pool = StdMemoryPool::new(me); + let new_pool = StdMemoryPool::new(me.clone()); *pool = Arc::downgrade(&new_pool); new_pool } diff --git a/vulkano/src/framebuffer/attachments_list.rs b/vulkano/src/framebuffer/attachments_list.rs index 2c49ea7e..1e76aea7 100644 --- a/vulkano/src/framebuffer/attachments_list.rs +++ b/vulkano/src/framebuffer/attachments_list.rs @@ -7,43 +7,20 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use std::cmp; use std::sync::Arc; use SafeDeref; use image::ImageViewAccess; -use image::sys::UnsafeImageView; //use sync::AccessFlagBits; //use sync::PipelineStages; /// A list of attachments. // TODO: rework this trait pub unsafe trait AttachmentsList { - /// Returns the image views of this list. - // TODO: better return type - fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView>; - - /// Returns the dimensions of the intersection of the views. Returns `None` if the list is - /// empty. - /// - /// For example if one view is 256x256x2 and another one is 128x512x3, then this function - /// should return 128x256x2. - fn intersection_dimensions(&self) -> Option<[u32; 3]>; - // TODO: meh for API fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess>; } unsafe impl AttachmentsList for T where T: SafeDeref, T::Target: AttachmentsList { - #[inline] - fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView> { - (**self).raw_image_view_handles() - } - - #[inline] - fn intersection_dimensions(&self) -> Option<[u32; 3]> { - (**self).intersection_dimensions() - } - #[inline] fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> { (**self).as_image_view_accesses() @@ -51,16 +28,6 @@ unsafe impl AttachmentsList for T where T: SafeDeref, T::Target: AttachmentsL } unsafe impl AttachmentsList for () { - #[inline] - fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView> { - vec![] - } - - #[inline] - fn intersection_dimensions(&self) -> Option<[u32; 3]> { - None - } - #[inline] fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> { vec![] @@ -68,106 +35,19 @@ unsafe impl AttachmentsList for () { } unsafe impl AttachmentsList for Vec> { - #[inline] - fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView> { - self.iter().map(|img| img.inner()).collect() - } - - #[inline] - fn intersection_dimensions(&self) -> Option<[u32; 3]> { - let mut dims = None; - - for view in self.iter() { - debug_assert_eq!(view.dimensions().depth(), 1); - - match dims { - None => { - dims = Some([ - view.dimensions().width(), - view.dimensions().height(), - view.dimensions().array_layers() - ]); - }, - Some(ref mut d) => { - d[0] = cmp::min(view.dimensions().width(), d[0]); - d[1] = cmp::min(view.dimensions().height(), d[1]); - d[2] = cmp::min(view.dimensions().array_layers(), d[2]); - }, - } - } - - dims - } - #[inline] fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> { self.iter().map(|p| &**p as &ImageViewAccess).collect() } } -macro_rules! impl_into_atch_list { - ($first:ident $(, $rest:ident)*) => ( - unsafe impl<$first $(, $rest)*> AttachmentsList for ($first, $($rest),*) - where $first: ImageViewAccess, - $($rest: ImageViewAccess,)* - { - #[inline] - #[allow(non_snake_case)] - fn raw_image_view_handles(&self) -> Vec<&UnsafeImageView> { - let &(ref $first, $(ref $rest,)*) = self; - - vec![ - &$first.inner(), - $( - &$rest.inner(), - )* - ] - } - - #[inline] - #[allow(non_snake_case)] - fn intersection_dimensions(&self) -> Option<[u32; 3]> { - let &(ref $first, $(ref $rest,)*) = self; - - let dims = { - let d = $first.dimensions(); - debug_assert_eq!(d.depth(), 1); - [d.width(), d.height(), d.array_layers()] - }; - - $( - let dims = { - let d = $rest.dimensions(); - debug_assert_eq!(d.depth(), 1); - [ - cmp::min(d.width(), dims[0]), - cmp::min(d.height(), dims[1]), - cmp::min(d.array_layers(), dims[2]) - ] - }; - )* - - Some(dims) - } - - #[inline] - #[allow(non_snake_case)] - fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> { - let &(ref $first, $(ref $rest,)*) = self; - - vec![ - &*$first, - $( - &*$rest, - )* - ] - } - } - - impl_into_atch_list!($($rest),*); - ); - - () => (); +unsafe impl AttachmentsList for (A, B) + where A: AttachmentsList, B: ImageViewAccess +{ + #[inline] + fn as_image_view_accesses(&self) -> Vec<&ImageViewAccess> { + let mut list = self.0.as_image_view_accesses(); + list.push(&self.1); + list + } } - -impl_into_atch_list!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); diff --git a/vulkano/src/framebuffer/compat_atch.rs b/vulkano/src/framebuffer/compat_atch.rs new file mode 100644 index 00000000..21e5743a --- /dev/null +++ b/vulkano/src/framebuffer/compat_atch.rs @@ -0,0 +1,232 @@ +// Copyright (c) 2017 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +//! This module contains the `ensure_image_view_compatible` function, which verifies whether +//! an image view can be used as a render pass attachment. + +use std::error; +use std::fmt; +use format::Format; +use framebuffer::RenderPassDesc; +use image::ImageViewAccess; + +/// Checks whether the given image view is allowed to be the nth attachment of the given render +/// pass. +/// +/// # Panic +/// +/// Panicks if the attachment number is out of range. +// TODO: add a specializable trait instead, that uses this function +// TODO: ImageView instead of ImageViewAccess? +pub fn ensure_image_view_compatible(render_pass: &Rp, attachment_num: usize, image: &I) + -> Result<(), IncompatibleRenderPassAttachmentError> + where Rp: ?Sized + RenderPassDesc, + I: ?Sized + ImageViewAccess +{ + let attachment_desc = render_pass.attachment_desc(attachment_num) + .expect("Attachment num out of range"); + + if image.format() != attachment_desc.format { + return Err(IncompatibleRenderPassAttachmentError::FormatMismatch { + expected: attachment_desc.format, + obtained: image.format(), + }); + } + + if image.samples() != attachment_desc.samples { + return Err(IncompatibleRenderPassAttachmentError::SamplesMismatch { + expected: attachment_desc.samples, + obtained: image.samples(), + }); + } + + if !image.identity_swizzle() { + return Err(IncompatibleRenderPassAttachmentError::NotIdentitySwizzled); + } + + for subpass_num in 0 .. render_pass.num_subpasses() { + let subpass = render_pass.subpass_desc(subpass_num).expect("Subpass num out of range ; \ + wrong RenderPassDesc trait impl"); + + if subpass.color_attachments.iter().any(|&(n, _)| n == attachment_num) { + debug_assert!(image.parent().has_color()); // Was normally checked by the render pass. + if !image.parent().inner().usage_color_attachment() { + return Err(IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage); + } + } + + if let Some((ds, _)) = subpass.depth_stencil { + if ds == attachment_num { + // Was normally checked by the render pass. + debug_assert!(image.parent().has_depth() || image.parent().has_stencil()); + if !image.parent().inner().usage_depth_stencil_attachment() { + return Err(IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage); + } + } + } + + if subpass.input_attachments.iter().any(|&(n, _)| n == attachment_num) { + if !image.parent().inner().usage_input_attachment() { + return Err(IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage); + } + } + } + + // TODO: consider forbidding LoadOp::Load if image is transient + + // TODO: are all image layouts allowed? check this + + Ok(()) +} + +/// Error that can happen when an image is not compatible with a render pass attachment slot. +#[derive(Copy, Clone, Debug)] +pub enum IncompatibleRenderPassAttachmentError { + /// The image format expected by the render pass doesn't match the actual format of + /// the image. + FormatMismatch { + /// Format expected by the render pass. + expected: Format, + /// Format of the image. + obtained: Format, + }, + + /// The number of samples expected by the render pass doesn't match the number of samples of + /// the image. + SamplesMismatch { + /// Number of samples expected by the render pass. + expected: u32, + /// Number of samples of the image. + obtained: u32, + }, + + /// The image view has a component swizzle that is different from identity. + NotIdentitySwizzled, + + /// The image is used as a color attachment but is missing the color attachment usage. + MissingColorAttachmentUsage, + + /// The image is used as a depth/stencil attachment but is missing the depth-stencil attachment + /// usage. + MissingDepthStencilAttachmentUsage, + + /// The image is used as an input attachment but is missing the input attachment usage. + MissingInputAttachmentUsage, +} + +impl error::Error for IncompatibleRenderPassAttachmentError { + #[inline] + fn description(&self) -> &str { + match *self { + IncompatibleRenderPassAttachmentError::FormatMismatch { .. } => { + "mismatch between the format expected by the render pass and the actual format" + }, + IncompatibleRenderPassAttachmentError::SamplesMismatch { .. } => { + "mismatch between the number of samples expected by the render pass and the actual \ + number of samples" + }, + IncompatibleRenderPassAttachmentError::NotIdentitySwizzled => { + "the image view does not use identity swizzling" + }, + IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage => { + "the image is used as a color attachment but is missing the color attachment usage" + }, + IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage => { + "the image is used as a depth/stencil attachment but is missing the depth-stencil \ + attachment usage" + }, + IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage => { + "the image is used as an input attachment but is missing the input \ + attachment usage" + }, + } + } +} + +impl fmt::Display for IncompatibleRenderPassAttachmentError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "{}", error::Error::description(self)) + } +} + +#[cfg(test)] +mod tests { + use format::Format; + use framebuffer::EmptySinglePassRenderPassDesc; + use image::AttachmentImage; + use image::ImageView; + use super::ensure_image_view_compatible; + use super::IncompatibleRenderPassAttachmentError; + + #[test] + fn basic_ok() { + let (device, _) = gfx_dev_and_queue!(); + + let rp = single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: Format::R8G8B8A8Unorm, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap(); + + let img = AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap(); + + ensure_image_view_compatible(&rp, 0, &img.access()).unwrap(); + } + + #[test] + fn format_mismatch() { + let (device, _) = gfx_dev_and_queue!(); + + let rp = single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: Store, + format: Format::R16G16Sfloat, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap(); + + let img = AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap(); + + match ensure_image_view_compatible(&rp, 0, &img.access()) { + Err(IncompatibleRenderPassAttachmentError::FormatMismatch { + expected: Format::R16G16Sfloat, obtained: Format::R8G8B8A8Unorm }) => (), + e => panic!("{:?}", e) + } + } + + #[test] + #[should_panic(expected = "Attachment num out of range")] + fn attachment_out_of_range() { + let (device, _) = gfx_dev_and_queue!(); + + let rp = EmptySinglePassRenderPassDesc; + let img = AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap(); + + let _ = ensure_image_view_compatible(&rp, 0, &img.access()); + } + + // TODO: more tests +} diff --git a/vulkano/src/framebuffer/desc.rs b/vulkano/src/framebuffer/desc.rs index ddd6ed67..99f2131e 100644 --- a/vulkano/src/framebuffer/desc.rs +++ b/vulkano/src/framebuffer/desc.rs @@ -14,12 +14,10 @@ use format::ClearValue; use format::Format; use format::FormatTy; use framebuffer::RenderPass; -use framebuffer::RenderPassDescAttachmentsList; use framebuffer::RenderPassDescClearValues; use framebuffer::RenderPassCompatible; use framebuffer::RenderPassCreationError; -use image::Layout as ImageLayout; -use image::ImageViewAccess; +use image::ImageLayout as ImageLayout; use sync::AccessFlagBits; use sync::PipelineStages; @@ -44,42 +42,46 @@ use vk; /// - The provided methods shouldn't be overriden with fancy implementations. For example /// `build_render_pass` must build a render pass from the description and not a different one. /// -pub unsafe trait RenderPassDesc: RenderPassDescClearValues> + - RenderPassDescAttachmentsList>> -{ +pub unsafe trait RenderPassDesc: RenderPassDescClearValues> { /// Returns the number of attachments of the render pass. fn num_attachments(&self) -> usize; + /// Returns the description of an attachment. /// /// Returns `None` if `num` is superior to `num_attachments()`. - fn attachment(&self, num: usize) -> Option; + fn attachment_desc(&self, num: usize) -> Option; + /// Returns an iterator to the list of attachments. #[inline] - fn attachments(&self) -> RenderPassDescAttachments where Self: Sized { + fn attachment_descs(&self) -> RenderPassDescAttachments where Self: Sized { RenderPassDescAttachments { render_pass: self, num: 0 } } /// Returns the number of subpasses of the render pass. fn num_subpasses(&self) -> usize; - /// Returns the description of a suvpass. + + /// Returns the description of a subpass. /// /// Returns `None` if `num` is superior to `num_subpasses()`. - fn subpass(&self, num: usize) -> Option; + fn subpass_desc(&self, num: usize) -> Option; + /// Returns an iterator to the list of subpasses. #[inline] - fn subpasses(&self) -> RenderPassDescSubpasses where Self: Sized { + fn subpass_descs(&self) -> RenderPassDescSubpasses where Self: Sized { RenderPassDescSubpasses { render_pass: self, num: 0 } } /// Returns the number of dependencies of the render pass. fn num_dependencies(&self) -> usize; + /// Returns the description of a dependency. /// /// Returns `None` if `num` is superior to `num_dependencies()`. - fn dependency(&self, num: usize) -> Option; + fn dependency_desc(&self, num: usize) -> Option; + /// Returns an iterator to the list of dependencies. #[inline] - fn dependencies(&self) -> RenderPassDescDependencies where Self: Sized { + fn dependency_descs(&self) -> RenderPassDescDependencies where Self: Sized { RenderPassDescDependencies { render_pass: self, num: 0 } } @@ -112,17 +114,17 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues> + /// Returns the number of color attachments of a subpass. Returns `None` if out of range. #[inline] fn num_color_attachments(&self, subpass: u32) -> Option { - (&self).subpasses().skip(subpass as usize).next().map(|p| p.color_attachments.len() as u32) + (&self).subpass_descs().skip(subpass as usize).next().map(|p| p.color_attachments.len() as u32) } /// Returns the number of samples of the attachments of a subpass. Returns `None` if out of /// range or if the subpass has no attachment. TODO: return an enum instead? #[inline] fn num_samples(&self, subpass: u32) -> Option { - (&self).subpasses().skip(subpass as usize).next().and_then(|p| { + (&self).subpass_descs().skip(subpass as usize).next().and_then(|p| { // TODO: chain input attachments as well? p.color_attachments.iter().cloned().chain(p.depth_stencil.clone().into_iter()) - .filter_map(|a| (&self).attachments().skip(a.0).next()) + .filter_map(|a| (&self).attachment_descs().skip(a.0).next()) .next().map(|a| a.samples) }) } @@ -131,13 +133,13 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues> + /// second element is `true` if there's a stencil attachment. Returns `None` if out of range. #[inline] fn has_depth_stencil_attachment(&self, subpass: u32) -> Option<(bool, bool)> { - (&self).subpasses().skip(subpass as usize).next().map(|p| { + (&self).subpass_descs().skip(subpass as usize).next().map(|p| { let atch_num = match p.depth_stencil { Some((d, _)) => d, None => return (false, false) }; - match (&self).attachments().skip(atch_num).next().unwrap().format.ty() { + match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() { FormatTy::Depth => (true, false), FormatTy::Stencil => (false, true), FormatTy::DepthStencil => (true, true), @@ -149,13 +151,13 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues> + /// Returns true if a subpass has a depth attachment or a depth-stencil attachment. #[inline] fn has_depth(&self, subpass: u32) -> Option { - (&self).subpasses().skip(subpass as usize).next().map(|p| { + (&self).subpass_descs().skip(subpass as usize).next().map(|p| { let atch_num = match p.depth_stencil { Some((d, _)) => d, None => return false }; - match (&self).attachments().skip(atch_num).next().unwrap().format.ty() { + match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() { FormatTy::Depth => true, FormatTy::Stencil => false, FormatTy::DepthStencil => true, @@ -168,7 +170,7 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues> + /// layout is not `DepthStencilReadOnlyOptimal`. #[inline] fn has_writable_depth(&self, subpass: u32) -> Option { - (&self).subpasses().skip(subpass as usize).next().map(|p| { + (&self).subpass_descs().skip(subpass as usize).next().map(|p| { let atch_num = match p.depth_stencil { Some((d, l)) => { if l == ImageLayout::DepthStencilReadOnlyOptimal { return false; } @@ -177,7 +179,7 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues> + None => return false }; - match (&self).attachments().skip(atch_num).next().unwrap().format.ty() { + match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() { FormatTy::Depth => true, FormatTy::Stencil => false, FormatTy::DepthStencil => true, @@ -189,13 +191,13 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues> + /// Returns true if a subpass has a stencil attachment or a depth-stencil attachment. #[inline] fn has_stencil(&self, subpass: u32) -> Option { - (&self).subpasses().skip(subpass as usize).next().map(|p| { + (&self).subpass_descs().skip(subpass as usize).next().map(|p| { let atch_num = match p.depth_stencil { Some((d, _)) => d, None => return false }; - match (&self).attachments().skip(atch_num).next().unwrap().format.ty() { + match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() { FormatTy::Depth => false, FormatTy::Stencil => true, FormatTy::DepthStencil => true, @@ -208,7 +210,7 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues> + /// layout is not `DepthStencilReadOnlyOptimal`. #[inline] fn has_writable_stencil(&self, subpass: u32) -> Option { - (&self).subpasses().skip(subpass as usize).next().map(|p| { + (&self).subpass_descs().skip(subpass as usize).next().map(|p| { let atch_num = match p.depth_stencil { Some((d, l)) => { if l == ImageLayout::DepthStencilReadOnlyOptimal { return false; } @@ -217,7 +219,7 @@ pub unsafe trait RenderPassDesc: RenderPassDescClearValues> + None => return false }; - match (&self).attachments().skip(atch_num).next().unwrap().format.ty() { + match (&self).attachment_descs().skip(atch_num).next().unwrap().format.ty() { FormatTy::Depth => false, FormatTy::Stencil => true, FormatTy::DepthStencil => true, @@ -234,8 +236,8 @@ unsafe impl RenderPassDesc for T where T: SafeDeref, T::Target: RenderPassDes } #[inline] - fn attachment(&self, num: usize) -> Option { - (**self).attachment(num) + fn attachment_desc(&self, num: usize) -> Option { + (**self).attachment_desc(num) } #[inline] @@ -244,8 +246,8 @@ unsafe impl RenderPassDesc for T where T: SafeDeref, T::Target: RenderPassDes } #[inline] - fn subpass(&self, num: usize) -> Option { - (**self).subpass(num) + fn subpass_desc(&self, num: usize) -> Option { + (**self).subpass_desc(num) } #[inline] @@ -254,8 +256,8 @@ unsafe impl RenderPassDesc for T where T: SafeDeref, T::Target: RenderPassDes } #[inline] - fn dependency(&self, num: usize) -> Option { - (**self).dependency(num) + fn dependency_desc(&self, num: usize) -> Option { + (**self).dependency_desc(num) } } @@ -273,7 +275,7 @@ impl<'a, R: ?Sized + 'a> Iterator for RenderPassDescAttachments<'a, R> where R: if self.num < self.render_pass.num_attachments() { let n = self.num; self.num += 1; - Some(self.render_pass.attachment(n).expect("Wrong RenderPassDesc implementation")) + Some(self.render_pass.attachment_desc(n).expect("Wrong RenderPassDesc implementation")) } else { None } @@ -294,7 +296,7 @@ impl<'a, R: ?Sized + 'a> Iterator for RenderPassDescSubpasses<'a, R> where R: Re if self.num < self.render_pass.num_subpasses() { let n = self.num; self.num += 1; - Some(self.render_pass.subpass(n).expect("Wrong RenderPassDesc implementation")) + Some(self.render_pass.subpass_desc(n).expect("Wrong RenderPassDesc implementation")) } else { None } @@ -315,7 +317,7 @@ impl<'a, R: ?Sized + 'a> Iterator for RenderPassDescDependencies<'a, R> where R: if self.num < self.render_pass.num_dependencies() { let n = self.num; self.num += 1; - Some(self.render_pass.dependency(n).expect("Wrong RenderPassDesc implementation")) + Some(self.render_pass.dependency_desc(n).expect("Wrong RenderPassDesc implementation")) } else { None } diff --git a/vulkano/src/framebuffer/empty.rs b/vulkano/src/framebuffer/empty.rs index ded6d292..dd9847d7 100644 --- a/vulkano/src/framebuffer/empty.rs +++ b/vulkano/src/framebuffer/empty.rs @@ -8,17 +8,12 @@ // according to those terms. use std::iter; -use std::sync::Arc; use format::ClearValue; -use framebuffer::AttachmentsList; -use framebuffer::FramebufferCreationError; use framebuffer::RenderPassDesc; -use framebuffer::RenderPassDescAttachmentsList; use framebuffer::RenderPassDescClearValues; use framebuffer::LayoutAttachmentDescription; use framebuffer::LayoutPassDescription; use framebuffer::LayoutPassDependencyDescription; -use image::ImageViewAccess; /// Description of an empty render pass. /// @@ -49,7 +44,7 @@ unsafe impl RenderPassDesc for EmptySinglePassRenderPassDesc { } #[inline] - fn attachment(&self, num: usize) -> Option { + fn attachment_desc(&self, num: usize) -> Option { None } @@ -59,7 +54,7 @@ unsafe impl RenderPassDesc for EmptySinglePassRenderPassDesc { } #[inline] - fn subpass(&self, num: usize) -> Option { + fn subpass_desc(&self, num: usize) -> Option { if num == 0 { Some(LayoutPassDescription { color_attachments: vec![], @@ -79,7 +74,7 @@ unsafe impl RenderPassDesc for EmptySinglePassRenderPassDesc { } #[inline] - fn dependency(&self, num: usize) -> Option { + fn dependency_desc(&self, num: usize) -> Option { None } @@ -143,24 +138,6 @@ unsafe impl RenderPassDesc for EmptySinglePassRenderPassDesc { } } -unsafe impl RenderPassDescAttachmentsList>> for EmptySinglePassRenderPassDesc { - #[inline] - fn check_attachments_list(&self, list: Vec>) -> Result, FramebufferCreationError> { - if list.is_empty() { - Ok(Box::new(()) as Box<_>) - } else { - panic!() // FIXME: return error instead - } - } -} - -unsafe impl RenderPassDescAttachmentsList<()> for EmptySinglePassRenderPassDesc { - #[inline] - fn check_attachments_list(&self, list: ()) -> Result, FramebufferCreationError> { - Ok(Box::new(()) as Box<_>) - } -} - unsafe impl RenderPassDescClearValues> for EmptySinglePassRenderPassDesc { #[inline] fn convert_clear_values(&self, values: Vec) -> Box> { diff --git a/vulkano/src/framebuffer/framebuffer.rs b/vulkano/src/framebuffer/framebuffer.rs index 765c8a03..62c803f5 100644 --- a/vulkano/src/framebuffer/framebuffer.rs +++ b/vulkano/src/framebuffer/framebuffer.rs @@ -7,6 +7,7 @@ // notice may not be copied, modified, or distributed except // according to those terms. +use std::cmp; use std::error; use std::fmt; use std::marker::PhantomData; @@ -20,14 +21,16 @@ use device::DeviceOwned; use format::ClearValue; use framebuffer::AttachmentsList; use framebuffer::FramebufferAbstract; +use framebuffer::IncompatibleRenderPassAttachmentError; use framebuffer::LayoutAttachmentDescription; use framebuffer::LayoutPassDependencyDescription; use framebuffer::LayoutPassDescription; use framebuffer::RenderPassAbstract; use framebuffer::RenderPassDescClearValues; -use framebuffer::RenderPassDescAttachmentsList; use framebuffer::RenderPassDesc; use framebuffer::RenderPassSys; +use framebuffer::ensure_image_view_compatible; +use image::ImageView; use image::ImageViewAccess; use Error; @@ -37,88 +40,47 @@ use VulkanPointers; use check_errors; use vk; -/// Contains the list of images attached to a render pass. +/// Contains a render pass and the image views that are attached to it. /// -/// Creating a framebuffer is done by passing the render pass object, the dimensions of the -/// framebuffer, and the list of attachments to `Framebuffer::new()`. +/// Creating a framebuffer is done by calling `Framebuffer::start`, which returns a +/// `FramebufferBuilder` object. You can then add the framebuffer attachments one by one by +/// calling `add(image)`. When you are done, call `build()`. /// -/// Just like all render pass objects implement the `RenderPassAbstract` trait, all framebuffer -/// objects implement the `FramebufferAbstract` trait. This means that you can cast any -/// `Arc>` into an `Arc` for easier storage. +/// Both the `add` and the `build` functions perform various checks to make sure that the number +/// of images is correct and that each image is compatible with the attachment definition in the +/// render pass. /// -/// ## With a generic list of attachments -/// -/// The list of attachments passed to `Framebuffer::new()` can be of various types, but one of the -/// possibilities is to pass an object of type `Vec>`. -/// -/// > **Note**: If you access a render pass object through the `RenderPassAbstract` trait, passing -/// > a `Vec>` is the only possible method. -/// -/// The framebuffer constructor will perform various checks to make sure that the number of images -/// is correct and that each image can be used with this render pass. -/// -/// ```ignore // FIXME: unignore +/// ``` /// # use std::sync::Arc; /// # use vulkano::framebuffer::RenderPassAbstract; /// use vulkano::framebuffer::Framebuffer; /// /// # let render_pass: Arc = return; -/// # let my_image: Arc = return; -/// // let render_pass: Arc = ...; -/// let framebuffer = Framebuffer::new(render_pass.clone(), [1024, 768, 1], -/// vec![my_image.clone() as Arc<_>]).unwrap(); +/// # let my_image: Arc> = return; +/// // let render_pass: Arc<_> = ...; +/// let framebuffer = Framebuffer::start(render_pass.clone()) +/// .add(my_image).unwrap() +/// .build().unwrap(); /// ``` /// -/// ## With a specialized list of attachments +/// Just like render pass objects implement the `RenderPassAbstract` trait, all framebuffer +/// objects implement the `FramebufferAbstract` trait. This means that you can cast any +/// `Arc>` into an `Arc` for easier storage. /// -/// The list of attachments can also be of any type `T`, as long as the render pass description -/// implements the trait `RenderPassDescAttachmentsList`. +/// ## Framebuffer dimensions /// -/// For example if you pass a render pass object that implements -/// `RenderPassDescAttachmentsList`, then you can pass a `Foo` as the list of attachments. +/// If you use `Framebuffer::start()` to create a framebuffer then vulkano will automatically +/// make sure that all the attachments have the same dimensions, as this is the most common +/// situation. /// -/// > **Note**: The reason why `Vec>` always works (see previous section) is that -/// > render pass descriptions are required to always implement -/// > `RenderPassDescAttachmentsList>>`. +/// Alternatively you can also use `with_intersecting_dimensions`, in which case the dimensions of +/// the framebuffer will be the intersection of the dimensions of all attachments, or +/// `with_dimensions` if you want to specify exact dimensions. If you use `with_dimensions`, you +/// are allowed to attach images that are larger than these dimensions. /// -/// When it comes to the `single_pass_renderpass!` and `ordered_passes_renderpass!` macros, you can -/// build a list of attachments by calling `start_attachments()` on the render pass description, -/// which will return an object that has a method whose name is the name of the first attachment -/// and that can be used to specify it. This method will return another object that has a method -/// whose name is the name of the second attachment, and so on. See the documentation of the macros -/// for more details. TODO: put link here +/// If the dimensions of the framebuffer don't match the dimensions of one of its attachment, then +/// only the top-left hand corner of the image will be drawn to. /// -/// ```ignore // FIXME: unignore -/// # #[macro_use] extern crate vulkano; -/// # fn main() { -/// # let device: std::sync::Arc = return; -/// use std::sync::Arc; -/// use vulkano::format::Format; -/// use vulkano::framebuffer::Framebuffer; -/// -/// let render_pass = single_pass_renderpass!(device.clone(), -/// attachments: { -/// // `foo` is a custom name we give to the first and only attachment. -/// foo: { -/// load: Clear, -/// store: Store, -/// format: Format::R8G8B8A8Unorm, -/// samples: 1, -/// } -/// }, -/// pass: { -/// color: [foo], // Repeat the attachment name here. -/// depth_stencil: {} -/// } -/// ).unwrap(); -/// -/// # let my_image: Arc = return; -/// let framebuffer = { -/// let atch = render_pass.desc().start_attachments().foo(my_image.clone() as Arc<_>); -/// Framebuffer::new(render_pass, [1024, 768, 1], atch).unwrap() -/// }; -/// # } -/// ``` #[derive(Debug)] pub struct Framebuffer { device: Arc, @@ -128,24 +90,199 @@ pub struct Framebuffer { resources: A, } -impl Framebuffer> { - /// Builds a new framebuffer. - /// - /// The `attachments` parameter depends on which render pass implementation is used. - // TODO: allow ImageView - pub fn new(render_pass: Rp, dimensions: [u32; 3], attachments: Ia) - -> Result>>, FramebufferCreationError> - where Rp: RenderPassAbstract + RenderPassDescAttachmentsList - { - let device = render_pass.device().clone(); +impl Framebuffer { + /// Starts building a framebuffer. + pub fn start(render_pass: Rp) -> FramebufferBuilder { + FramebufferBuilder { + render_pass: render_pass, + raw_ids: SmallVec::new(), + dimensions: FramebufferBuilderDimensions::AutoIdentical(None), + attachments: (), + } + } - // This function call is supposed to check whether the attachments are valid. - // For more safety, we do some additional `debug_assert`s below. - let attachments = try!(render_pass.check_attachments_list(attachments)); + /// Starts building a framebuffer. The dimensions of the framebuffer will automatically be + /// the intersection of the dimensions of all the attachments. + pub fn with_intersecting_dimensions(render_pass: Rp) -> FramebufferBuilder { + FramebufferBuilder { + render_pass: render_pass, + raw_ids: SmallVec::new(), + dimensions: FramebufferBuilderDimensions::AutoSmaller(None), + attachments: (), + } + } + + /// Starts building a framebuffer. + pub fn with_dimensions(render_pass: Rp, dimensions: [u32; 3]) -> FramebufferBuilder { + FramebufferBuilder { + render_pass: render_pass, + raw_ids: SmallVec::new(), + dimensions: FramebufferBuilderDimensions::Specific(dimensions), + attachments: (), + } + } +} + +/// Prototype of a framebuffer. +pub struct FramebufferBuilder { + render_pass: Rp, + raw_ids: SmallVec<[vk::ImageView; 8]>, + dimensions: FramebufferBuilderDimensions, + attachments: A, +} + +impl fmt::Debug for FramebufferBuilder where Rp: fmt::Debug, A: fmt::Debug { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt.debug_struct("FramebufferBuilder") + .field("render_pass", &self.render_pass) + .field("dimensions", &self.dimensions) + .field("attachments", &self.attachments) + .finish() + } +} + +#[derive(Debug)] +enum FramebufferBuilderDimensions { + AutoIdentical(Option<[u32; 3]>), + AutoSmaller(Option<[u32; 3]>), + Specific([u32; 3]), +} + +impl FramebufferBuilder + where Rp: RenderPassAbstract, + A: AttachmentsList, +{ + /// Appends an attachment to the prototype of the framebuffer. + /// + /// Attachments must be added in the same order as the one defined in the render pass. + pub fn add(self, attachment: T) + -> Result, FramebufferCreationError> + where T: ImageView + { + let access = attachment.access(); + + if self.raw_ids.len() >= self.render_pass.num_attachments() { + return Err(FramebufferCreationError::AttachmentsCountMismatch { + expected: self.render_pass.num_attachments(), + obtained: self.raw_ids.len() + 1, + }); + } + + match ensure_image_view_compatible(&self.render_pass, self.raw_ids.len(), &access) { + Ok(()) => (), + Err(err) => return Err(FramebufferCreationError::IncompatibleAttachment(err)) + }; + + let img_dims = access.dimensions(); + debug_assert_eq!(img_dims.depth(), 1); + + let dimensions = match self.dimensions { + FramebufferBuilderDimensions::AutoIdentical(None) => { + let dims = [img_dims.width(), img_dims.height(), img_dims.array_layers()]; + FramebufferBuilderDimensions::AutoIdentical(Some(dims)) + }, + FramebufferBuilderDimensions::AutoIdentical(Some(current)) => { + if img_dims.width() != current[0] || img_dims.height() != current[1] || + img_dims.array_layers() != current[2] + { + return Err(FramebufferCreationError::AttachmentDimensionsIncompatible { + expected: current, + obtained: [img_dims.width(), img_dims.height(), img_dims.array_layers()] + }); + } + + FramebufferBuilderDimensions::AutoIdentical(Some(current)) + } + FramebufferBuilderDimensions::AutoSmaller(None) => { + let dims = [img_dims.width(), img_dims.height(), img_dims.array_layers()]; + FramebufferBuilderDimensions::AutoSmaller(Some(dims)) + }, + FramebufferBuilderDimensions::AutoSmaller(Some(current)) => { + let new_dims = [ + cmp::min(current[0], img_dims.width()), + cmp::min(current[1], img_dims.height()), + cmp::min(current[2], img_dims.array_layers()) + ]; + + FramebufferBuilderDimensions::AutoSmaller(Some(new_dims)) + }, + FramebufferBuilderDimensions::Specific(current) => { + if img_dims.width() < current[0] || img_dims.height() < current[1] || + img_dims.array_layers() < current[2] + { + return Err(FramebufferCreationError::AttachmentDimensionsIncompatible { + expected: current, + obtained: [img_dims.width(), img_dims.height(), img_dims.array_layers()] + }); + } + + FramebufferBuilderDimensions::Specific([ + img_dims.width(), + img_dims.height(), + img_dims.array_layers() + ]) + } + }; + + let mut raw_ids = self.raw_ids; + raw_ids.push(access.inner().internal_object()); + + Ok(FramebufferBuilder { + render_pass: self.render_pass, + raw_ids: raw_ids, + dimensions: dimensions, + attachments: (self.attachments, access), + }) + } + + /// Turns this builder into a `FramebufferBuilder>`. + /// + /// This allows you to store the builder in situations where you don't know in advance the + /// number of attachments. + /// + /// > **Note**: This is a very rare corner case and you shouldn't have to use this function + /// > in most situations. + #[inline] + pub fn boxed(self) -> FramebufferBuilder> + where A: 'static + { + FramebufferBuilder { + render_pass: self.render_pass, + raw_ids: self.raw_ids, + dimensions: self.dimensions, + attachments: Box::new(self.attachments) as Box<_>, + } + } + + /// Builds the framebuffer. + pub fn build(self) -> Result, FramebufferCreationError> { + let device = self.render_pass.device().clone(); + + // Check the number of attachments. + if self.raw_ids.len() != self.render_pass.num_attachments() { + return Err(FramebufferCreationError::AttachmentsCountMismatch { + expected: self.render_pass.num_attachments(), + obtained: self.raw_ids.len(), + }); + } + + // Compute the dimensions. + let dimensions = match self.dimensions { + FramebufferBuilderDimensions::Specific(dims) | + FramebufferBuilderDimensions::AutoIdentical(Some(dims)) | + FramebufferBuilderDimensions::AutoSmaller(Some(dims)) => { + dims + }, + FramebufferBuilderDimensions::AutoIdentical(None) | + FramebufferBuilderDimensions::AutoSmaller(None) => { + return Err(FramebufferCreationError::CantDetermineDimensions); + }, + }; // Checking the dimensions against the limits. { - let limits = render_pass.device().physical_device().limits(); + let limits = device.physical_device().limits(); let limits = [limits.max_framebuffer_width(), limits.max_framebuffer_height(), limits.max_framebuffer_layers()]; if dimensions[0] > limits[0] || dimensions[1] > limits[1] || @@ -155,28 +292,16 @@ impl Framebuffer> { } } - // Checking the dimensions against the attachments. - if let Some(dims_constraints) = attachments.intersection_dimensions() { - if dims_constraints[0] < dimensions[0] || dims_constraints[1] < dimensions[1] || - dims_constraints[2] < dimensions[2] - { - return Err(FramebufferCreationError::AttachmentTooSmall); - } - } - - let ids: SmallVec<[vk::ImageView; 8]> = - attachments.raw_image_view_handles().into_iter().map(|v| v.internal_object()).collect(); - let framebuffer = unsafe { - let vk = render_pass.device().pointers(); + let vk = device.pointers(); let infos = vk::FramebufferCreateInfo { sType: vk::STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, pNext: ptr::null(), flags: 0, // reserved - renderPass: render_pass.inner().internal_object(), - attachmentCount: ids.len() as u32, - pAttachments: ids.as_ptr(), + renderPass: self.render_pass.inner().internal_object(), + attachmentCount: self.raw_ids.len() as u32, + pAttachments: self.raw_ids.as_ptr(), width: dimensions[0], height: dimensions[1], layers: dimensions[2], @@ -188,13 +313,13 @@ impl Framebuffer> { output }; - Ok(Arc::new(Framebuffer { + Ok(Framebuffer { device: device, - render_pass: render_pass, + render_pass: self.render_pass, framebuffer: framebuffer, dimensions: dimensions, - resources: attachments, - })) + resources: self.attachments, + }) } } @@ -263,8 +388,8 @@ unsafe impl RenderPassDesc for Framebuffer where Rp: RenderPassDes } #[inline] - fn attachment(&self, num: usize) -> Option { - self.render_pass.attachment(num) + fn attachment_desc(&self, num: usize) -> Option { + self.render_pass.attachment_desc(num) } #[inline] @@ -273,8 +398,8 @@ unsafe impl RenderPassDesc for Framebuffer where Rp: RenderPassDes } #[inline] - fn subpass(&self, num: usize) -> Option { - self.render_pass.subpass(num) + fn subpass_desc(&self, num: usize) -> Option { + self.render_pass.subpass_desc(num) } #[inline] @@ -283,17 +408,8 @@ unsafe impl RenderPassDesc for Framebuffer where Rp: RenderPassDes } #[inline] - fn dependency(&self, num: usize) -> Option { - self.render_pass.dependency(num) - } -} - -unsafe impl RenderPassDescAttachmentsList for Framebuffer - where Rp: RenderPassDescAttachmentsList -{ - #[inline] - fn check_attachments_list(&self, atch: At) -> Result, FramebufferCreationError> { - self.render_pass.check_attachments_list(atch) + fn dependency_desc(&self, num: usize) -> Option { + self.render_pass.dependency_desc(num) } } @@ -344,17 +460,30 @@ unsafe impl<'a> VulkanObject for FramebufferSys<'a> { } /// Error that can happen when creating a framebuffer object. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u32)] +#[derive(Copy, Clone, Debug)] pub enum FramebufferCreationError { /// Out of memory. OomError(OomError), /// The requested dimensions exceed the device's limits. DimensionsTooLarge, - /// One of the attachments has a component swizzle that is different from identity. - AttachmentNotIdentitySwizzled, - /// One of the attachments is too small compared to the requested framebuffer dimensions. - AttachmentTooSmall, + /// The attachment has a size that isn't compatible with the requested framebuffer dimensions. + AttachmentDimensionsIncompatible { + /// Expected dimensions. + expected: [u32; 3], + /// Attachment dimensions. + obtained: [u32; 3], + }, + /// The number of attachments doesn't match the number expected by the render pass. + AttachmentsCountMismatch { + /// Expected number of attachments. + expected: usize, + /// Number of attachments that were given. + obtained: usize, + }, + /// One of the images cannot be used as the requested attachment. + IncompatibleAttachment(IncompatibleRenderPassAttachmentError), + /// The framebuffer has no attachment and no dimension was specified. + CantDetermineDimensions, } impl From for FramebufferCreationError { @@ -371,12 +500,17 @@ impl error::Error for FramebufferCreationError { FramebufferCreationError::OomError(_) => "no memory available", FramebufferCreationError::DimensionsTooLarge => "the dimensions of the framebuffer \ are too large", - FramebufferCreationError::AttachmentNotIdentitySwizzled => { - "one of the attachments has a component swizzle that is different from identity" + FramebufferCreationError::AttachmentDimensionsIncompatible { .. } => { + "the attachment has a size that isn't compatible with the framebuffer dimensions" }, - FramebufferCreationError::AttachmentTooSmall => { - "one of the attachments is too small compared to the requested framebuffer \ - dimensions" + FramebufferCreationError::AttachmentsCountMismatch { .. } => { + "the number of attachments doesn't match the number expected by the render pass" + }, + FramebufferCreationError::IncompatibleAttachment(_) => { + "one of the images cannot be used as the requested attachment" + }, + FramebufferCreationError::CantDetermineDimensions => { + "the framebuffer has no attachment and no dimension was specified" }, } } @@ -385,6 +519,7 @@ impl error::Error for FramebufferCreationError { fn cause(&self) -> Option<&error::Error> { match *self { FramebufferCreationError::OomError(ref err) => Some(err), + FramebufferCreationError::IncompatibleAttachment(ref err) => Some(err), _ => None, } } @@ -404,70 +539,314 @@ impl From for FramebufferCreationError { } } -/* FIXME: restore #[cfg(test)] mod tests { - use format::R8G8B8A8Unorm; + use std::sync::Arc; + use format::Format; + use framebuffer::EmptySinglePassRenderPassDesc; use framebuffer::Framebuffer; use framebuffer::FramebufferCreationError; + use framebuffer::RenderPassDesc; use image::attachment::AttachmentImage; #[test] fn simple_create() { let (device, _) = gfx_dev_and_queue!(); - let render_pass = single_pass_renderpass! { + let render_pass = Arc::new(single_pass_renderpass!(device.clone(), attachments: { color: { load: Clear, store: DontCare, - format: R8G8B8A8Unorm, + format: Format::R8G8B8A8Unorm, + samples: 1, } }, pass: { color: [color], depth_stencil: {} } - }.unwrap(); + ).unwrap()); - let image = AttachmentImage::new(&device, [1024, 768], R8G8B8A8Unorm).unwrap(); - - let _ = Framebuffer::new(render_pass, [1024, 768, 1], example::AList { - color: image.clone() - }).unwrap(); + let image = AttachmentImage::new(device.clone(), [1024, 768], + Format::R8G8B8A8Unorm).unwrap(); + let _ = Framebuffer::start(render_pass).add(image.clone()).unwrap().build().unwrap(); } #[test] - fn framebuffer_too_large() { + fn check_device_limits() { let (device, _) = gfx_dev_and_queue!(); - let render_pass = example::CustomRenderPass::new(&device, &example::Formats { - color: (R8G8B8A8Unorm, 1) - }).unwrap(); - - let image = AttachmentImage::new(&device, [1024, 768], R8G8B8A8Unorm).unwrap(); - - let alist = example::AList { color: image.clone() }; - match Framebuffer::new(render_pass, [0xffffffff, 0xffffffff, 0xffffffff], alist) { + let rp = EmptySinglePassRenderPassDesc.build_render_pass(device).unwrap(); + let res = Framebuffer::with_dimensions(rp, [0xffffffff, 0xffffffff, 0xffffffff]).build(); + match res { Err(FramebufferCreationError::DimensionsTooLarge) => (), _ => panic!() } } #[test] - fn attachment_too_small() { + fn attachment_format_mismatch() { let (device, _) = gfx_dev_and_queue!(); - let render_pass = example::CustomRenderPass::new(&device, &example::Formats { - color: (R8G8B8A8Unorm, 1) - }).unwrap(); + let render_pass = Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()); - let image = AttachmentImage::new(&device, [512, 512], R8G8B8A8Unorm).unwrap(); + let image = AttachmentImage::new(device.clone(), [1024, 768], + Format::R8Unorm).unwrap(); - let alist = example::AList { color: image.clone() }; - match Framebuffer::new(render_pass, [600, 600, 1], alist) { - Err(FramebufferCreationError::AttachmentTooSmall) => (), + match Framebuffer::start(render_pass).add(image.clone()) { + Err(FramebufferCreationError::IncompatibleAttachment(_)) => (), _ => panic!() } } -}*/ + + // TODO: check samples mismatch + + #[test] + fn attachment_dims_larger_than_specified_valid() { + let (device, _) = gfx_dev_and_queue!(); + + let render_pass = Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()); + + let img = AttachmentImage::new(device.clone(), [600, 600], Format::R8G8B8A8Unorm).unwrap(); + + let _ = Framebuffer::with_dimensions(render_pass, [512, 512, 1]) + .add(img).unwrap() + .build().unwrap(); + } + + #[test] + fn attachment_dims_smaller_than_specified() { + let (device, _) = gfx_dev_and_queue!(); + + let render_pass = Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + color: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + } + }, + pass: { + color: [color], + depth_stencil: {} + } + ).unwrap()); + + let img = AttachmentImage::new(device.clone(), [512, 700], Format::R8G8B8A8Unorm).unwrap(); + + match Framebuffer::with_dimensions(render_pass, [600, 600, 1]).add(img) { + Err(FramebufferCreationError::AttachmentDimensionsIncompatible { expected, obtained }) => { + assert_eq!(expected, [600, 600, 1]); + assert_eq!(obtained, [512, 700, 1]); + }, + _ => panic!() + } + } + + #[test] + fn multi_attachments_dims_not_identical() { + let (device, _) = gfx_dev_and_queue!(); + + let render_pass = Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + a: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + }, + b: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + } + }, + pass: { + color: [a, b], + depth_stencil: {} + } + ).unwrap()); + + let a = AttachmentImage::new(device.clone(), [512, 512], Format::R8G8B8A8Unorm).unwrap(); + let b = AttachmentImage::new(device.clone(), [512, 513], Format::R8G8B8A8Unorm).unwrap(); + + match Framebuffer::start(render_pass).add(a).unwrap().add(b) { + Err(FramebufferCreationError::AttachmentDimensionsIncompatible { expected, obtained }) => { + assert_eq!(expected, [512, 512, 1]); + assert_eq!(obtained, [512, 513, 1]); + }, + _ => panic!() + } + } + + #[test] + fn multi_attachments_auto_smaller() { + let (device, _) = gfx_dev_and_queue!(); + + let render_pass = Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + a: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + }, + b: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + } + }, + pass: { + color: [a, b], + depth_stencil: {} + } + ).unwrap()); + + let a = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap(); + let b = AttachmentImage::new(device.clone(), [512, 128], Format::R8G8B8A8Unorm).unwrap(); + + let fb = Framebuffer::with_intersecting_dimensions(render_pass) + .add(a).unwrap() + .add(b).unwrap() + .build().unwrap(); + + match (fb.width(), fb.height(), fb.layers()) { + (256, 128, 1) => (), + _ => panic!() + } + } + + #[test] + fn not_enough_attachments() { + let (device, _) = gfx_dev_and_queue!(); + + let render_pass = Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + a: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + }, + b: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + } + }, + pass: { + color: [a, b], + depth_stencil: {} + } + ).unwrap()); + + let img = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap(); + + let res = Framebuffer::with_intersecting_dimensions(render_pass) + .add(img).unwrap() + .build(); + + match res { + Err(FramebufferCreationError::AttachmentsCountMismatch { expected: 2, + obtained: 1 }) => (), + _ => panic!() + } + } + + #[test] + fn too_many_attachments() { + let (device, _) = gfx_dev_and_queue!(); + + let render_pass = Arc::new(single_pass_renderpass!(device.clone(), + attachments: { + a: { + load: Clear, + store: DontCare, + format: Format::R8G8B8A8Unorm, + samples: 1, + } + }, + pass: { + color: [a], + depth_stencil: {} + } + ).unwrap()); + + let a = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap(); + let b = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap(); + + let res = Framebuffer::with_intersecting_dimensions(render_pass) + .add(a).unwrap() + .add(b); + + match res { + Err(FramebufferCreationError::AttachmentsCountMismatch { expected: 1, + obtained: 2 }) => (), + _ => panic!() + } + } + + #[test] + fn empty_working() { + let (device, _) = gfx_dev_and_queue!(); + + let rp = EmptySinglePassRenderPassDesc.build_render_pass(device).unwrap(); + let _ = Framebuffer::with_dimensions(rp, [512, 512, 1]).build().unwrap(); + } + + #[test] + fn cant_determine_dimensions_auto() { + let (device, _) = gfx_dev_and_queue!(); + + let rp = EmptySinglePassRenderPassDesc.build_render_pass(device).unwrap(); + let res = Framebuffer::start(rp).build(); + match res { + Err(FramebufferCreationError::CantDetermineDimensions) => (), + _ => panic!() + } + } + + #[test] + fn cant_determine_dimensions_intersect() { + let (device, _) = gfx_dev_and_queue!(); + + let rp = EmptySinglePassRenderPassDesc.build_render_pass(device).unwrap(); + let res = Framebuffer::with_intersecting_dimensions(rp).build(); + match res { + Err(FramebufferCreationError::CantDetermineDimensions) => (), + _ => panic!() + } + } +} diff --git a/vulkano/src/framebuffer/macros.rs b/vulkano/src/framebuffer/macros.rs index e2520289..79b0aded 100644 --- a/vulkano/src/framebuffer/macros.rs +++ b/vulkano/src/framebuffer/macros.rs @@ -65,19 +65,14 @@ macro_rules! ordered_passes_renderpass { #![allow(non_camel_case_types)] #![allow(non_snake_case)] - use std::sync::Arc; use $crate::format::ClearValue; use $crate::format::Format; - use $crate::framebuffer::AttachmentsList; - use $crate::framebuffer::FramebufferCreationError; use $crate::framebuffer::RenderPassDesc; - use $crate::framebuffer::RenderPassDescAttachmentsList; use $crate::framebuffer::RenderPassDescClearValues; use $crate::framebuffer::LayoutAttachmentDescription; use $crate::framebuffer::LayoutPassDescription; use $crate::framebuffer::LayoutPassDependencyDescription; - use $crate::image::Layout; - use $crate::image::ImageViewAccess; + use $crate::image::ImageLayout; use $crate::sync::AccessFlagBits; use $crate::sync::PipelineStages; @@ -87,37 +82,6 @@ macro_rules! ordered_passes_renderpass { )* } - impl CustomRenderPassDesc { - #[inline] - pub fn start_attachments(&self) -> atch::AttachmentsStart { - atch::AttachmentsStart - } - - #[inline] - pub fn start_clear_values(&self) -> cv::ClearValuesStart { - cv::ClearValuesStart - } - } - - pub mod atch { - use $crate::framebuffer::AttachmentsList; - use $crate::framebuffer::FramebufferCreationError; - use $crate::framebuffer::RenderPassDescAttachmentsList; - use $crate::image::traits::ImageViewAccess; - use super::CustomRenderPassDesc; - pub struct AttachmentsStart; - ordered_passes_renderpass!{[] __impl_attachments__ [] [] [$($atch_name),*] [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z]} - } - - pub mod cv { - use std::iter; - use $crate::format::ClearValue; - use $crate::framebuffer::RenderPassDescClearValues; - use super::CustomRenderPassDesc; - pub struct ClearValuesStart; - ordered_passes_renderpass!{[] __impl_clear_values__ [] [] [$($atch_name: $load),*] [A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z]} - } - #[allow(unsafe_code)] unsafe impl RenderPassDesc for CustomRenderPassDesc { #[inline] @@ -126,7 +90,7 @@ macro_rules! ordered_passes_renderpass { } #[inline] - fn attachment(&self, id: usize) -> Option { + fn attachment_desc(&self, id: usize) -> Option { attachment(self, id) } @@ -136,7 +100,7 @@ macro_rules! ordered_passes_renderpass { } #[inline] - fn subpass(&self, id: usize) -> Option { + fn subpass_desc(&self, id: usize) -> Option { subpass(id) } @@ -146,7 +110,7 @@ macro_rules! ordered_passes_renderpass { } #[inline] - fn dependency(&self, id: usize) -> Option { + fn dependency_desc(&self, id: usize) -> Option { dependency(id) } } @@ -158,14 +122,6 @@ macro_rules! ordered_passes_renderpass { } } - unsafe impl RenderPassDescAttachmentsList>> for CustomRenderPassDesc { - fn check_attachments_list(&self, list: Vec>) -> Result, FramebufferCreationError> { - // FIXME: correct safety checks - assert_eq!(list.len(), self.num_attachments()); - Ok(Box::new(list) as Box<_>) - } - } - #[inline] fn num_attachments() -> usize { #![allow(unused_assignments)] @@ -233,19 +189,19 @@ macro_rules! ordered_passes_renderpass { if id == cur_pass_num { let mut depth = None; $( - depth = Some(($depth_atch, Layout::DepthStencilAttachmentOptimal)); + depth = Some(($depth_atch, ImageLayout::DepthStencilAttachmentOptimal)); )* return Some(LayoutPassDescription { color_attachments: vec![ $( - ($color_atch, Layout::ColorAttachmentOptimal) + ($color_atch, ImageLayout::ColorAttachmentOptimal) ),* ], depth_stencil: depth, input_attachments: vec![ $( - ($input_atch, Layout::ShaderReadOnlyOptimal) + ($input_atch, ImageLayout::ShaderReadOnlyOptimal) ),* ], resolve_attachments: vec![], @@ -291,7 +247,7 @@ macro_rules! ordered_passes_renderpass { /// Returns the initial and final layout of an attachment, given its num. /// /// The value always correspond to the first and last usages of an attachment. - fn attachment_layouts(num: usize) -> (Layout, Layout) { + fn attachment_layouts(num: usize) -> (ImageLayout, ImageLayout) { #![allow(unused_assignments)] #![allow(unused_mut)] #![allow(unused_variables)] @@ -309,40 +265,40 @@ macro_rules! ordered_passes_renderpass { $( if $depth_atch == num { if initial_layout.is_none() { - initial_layout = Some(Layout::DepthStencilAttachmentOptimal); + initial_layout = Some(ImageLayout::DepthStencilAttachmentOptimal); } - final_layout = Some(Layout::DepthStencilAttachmentOptimal); + final_layout = Some(ImageLayout::DepthStencilAttachmentOptimal); } )* $( if $color_atch == num { if initial_layout.is_none() { - initial_layout = Some(Layout::ColorAttachmentOptimal); + initial_layout = Some(ImageLayout::ColorAttachmentOptimal); } - final_layout = Some(Layout::ColorAttachmentOptimal); + final_layout = Some(ImageLayout::ColorAttachmentOptimal); } )* $( if $input_atch == num { if initial_layout.is_none() { - initial_layout = Some(Layout::ShaderReadOnlyOptimal); + initial_layout = Some(ImageLayout::ShaderReadOnlyOptimal); } - final_layout = Some(Layout::ShaderReadOnlyOptimal); + final_layout = Some(ImageLayout::ShaderReadOnlyOptimal); } )* })* $(if $atch_name == num { // If the clear OP is Clear or DontCare, default to the Undefined layout. - if initial_layout == Some(Layout::DepthStencilAttachmentOptimal) || - initial_layout == Some(Layout::ColorAttachmentOptimal) + if initial_layout == Some(ImageLayout::DepthStencilAttachmentOptimal) || + initial_layout == Some(ImageLayout::ColorAttachmentOptimal) { if $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::Clear || $crate::framebuffer::LoadOp::$load == $crate::framebuffer::LoadOp::DontCare { - initial_layout = Some(Layout::Undefined); + initial_layout = Some(ImageLayout::Undefined); } } @@ -360,178 +316,4 @@ macro_rules! ordered_passes_renderpass { )* }.build_render_pass($device) }); - - - - - - - - ([] __impl_attachments__ [] [] [] [$($params:ident),*]) => { - unsafe impl RenderPassDescAttachmentsList for CustomRenderPassDesc { - type List = (); - - fn check_attachments_list(&self, attachments: AttachmentsStart) -> Result<(), FramebufferCreationError> { - Ok(()) // FIXME: - } - } - }; - - ([] __impl_attachments__ [] [] [$next:ident $(, $rest:ident)*] [$first_param:ident, $($rest_params:ident),*]) => { - pub struct $next<$first_param> { - current: $first_param, - } - - impl AttachmentsStart { - pub fn $next<$first_param>(self, next: $first_param) -> $next<$first_param> { - $next { - current: next, - } - } - } - - impl<$first_param> $next<$first_param> { - fn check_attachments_list(self) -> Result<($first_param,), FramebufferCreationError> { - Ok((self.current,)) // FIXME: check attachment - } - } - - ordered_passes_renderpass!{[] __impl_attachments__ [$next] [$first_param] [$($rest),*] [$($rest_params),*]} - }; - - ([] __impl_attachments__ [$prev:ident] [$($prev_params:ident),*] [] [$($params:ident),*]) => { - unsafe impl<$($prev_params),*> RenderPassDescAttachmentsList<$prev<$($prev_params),*>> for CustomRenderPassDesc - where $($prev_params: ImageViewAccess + Send + Sync + 'static),* - { - //type List = ($($prev_params,)*); - - fn check_attachments_list(&self, attachments: $prev<$($prev_params,)*>) -> Result, FramebufferCreationError> { - Ok(Box::new(try!(attachments.check_attachments_list()))) - - // FIXME: - /*$({ - if !l.$atch_name.identity_swizzle() { - return Err(FramebufferCreationError::AttachmentNotIdentitySwizzled); - } - })**/ - } - } - }; - - ([] __impl_attachments__ [$prev:ident] [$($prev_params:ident),*] [$next:ident $(, $rest:ident)*] [$first_param:ident, $($rest_params:ident),*]) => { - pub struct $next<$($prev_params,)* $first_param> { - prev: $prev<$($prev_params),*>, - current: $first_param, - } - - impl<$($prev_params),*> $prev<$($prev_params),*> { - pub fn $next<$first_param>(self, next: $first_param) -> $next<$($prev_params,)* $first_param> { - $next { - prev: self, - current: next, - } - } - } - - impl<$($prev_params,)* $first_param> $next<$($prev_params,)* $first_param> { - fn check_attachments_list(self) -> Result<($($prev_params,)* $first_param), FramebufferCreationError> { - let ($($prev_params,)*) = try!(self.prev.check_attachments_list()); - // FIXME: check attachment - Ok(($($prev_params,)* self.current)) - } - } - - ordered_passes_renderpass!{[] __impl_attachments__ [$next] [$($prev_params,)* $first_param] [$($rest),*] [$($rest_params),*]} - }; - - - - - - - - ([] __impl_clear_values__ [] [] [] [$($params:ident),*]) => { - unsafe impl RenderPassDescClearValues for CustomRenderPassDesc { - #[inline] - fn convert_clear_values(&self, values: ClearValuesStart) -> Box> { - Box::new(iter::empty()) - } - } - }; - - ([] __impl_clear_values__ [] [] [$next:ident: Clear $(, $rest:ident: $rest_load:ident)*] [$first_param:ident, $($rest_params:ident),*]) => { - pub struct $next<$first_param> { - current: $first_param, - } - - impl ClearValuesStart { - pub fn $next<$first_param>(self, next: $first_param) -> $next<$first_param> { - $next { - current: next, - } - } - } - - impl<$first_param> $next<$first_param> - where $first_param: Into - { - #[inline] - fn convert_clear_values(self) -> iter::Once { - // FIXME: check format - iter::once(self.current.into()) - } - } - - ordered_passes_renderpass!{[] __impl_clear_values__ [$next] [$first_param] [$($rest: $rest_load),*] [$($rest_params),*]} - }; - - ([] __impl_clear_values__ [] [] [$next:ident: $other:ident $(, $rest:ident: $rest_load:ident)*] [$first_param:ident, $($rest_params:ident),*]) => { - ordered_passes_renderpass!{[] __impl_clear_values__ [] [] [$($rest: $rest_load),*] [$first_param, $($rest_params),*]} - }; - - ([] __impl_clear_values__ [$prev:ident] [$($prev_params:ident),*] [] [$($params:ident),*]) => { - unsafe impl<$($prev_params),*> RenderPassDescClearValues<$prev<$($prev_params),*>> for CustomRenderPassDesc - where $($prev_params: Into),* - { - #[inline] - fn convert_clear_values(&self, values: $prev<$($prev_params,)*>) -> Box> { - Box::new(values.convert_clear_values()) - } - } - }; - - ([] __impl_clear_values__ [$prev:ident] [$($prev_params:ident),*] [$next:ident: Clear $(, $rest:ident: $rest_load:ident)*] [$first_param:ident, $($rest_params:ident),*]) => { - pub struct $next<$($prev_params,)* $first_param> { - prev: $prev<$($prev_params,)*>, - current: $first_param, - } - - impl<$($prev_params,)*> $prev<$($prev_params,)*> { - pub fn $next<$first_param>(self, next: $first_param) -> $next<$($prev_params,)* $first_param> { - $next { - prev: self, - current: next, - } - } - } - - impl<$($prev_params,)* $first_param> $next<$($prev_params,)* $first_param> - where $first_param: Into - $(, $prev_params: Into)* - { - #[inline] - fn convert_clear_values(self) -> Box> { - // TODO: subopptimal iterator - let prev = self.prev.convert_clear_values(); - // FIXME: check format - Box::new(prev.chain(iter::once(self.current.into()))) - } - } - - ordered_passes_renderpass!{[] __impl_clear_values__ [$next] [$($prev_params,)* $first_param] [$($rest: $rest_load),*] [$($rest_params),*]} - }; - - ([] __impl_clear_values__ [$prev:ident] [$($prev_params:ident),*] [$next:ident: $other:ident $(, $rest:ident: $rest_load:ident)*] [$first_param:ident, $($rest_params:ident),*]) => { - ordered_passes_renderpass!{[] __impl_clear_values__ [$prev] [$($prev_params,)*] [$($rest: $rest_load),*] [$first_param, $($rest_params),*]} - }; } diff --git a/vulkano/src/framebuffer/mod.rs b/vulkano/src/framebuffer/mod.rs index 819b5e96..77315782 100644 --- a/vulkano/src/framebuffer/mod.rs +++ b/vulkano/src/framebuffer/mod.rs @@ -91,6 +91,8 @@ //! pub use self::attachments_list::AttachmentsList; +pub use self::compat_atch::ensure_image_view_compatible; +pub use self::compat_atch::IncompatibleRenderPassAttachmentError; pub use self::desc::LayoutAttachmentDescription; pub use self::desc::LayoutPassDescription; pub use self::desc::LayoutPassDependencyDescription; @@ -102,6 +104,7 @@ pub use self::desc::StoreOp; pub use self::desc::LoadOp; pub use self::empty::EmptySinglePassRenderPassDesc; pub use self::framebuffer::Framebuffer; +pub use self::framebuffer::FramebufferBuilder; pub use self::framebuffer::FramebufferCreationError; pub use self::framebuffer::FramebufferSys; pub use self::sys::RenderPass; @@ -110,7 +113,6 @@ pub use self::sys::RenderPassSys; pub use self::traits::FramebufferAbstract; pub use self::traits::RenderPassDescClearValues; pub use self::traits::RenderPassCompatible; -pub use self::traits::RenderPassDescAttachmentsList; pub use self::traits::RenderPassAbstract; pub use self::traits::RenderPassSubpassInterface; pub use self::traits::Subpass; @@ -118,6 +120,7 @@ pub use self::traits::Subpass; #[macro_use] mod macros; mod attachments_list; +mod compat_atch; mod desc; mod empty; mod framebuffer; diff --git a/vulkano/src/framebuffer/sys.rs b/vulkano/src/framebuffer/sys.rs index 0fc5acad..d60e92f5 100644 --- a/vulkano/src/framebuffer/sys.rs +++ b/vulkano/src/framebuffer/sys.rs @@ -19,15 +19,12 @@ use smallvec::SmallVec; use device::Device; use device::DeviceOwned; use format::ClearValue; -use framebuffer::AttachmentsList; use framebuffer::EmptySinglePassRenderPassDesc; -use framebuffer::FramebufferCreationError; use framebuffer::LayoutAttachmentDescription; use framebuffer::LayoutPassDependencyDescription; use framebuffer::LayoutPassDescription; use framebuffer::LoadOp; use framebuffer::RenderPassDescClearValues; -use framebuffer::RenderPassDescAttachmentsList; use framebuffer::RenderPassDesc; use framebuffer::RenderPassAbstract; @@ -73,12 +70,12 @@ impl RenderPass where D: RenderPassDesc { // If the first use of an attachment in this render pass is as an input attachment, and // the attachment is not also used as a color or depth/stencil attachment in the same // subpass, then loadOp must not be VK_ATTACHMENT_LOAD_OP_CLEAR - debug_assert!(description.attachments().enumerate().all(|(atch_num, attachment)| { + debug_assert!(description.attachment_descs().enumerate().all(|(atch_num, attachment)| { if attachment.load != LoadOp::Clear { return true; } - for p in description.subpasses() { + for p in description.subpass_descs() { if p.color_attachments.iter().find(|&&(a, _)| a == atch_num).is_some() { return true; } if let Some((a, _)) = p.depth_stencil { if a == atch_num { return true; } } if p.input_attachments.iter().find(|&&(a, _)| a == atch_num).is_some() { return false; } @@ -87,7 +84,7 @@ impl RenderPass where D: RenderPassDesc { true })); - let attachments = description.attachments().map(|attachment| { + let attachments = description.attachment_descs().map(|attachment| { debug_assert!(attachment.samples.is_power_of_two()); vk::AttachmentDescription { @@ -109,7 +106,7 @@ impl RenderPass where D: RenderPassDesc { // This block allocates, for each pass, in order, all color attachment references, then all // input attachment references, then all resolve attachment references, then the depth // stencil attachment reference. - let attachment_references = description.subpasses().flat_map(|pass| { + let attachment_references = description.subpass_descs().flat_map(|pass| { // Performing some validation with debug asserts. debug_assert!(pass.resolve_attachments.is_empty() || pass.resolve_attachments.len() == pass.color_attachments.len()); @@ -171,7 +168,7 @@ impl RenderPass where D: RenderPassDesc { // Same as `attachment_references` but only for the preserve attachments. // This is separate because attachment references are u32s and not `vkAttachmentReference` // structs. - let preserve_attachments_references = description.subpasses().flat_map(|pass| { + let preserve_attachments_references = description.subpass_descs().flat_map(|pass| { pass.preserve_attachments.into_iter().map(|offset| offset as u32) }).collect::>(); @@ -184,7 +181,7 @@ impl RenderPass where D: RenderPassDesc { let mut preserve_ref_index = 0usize; let mut out: SmallVec<[_; 16]> = SmallVec::new(); - for pass in description.subpasses() { + for pass in description.subpass_descs() { if pass.color_attachments.len() as u32 > device.physical_device().limits().max_color_attachments() { @@ -235,7 +232,7 @@ impl RenderPass where D: RenderPassDesc { out }; - let dependencies = description.dependencies().map(|dependency| { + let dependencies = description.dependency_descs().map(|dependency| { debug_assert!(dependency.source_subpass < passes.len()); debug_assert!(dependency.destination_subpass < passes.len()); @@ -336,8 +333,8 @@ unsafe impl RenderPassDesc for RenderPass where D: RenderPassDesc { } #[inline] - fn attachment(&self, num: usize) -> Option { - self.desc.attachment(num) + fn attachment_desc(&self, num: usize) -> Option { + self.desc.attachment_desc(num) } #[inline] @@ -346,8 +343,8 @@ unsafe impl RenderPassDesc for RenderPass where D: RenderPassDesc { } #[inline] - fn subpass(&self, num: usize) -> Option { - self.desc.subpass(num) + fn subpass_desc(&self, num: usize) -> Option { + self.desc.subpass_desc(num) } #[inline] @@ -356,17 +353,8 @@ unsafe impl RenderPassDesc for RenderPass where D: RenderPassDesc { } #[inline] - fn dependency(&self, num: usize) -> Option { - self.desc.dependency(num) - } -} - -unsafe impl RenderPassDescAttachmentsList for RenderPass - where D: RenderPassDescAttachmentsList -{ - #[inline] - fn check_attachments_list(&self, atch: A) -> Result, FramebufferCreationError> { - self.desc.check_attachments_list(atch) + fn dependency_desc(&self, num: usize) -> Option { + self.desc.dependency_desc(num) } } diff --git a/vulkano/src/framebuffer/traits.rs b/vulkano/src/framebuffer/traits.rs index 415c7c0a..c0bf4620 100644 --- a/vulkano/src/framebuffer/traits.rs +++ b/vulkano/src/framebuffer/traits.rs @@ -9,8 +9,6 @@ use device::DeviceOwned; use format::ClearValue; -use framebuffer::AttachmentsList; -use framebuffer::FramebufferCreationError; use framebuffer::FramebufferSys; use framebuffer::RenderPassDesc; use framebuffer::RenderPassSys; @@ -108,40 +106,6 @@ unsafe impl RenderPassAbstract for T where T: SafeDeref, T::Target: RenderPas } } -/// Extension trait for `RenderPassDesc`. Defines which types are allowed as an attachments list. -/// -/// When the user creates a framebuffer, they need to pass a render pass object and a list of -/// attachments. In order for it to work, the render pass object must implement -/// `RenderPassDescAttachmentsList` where `A` is the type of the list of attachments. -/// -/// # Safety -/// -/// This trait is unsafe because it's the job of the implementation to check whether the -/// attachments list is correct. What needs to be checked: -/// -/// - That the attachments' format and samples count match the render pass layout. -/// - That the attachments have been created with the proper usage flags. -/// - That the attachments only expose one mipmap. -/// - That the attachments use identity components swizzling. -/// TODO: more stuff with aliasing -/// -pub unsafe trait RenderPassDescAttachmentsList { - /// Decodes a `A` into a list of attachments. - /// - /// Checks that the attachments match the render pass, and returns a list. Returns an error if - /// one of the attachments is wrong. - fn check_attachments_list(&self, A) -> Result, FramebufferCreationError>; -} - -unsafe impl RenderPassDescAttachmentsList for T - where T: SafeDeref, T::Target: RenderPassDescAttachmentsList -{ - #[inline] - fn check_attachments_list(&self, atch: A) -> Result, FramebufferCreationError> { - (**self).check_attachments_list(atch) - } -} - /// Extension trait for `RenderPassDesc`. Defines which types are allowed as a list of clear values. /// /// When the user enters a render pass, they need to pass a list of clear values to apply to @@ -203,7 +167,7 @@ unsafe impl RenderPassSubpassInterface for A where A: RenderPassDesc, B: ShaderInterfaceDef { fn is_compatible_with(&self, subpass: u32, other: &B) -> bool { - let pass_descr = match RenderPassDesc::subpasses(self).skip(subpass as usize).next() { + let pass_descr = match RenderPassDesc::subpass_descs(self).skip(subpass as usize).next() { Some(s) => s, None => return false, }; @@ -215,7 +179,7 @@ unsafe impl RenderPassSubpassInterface for A None => return false, }; - let attachment_desc = (&self).attachments().skip(attachment_id).next().unwrap(); + let attachment_desc = (&self).attachment_descs().skip(attachment_id).next().unwrap(); // FIXME: compare formats depending on the number of components and data type /*if attachment_desc.format != element.format { diff --git a/vulkano/src/image/attachment.rs b/vulkano/src/image/attachment.rs index ef10053b..0b5767e4 100644 --- a/vulkano/src/image/attachment.rs +++ b/vulkano/src/image/attachment.rs @@ -23,10 +23,10 @@ use image::Dimensions; use image::ImageDimensions; use image::ViewType; use image::sys::ImageCreationError; -use image::sys::Layout; +use image::ImageLayout; +use image::ImageUsage; use image::sys::UnsafeImage; use image::sys::UnsafeImageView; -use image::sys::Usage; use image::traits::ImageAccess; use image::traits::ImageClearValue; use image::traits::ImageContent; @@ -36,7 +36,8 @@ use image::traits::ImageView; use memory::pool::AllocLayout; use memory::pool::MemoryPool; use memory::pool::MemoryPoolAlloc; -use memory::pool::StdMemoryPool; +use memory::pool::StdMemoryPoolAlloc; +use sync::AccessError; use sync::Sharing; /// ImageAccess whose purpose is to be used as a framebuffer attachment. @@ -69,7 +70,7 @@ use sync::Sharing; /// // TODO: forbid reading transient images outside render passes? #[derive(Debug)] -pub struct AttachmentImage> where A: MemoryPool { +pub struct AttachmentImage { // Inner implementation. image: UnsafeImage, @@ -77,14 +78,14 @@ pub struct AttachmentImage> where A: MemoryPool { view: UnsafeImageView, // Memory used to back the image. - memory: A::Alloc, + memory: A, // Format. format: F, // Layout to use when the image is used as a framebuffer attachment. // Must be either "depth-stencil optimal" or "color optimal". - attachment_layout: Layout, + attachment_layout: ImageLayout, // Number of times this image is locked on the GPU side. gpu_lock: AtomicUsize, @@ -96,16 +97,16 @@ impl AttachmentImage { /// Returns an error if the dimensions are too large or if the backend doesn't support this /// format as a framebuffer attachment. #[inline] - pub fn new(device: &Arc, dimensions: [u32; 2], format: F) + pub fn new(device: Arc, dimensions: [u32; 2], format: F) -> Result>, ImageCreationError> where F: FormatDesc { - AttachmentImage::new_impl(device, dimensions, format, Usage::none()) + AttachmentImage::new_impl(device, dimensions, format, ImageUsage::none()) } /// Same as `new`, but lets you specify additional usages. #[inline] - pub fn with_usage(device: &Arc, dimensions: [u32; 2], format: F, usage: Usage) + pub fn with_usage(device: Arc, dimensions: [u32; 2], format: F, usage: ImageUsage) -> Result>, ImageCreationError> where F: FormatDesc { @@ -117,19 +118,19 @@ impl AttachmentImage { /// A transient image is special because its content is undefined outside of a render pass. /// This means that the implementation has the possibility to not allocate any memory for it. #[inline] - pub fn transient(device: &Arc, dimensions: [u32; 2], format: F) + pub fn transient(device: Arc, dimensions: [u32; 2], format: F) -> Result>, ImageCreationError> where F: FormatDesc { - let base_usage = Usage { + let base_usage = ImageUsage { transient_attachment: true, - .. Usage::none() + .. ImageUsage::none() }; AttachmentImage::new_impl(device, dimensions, format, base_usage) } - fn new_impl(device: &Arc, dimensions: [u32; 2], format: F, base_usage: Usage) + fn new_impl(device: Arc, dimensions: [u32; 2], format: F, base_usage: ImageUsage) -> Result>, ImageCreationError> where F: FormatDesc { @@ -143,14 +144,14 @@ impl AttachmentImage { _ => false }; - let usage = Usage { + let usage = ImageUsage { color_attachment: !is_depth, depth_stencil_attachment: is_depth, .. base_usage }; let (image, mem_reqs) = unsafe { - try!(UnsafeImage::new(device, &usage, format.format(), + try!(UnsafeImage::new(device.clone(), usage, format.format(), ImageDimensions::Dim2d { width: dimensions[0], height: dimensions[1], array_layers: 1, cubemap_compatible: false }, 1, 1, Sharing::Exclusive::>, false, false)) }; @@ -164,7 +165,7 @@ impl AttachmentImage { device_local.chain(any).next().unwrap() }; - let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty, + let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty, mem_reqs.size, mem_reqs.alignment, AllocLayout::Optimal)); debug_assert!((mem.offset() % mem_reqs.alignment) == 0); unsafe { try!(image.bind_memory(mem.memory(), mem.offset())); } @@ -178,14 +179,14 @@ impl AttachmentImage { view: view, memory: mem, format: format, - attachment_layout: if is_depth { Layout::DepthStencilAttachmentOptimal } - else { Layout::ColorAttachmentOptimal }, + attachment_layout: if is_depth { ImageLayout::DepthStencilAttachmentOptimal } + else { ImageLayout::ColorAttachmentOptimal }, gpu_lock: AtomicUsize::new(0), })) } } -impl AttachmentImage where A: MemoryPool { +impl AttachmentImage { /// Returns the dimensions of the image. #[inline] pub fn dimensions(&self) -> [u32; 2] { @@ -195,13 +196,13 @@ impl AttachmentImage where A: MemoryPool { } /// GPU access to an attachment image. -pub struct AttachmentImageAccess where A: MemoryPool { +pub struct AttachmentImageAccess { img: Arc>, // True if `try_gpu_lock` was already called on it. already_locked: AtomicBool, } -impl Clone for AttachmentImageAccess where A: MemoryPool { +impl Clone for AttachmentImageAccess { #[inline] fn clone(&self) -> AttachmentImageAccess { AttachmentImageAccess { @@ -212,8 +213,7 @@ impl Clone for AttachmentImageAccess where A: MemoryPool { } unsafe impl ImageAccess for AttachmentImageAccess - where F: 'static + Send + Sync, - A: MemoryPool + where F: 'static + Send + Sync { #[inline] fn inner(&self) -> &UnsafeImage { @@ -221,12 +221,12 @@ unsafe impl ImageAccess for AttachmentImageAccess } #[inline] - fn initial_layout_requirement(&self) -> Layout { + fn initial_layout_requirement(&self) -> ImageLayout { self.img.attachment_layout } #[inline] - fn final_layout_requirement(&self) -> Layout { + fn final_layout_requirement(&self) -> ImageLayout { self.img.attachment_layout } @@ -236,25 +236,30 @@ unsafe impl ImageAccess for AttachmentImageAccess } #[inline] - fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool { - if self.already_locked.swap(true, Ordering::SeqCst) == true { + fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> { + // FIXME: uncomment when it's working + // the problem is in the submit sync layer which locks framebuffer attachments and + // keeps them locked even after destruction + Ok(()) + /*if self.already_locked.swap(true, Ordering::SeqCst) == true { return false; } - self.img.gpu_lock.compare_and_swap(0, 1, Ordering::SeqCst) == 0 + self.img.gpu_lock.compare_and_swap(0, 1, Ordering::SeqCst) == 0*/ } #[inline] unsafe fn increase_gpu_lock(&self) { - debug_assert!(self.already_locked.load(Ordering::SeqCst)); + // FIXME: uncomment when it's working + // the problem is in the submit sync layer which locks framebuffer attachments and + // keeps them locked even after destruction + /*debug_assert!(self.already_locked.load(Ordering::SeqCst)); let val = self.img.gpu_lock.fetch_add(1, Ordering::SeqCst); - debug_assert!(val >= 1); + debug_assert!(val >= 1);*/ } } -impl Drop for AttachmentImageAccess - where A: MemoryPool -{ +impl Drop for AttachmentImageAccess { fn drop(&mut self) { if self.already_locked.load(Ordering::SeqCst) { let prev_val = self.img.gpu_lock.fetch_sub(1, Ordering::SeqCst); @@ -264,8 +269,7 @@ impl Drop for AttachmentImageAccess } unsafe impl ImageClearValue for AttachmentImageAccess - where F: FormatDesc + 'static + Send + Sync, - A: MemoryPool + where F: FormatDesc + 'static + Send + Sync { #[inline] fn decode(&self, value: F::ClearValue) -> Option { @@ -274,8 +278,7 @@ unsafe impl ImageClearValue for AttachmentImageAccess } unsafe impl ImageContent

for AttachmentImageAccess - where F: 'static + Send + Sync, - A: MemoryPool + where F: 'static + Send + Sync { #[inline] fn matches_format(&self) -> bool { @@ -284,7 +287,7 @@ unsafe impl ImageContent

for AttachmentImageAccess } unsafe impl Image for Arc> - where F: 'static + Send + Sync, A: MemoryPool + where F: 'static + Send + Sync { type Access = AttachmentImageAccess; @@ -313,7 +316,7 @@ unsafe impl Image for Arc> } unsafe impl ImageView for Arc> - where F: 'static + Send + Sync, A: MemoryPool + where F: 'static + Send + Sync { type Access = AttachmentImageAccess; @@ -327,7 +330,7 @@ unsafe impl ImageView for Arc> } unsafe impl ImageViewAccess for AttachmentImageAccess - where F: 'static + Send + Sync, A: MemoryPool + where F: 'static + Send + Sync { #[inline] fn parent(&self) -> &ImageAccess { @@ -346,23 +349,23 @@ unsafe impl ImageViewAccess for AttachmentImageAccess } #[inline] - fn descriptor_set_storage_image_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_storage_image_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] - fn descriptor_set_combined_image_sampler_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] - fn descriptor_set_sampled_image_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_sampled_image_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] - fn descriptor_set_input_attachment_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_input_attachment_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] @@ -379,18 +382,18 @@ mod tests { #[test] fn create_regular() { let (device, _) = gfx_dev_and_queue!(); - let _img = AttachmentImage::new(&device, [32, 32], Format::R8G8B8A8Unorm).unwrap(); + let _img = AttachmentImage::new(device, [32, 32], Format::R8G8B8A8Unorm).unwrap(); } #[test] fn create_transient() { let (device, _) = gfx_dev_and_queue!(); - let _img = AttachmentImage::transient(&device, [32, 32], Format::R8G8B8A8Unorm).unwrap(); + let _img = AttachmentImage::transient(device, [32, 32], Format::R8G8B8A8Unorm).unwrap(); } #[test] fn d16_unorm_always_supported() { let (device, _) = gfx_dev_and_queue!(); - let _img = AttachmentImage::new(&device, [32, 32], Format::D16Unorm).unwrap(); + let _img = AttachmentImage::new(device, [32, 32], Format::D16Unorm).unwrap(); } } diff --git a/vulkano/src/image/immutable.rs b/vulkano/src/image/immutable.rs index fe7390f5..b535ab19 100644 --- a/vulkano/src/image/immutable.rs +++ b/vulkano/src/image/immutable.rs @@ -18,10 +18,10 @@ use image::Dimensions; use image::ImageDimensions; use image::MipmapsCount; use image::sys::ImageCreationError; -use image::sys::Layout; +use image::ImageLayout; +use image::ImageUsage; use image::sys::UnsafeImage; use image::sys::UnsafeImageView; -use image::sys::Usage; use image::traits::ImageAccess; use image::traits::ImageContent; use image::traits::ImageViewAccess; @@ -32,6 +32,7 @@ use memory::pool::AllocLayout; use memory::pool::MemoryPool; use memory::pool::MemoryPoolAlloc; use memory::pool::StdMemoryPool; +use sync::AccessError; use sync::Sharing; /// Image whose purpose is to be used for read-only purposes. You can write to the image once, @@ -50,7 +51,7 @@ impl ImmutableImage { /// Builds a new immutable image. // TODO: one mipmap is probably not a great default #[inline] - pub fn new<'a, I>(device: &Arc, dimensions: Dimensions, format: F, queue_families: I) + pub fn new<'a, I>(device: Arc, dimensions: Dimensions, format: F, queue_families: I) -> Result>, ImageCreationError> where F: FormatDesc, I: IntoIterator> { @@ -58,16 +59,16 @@ impl ImmutableImage { } /// Builds a new immutable image with the given number of mipmaps. - pub fn with_mipmaps<'a, I, M>(device: &Arc, dimensions: Dimensions, format: F, + pub fn with_mipmaps<'a, I, M>(device: Arc, dimensions: Dimensions, format: F, mipmaps: M, queue_families: I) -> Result>, ImageCreationError> where F: FormatDesc, I: IntoIterator>, M: Into { - let usage = Usage { + let usage = ImageUsage { transfer_source: true, // for blits transfer_dest: true, sampled: true, - .. Usage::none() + .. ImageUsage::none() }; let queue_families = queue_families.into_iter().map(|f| f.id()) @@ -80,7 +81,7 @@ impl ImmutableImage { Sharing::Exclusive }; - try!(UnsafeImage::new(device, &usage, format.format(), dimensions.to_image_dimensions(), + try!(UnsafeImage::new(device.clone(), usage, format.format(), dimensions.to_image_dimensions(), 1, mipmaps, sharing, false, false)) }; @@ -93,7 +94,7 @@ impl ImmutableImage { device_local.chain(any).next().unwrap() }; - let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty, + let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty, mem_reqs.size, mem_reqs.alignment, AllocLayout::Optimal)); debug_assert!((mem.offset() % mem_reqs.alignment) == 0); unsafe { try!(image.bind_memory(mem.memory(), mem.offset())); } @@ -173,13 +174,13 @@ unsafe impl ImageAccess for ImmutableImage where F: 'static + Send + } #[inline] - fn initial_layout_requirement(&self) -> Layout { - Layout::ShaderReadOnlyOptimal // TODO: ? + fn initial_layout_requirement(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal // TODO: ? } #[inline] - fn final_layout_requirement(&self) -> Layout { - Layout::ShaderReadOnlyOptimal // TODO: ? + fn final_layout_requirement(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal // TODO: ? } #[inline] @@ -188,8 +189,8 @@ unsafe impl ImageAccess for ImmutableImage where F: 'static + Send + } #[inline] - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool { - true // FIXME: + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> { + Ok(()) // FIXME: } #[inline] @@ -226,23 +227,23 @@ unsafe impl ImageViewAccess for ImmutableImage } #[inline] - fn descriptor_set_storage_image_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_storage_image_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] - fn descriptor_set_combined_image_sampler_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] - fn descriptor_set_sampled_image_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_sampled_image_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] - fn descriptor_set_input_attachment_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_input_attachment_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] diff --git a/vulkano/src/image/layout.rs b/vulkano/src/image/layout.rs new file mode 100644 index 00000000..6a6d880d --- /dev/null +++ b/vulkano/src/image/layout.rs @@ -0,0 +1,42 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use vk; + +/// Layout of an image. +/// +/// > **Note**: In vulkano, image layouts are mostly a low-level detail. You can ignore them, +/// > unless you use an unsafe function that states in its documentation that you must take care of +/// > an image's layout. +/// +/// In the Vulkan API, each mipmap level of each array layer is in one of the layouts of this enum. +/// +/// Unless you use some short of high-level shortcut function, an image always starts in either +/// the `Undefined` or the `Preinitialized` layout. +/// Before you can use an image for a given purpose, you must ensure that the image in question is +/// in the layout required for that purpose. For example if you want to write data to an image, you +/// must first transition the image to the `TransferDstOptimal` layout. The `General` layout can +/// also be used as a general-purpose fit-all layout, but using it will result in slower operations. +/// +/// Transitionning between layouts can only be done through a GPU-side operation that is part of +/// a command buffer. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(u32)] +pub enum ImageLayout { + Undefined = vk::IMAGE_LAYOUT_UNDEFINED, + General = vk::IMAGE_LAYOUT_GENERAL, + ColorAttachmentOptimal = vk::IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + DepthStencilAttachmentOptimal = vk::IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + DepthStencilReadOnlyOptimal = vk::IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, + ShaderReadOnlyOptimal = vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + TransferSrcOptimal = vk::IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + TransferDstOptimal = vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + Preinitialized = vk::IMAGE_LAYOUT_PREINITIALIZED, + PresentSrc = vk::IMAGE_LAYOUT_PRESENT_SRC_KHR, +} diff --git a/vulkano/src/image/mod.rs b/vulkano/src/image/mod.rs index dee60eae..ba0ff89d 100644 --- a/vulkano/src/image/mod.rs +++ b/vulkano/src/image/mod.rs @@ -48,22 +48,24 @@ pub use self::attachment::AttachmentImage; pub use self::immutable::ImmutableImage; +pub use self::layout::ImageLayout; pub use self::storage::StorageImage; pub use self::swapchain::SwapchainImage; pub use self::sys::ImageCreationError; -pub use self::sys::Layout; -pub use self::sys::Usage; pub use self::traits::ImageAccess; pub use self::traits::ImageViewAccess; pub use self::traits::Image; pub use self::traits::ImageView; +pub use self::usage::ImageUsage; pub mod attachment; // TODO: make private pub mod immutable; // TODO: make private +mod layout; mod storage; pub mod swapchain; // TODO: make private pub mod sys; pub mod traits; +mod usage; /// Specifies how many mipmaps must be allocated. /// diff --git a/vulkano/src/image/storage.rs b/vulkano/src/image/storage.rs index 64b34e61..591d6c9a 100644 --- a/vulkano/src/image/storage.rs +++ b/vulkano/src/image/storage.rs @@ -22,10 +22,10 @@ use format::Format; use image::Dimensions; use image::ImageDimensions; use image::sys::ImageCreationError; -use image::sys::Layout; +use image::ImageLayout; +use image::ImageUsage; use image::sys::UnsafeImage; use image::sys::UnsafeImageView; -use image::sys::Usage; use image::traits::ImageAccess; use image::traits::ImageClearValue; use image::traits::ImageContent; @@ -37,6 +37,7 @@ use memory::pool::AllocLayout; use memory::pool::MemoryPool; use memory::pool::MemoryPoolAlloc; use memory::pool::StdMemoryPool; +use sync::AccessError; use sync::Sharing; /// General-purpose image in device memory. Can be used for any usage, but will be slower than a @@ -67,7 +68,7 @@ pub struct StorageImage> where A: MemoryPool { impl StorageImage { /// Creates a new image with the given dimensions and format. - pub fn new<'a, I>(device: &Arc, dimensions: Dimensions, format: F, queue_families: I) + pub fn new<'a, I>(device: Arc, dimensions: Dimensions, format: F, queue_families: I) -> Result>, ImageCreationError> where F: FormatDesc, I: IntoIterator> @@ -80,7 +81,7 @@ impl StorageImage { _ => false }; - let usage = Usage { + let usage = ImageUsage { transfer_source: true, transfer_dest: true, sampled: true, @@ -101,7 +102,7 @@ impl StorageImage { Sharing::Exclusive }; - try!(UnsafeImage::new(device, &usage, format.format(), dimensions.to_image_dimensions(), + try!(UnsafeImage::new(device.clone(), usage, format.format(), dimensions.to_image_dimensions(), 1, 1, Sharing::Exclusive::>, false, false)) }; @@ -114,7 +115,7 @@ impl StorageImage { device_local.chain(any).next().unwrap() }; - let mem = try!(MemoryPool::alloc(&Device::standard_pool(device), mem_ty, + let mem = try!(MemoryPool::alloc(&Device::standard_pool(&device), mem_ty, mem_reqs.size, mem_reqs.alignment, AllocLayout::Optimal)); debug_assert!((mem.offset() % mem_reqs.alignment) == 0); unsafe { try!(image.bind_memory(mem.memory(), mem.offset())); } @@ -190,13 +191,13 @@ unsafe impl ImageAccess for StorageImage where F: 'static + Send + S } #[inline] - fn initial_layout_requirement(&self) -> Layout { - Layout::General + fn initial_layout_requirement(&self) -> ImageLayout { + ImageLayout::General } #[inline] - fn final_layout_requirement(&self) -> Layout { - Layout::General + fn final_layout_requirement(&self) -> ImageLayout { + ImageLayout::General } #[inline] @@ -205,13 +206,13 @@ unsafe impl ImageAccess for StorageImage where F: 'static + Send + S } #[inline] - fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool { + fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> { let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst); if val == 1 { - true + Ok(()) } else { self.gpu_lock.fetch_sub(1, Ordering::SeqCst); - false + Err(AccessError::AlreadyInUse) } } @@ -259,23 +260,23 @@ unsafe impl ImageViewAccess for StorageImage } #[inline] - fn descriptor_set_storage_image_layout(&self) -> Layout { - Layout::General + fn descriptor_set_storage_image_layout(&self) -> ImageLayout { + ImageLayout::General } #[inline] - fn descriptor_set_combined_image_sampler_layout(&self) -> Layout { - Layout::General + fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout { + ImageLayout::General } #[inline] - fn descriptor_set_sampled_image_layout(&self) -> Layout { - Layout::General + fn descriptor_set_sampled_image_layout(&self) -> ImageLayout { + ImageLayout::General } #[inline] - fn descriptor_set_input_attachment_layout(&self) -> Layout { - Layout::General + fn descriptor_set_input_attachment_layout(&self) -> ImageLayout { + ImageLayout::General } #[inline] @@ -293,7 +294,7 @@ mod tests { #[test] fn create() { let (device, queue) = gfx_dev_and_queue!(); - let _img = StorageImage::new(&device, Dimensions::Dim2d { width: 32, height: 32 }, + let _img = StorageImage::new(device, Dimensions::Dim2d { width: 32, height: 32 }, Format::R8G8B8A8Unorm, Some(queue.family())).unwrap(); } } diff --git a/vulkano/src/image/swapchain.rs b/vulkano/src/image/swapchain.rs index a2b63476..83d0d835 100644 --- a/vulkano/src/image/swapchain.rs +++ b/vulkano/src/image/swapchain.rs @@ -22,10 +22,11 @@ use image::traits::ImageContent; use image::traits::ImageViewAccess; use image::traits::Image; use image::traits::ImageView; -use image::sys::Layout; +use image::ImageLayout; use image::sys::UnsafeImage; use image::sys::UnsafeImageView; use swapchain::Swapchain; +use sync::AccessError; use OomError; @@ -42,30 +43,27 @@ use OomError; /// method on the swapchain), which will have the effect of showing the content of the image to /// the screen. Once an image has been presented, it can no longer be used unless it is acquired /// again. -// TODO: #[derive(Debug)] (needs https://github.com/aturon/crossbeam/issues/62) +// TODO: #[derive(Debug)] pub struct SwapchainImage { - image: UnsafeImage, - view: UnsafeImageView, - format: Format, swapchain: Arc, - id: u32, + image_offset: usize, + view: UnsafeImageView, } impl SwapchainImage { /// Builds a `SwapchainImage` from raw components. /// /// This is an internal method that you shouldn't call. - pub unsafe fn from_raw(image: UnsafeImage, format: Format, swapchain: &Arc, id: u32) + pub unsafe fn from_raw(swapchain: Arc, id: usize) -> Result, OomError> { + let image = swapchain.raw_image(id).unwrap(); let view = try!(UnsafeImageView::raw(&image, ViewType::Dim2d, 0 .. 1, 0 .. 1)); Ok(Arc::new(SwapchainImage { - image: image, - view: view, - format: format, swapchain: swapchain.clone(), - id: id, + image_offset: id, + view: view, })) } @@ -74,49 +72,47 @@ impl SwapchainImage { /// A `SwapchainImage` is always two-dimensional. #[inline] pub fn dimensions(&self) -> [u32; 2] { - let dims = self.image.dimensions(); + let dims = self.my_image().dimensions(); [dims.width(), dims.height()] } - /// Returns the format of the image. - // TODO: return `ColorFormat` or something like this instead, for stronger typing - #[inline] - pub fn format(&self) -> Format { - self.format - } - /// Returns the swapchain this image belongs to. #[inline] pub fn swapchain(&self) -> &Arc { &self.swapchain } + + #[inline] + fn my_image(&self) -> &UnsafeImage { + self.swapchain.raw_image(self.image_offset).unwrap() + } } unsafe impl ImageAccess for SwapchainImage { #[inline] fn inner(&self) -> &UnsafeImage { - &self.image + self.my_image() } #[inline] - fn initial_layout_requirement(&self) -> Layout { - Layout::PresentSrc + fn initial_layout_requirement(&self) -> ImageLayout { + ImageLayout::PresentSrc } #[inline] - fn final_layout_requirement(&self) -> Layout { - Layout::PresentSrc + fn final_layout_requirement(&self) -> ImageLayout { + ImageLayout::PresentSrc } #[inline] fn conflict_key(&self, _: u32, _: u32, _: u32, _: u32) -> u64 { - self.image.key() + self.my_image().key() } #[inline] - fn try_gpu_lock(&self, _: bool, _: &Queue) -> bool { + fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> { // Swapchain image are only accessible after being acquired. - false + Err(AccessError::SwapchainImageAcquireOnly) } #[inline] @@ -128,7 +124,7 @@ unsafe impl ImageClearValue<::ClearValue> for SwapchainIma { #[inline] fn decode(&self, value: ::ClearValue) -> Option { - Some(self.format.decode_clear_value(value)) + Some(self.swapchain.format().decode_clear_value(value)) } } @@ -147,8 +143,8 @@ unsafe impl ImageViewAccess for SwapchainImage { #[inline] fn dimensions(&self) -> Dimensions { - let dims = self.image.dimensions(); - Dimensions::Dim2d { width: dims.width(), height: dims.height() } + let dims = self.swapchain.dimensions(); + Dimensions::Dim2d { width: dims[0], height: dims[1] } } #[inline] @@ -157,23 +153,23 @@ unsafe impl ImageViewAccess for SwapchainImage { } #[inline] - fn descriptor_set_storage_image_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_storage_image_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] - fn descriptor_set_combined_image_sampler_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] - fn descriptor_set_sampled_image_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_sampled_image_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] - fn descriptor_set_input_attachment_layout(&self) -> Layout { - Layout::ShaderReadOnlyOptimal + fn descriptor_set_input_attachment_layout(&self) -> ImageLayout { + ImageLayout::ShaderReadOnlyOptimal } #[inline] @@ -192,17 +188,17 @@ unsafe impl Image for SwapchainImage { #[inline] fn format(&self) -> Format { - self.image.format() + self.my_image().format() } #[inline] fn samples(&self) -> u32 { - self.image.samples() + self.my_image().samples() } #[inline] fn dimensions(&self) -> ImageDimensions { - self.image.dimensions() + self.my_image().dimensions() } } @@ -224,17 +220,17 @@ unsafe impl Image for Arc { #[inline] fn format(&self) -> Format { - self.image.format() + self.my_image().format() } #[inline] fn samples(&self) -> u32 { - self.image.samples() + self.my_image().samples() } #[inline] fn dimensions(&self) -> ImageDimensions { - self.image.dimensions() + self.my_image().dimensions() } } diff --git a/vulkano/src/image/sys.rs b/vulkano/src/image/sys.rs index 252bee25..ed74be4d 100644 --- a/vulkano/src/image/sys.rs +++ b/vulkano/src/image/sys.rs @@ -25,6 +25,7 @@ use device::Device; use format::Format; use format::FormatTy; use image::ImageDimensions; +use image::ImageUsage; use image::MipmapsCount; use image::ViewType; use memory::DeviceMemory; @@ -78,7 +79,7 @@ impl UnsafeImage { /// - Panics if the number of samples is 0. /// #[inline] - pub unsafe fn new<'a, Mi, I>(device: &Arc, usage: &Usage, format: Format, + pub unsafe fn new<'a, Mi, I>(device: Arc, usage: ImageUsage, format: Format, dimensions: ImageDimensions, num_samples: u32, mipmaps: Mi, sharing: Sharing, linear_tiling: bool, preinitialized_layout: bool) @@ -95,7 +96,7 @@ impl UnsafeImage { } // Non-templated version to avoid inlining and improve compile times. - unsafe fn new_impl(device: &Arc, usage: &Usage, format: Format, + unsafe fn new_impl(device: Arc, usage: ImageUsage, format: Format, dimensions: ImageDimensions, num_samples: u32, mipmaps: MipmapsCount, (sh_mode, sh_indices): (vk::SharingMode, SmallVec<[u32; 8]>), linear_tiling: bool, preinitialized_layout: bool) @@ -153,7 +154,7 @@ impl UnsafeImage { // If `transient_attachment` is true, then only `color_attachment`, // `depth_stencil_attachment` and `input_attachment` can be true as well. if usage.transient_attachment { - let u = Usage { + let u = ImageUsage { transient_attachment: false, color_attachment: false, depth_stencil_attachment: false, @@ -161,7 +162,7 @@ impl UnsafeImage { .. usage.clone() }; - if u != Usage::none() { + if u != ImageUsage::none() { return Err(ImageCreationError::UnsupportedUsage); } } @@ -451,7 +452,7 @@ impl UnsafeImage { /// Creates an image from a raw handle. The image won't be destroyed. /// /// This function is for example used at the swapchain's initialization. - pub unsafe fn from_raw(device: &Arc, handle: u64, usage: u32, format: Format, + pub unsafe fn from_raw(device: Arc, handle: u64, usage: u32, format: Format, dimensions: ImageDimensions, samples: u32, mipmaps: u32) -> UnsafeImage { @@ -972,135 +973,6 @@ impl Drop for UnsafeImageView { } } -/// Describes how an image is going to be used. This is **not** an optimization. -/// -/// If you try to use an image in a way that you didn't declare, a panic will happen. -/// -/// If `transient_attachment` is true, then only `color_attachment`, `depth_stencil_attachment` -/// and `input_attachment` can be true as well. The rest must be false or an error will be returned -/// when creating the image. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct Usage { - /// Can be used a source for transfers. Includes blits. - pub transfer_source: bool, - - /// Can be used a destination for transfers. Includes blits. - pub transfer_dest: bool, - - /// Can be sampled from a shader. - pub sampled: bool, - - /// Can be used as an image storage in a shader. - pub storage: bool, - - /// Can be attached as a color attachment to a framebuffer. - pub color_attachment: bool, - - /// Can be attached as a depth, stencil or depth-stencil attachment to a framebuffer. - pub depth_stencil_attachment: bool, - - /// Indicates that this image will only ever be used as a temporary framebuffer attachment. - /// As soon as you leave a render pass, the content of transient images becomes undefined. - /// - /// This is a hint to the Vulkan implementation that it may not need allocate any memory for - /// this image if the image can live entirely in some cache. - pub transient_attachment: bool, - - /// Can be used as an input attachment. In other words, you can draw to it in a subpass then - /// read from it in a following pass. - pub input_attachment: bool, -} - -impl Usage { - /// Builds a `Usage` with all values set to true. Note that using the returned value will - /// produce an error because of `transient_attachment` being true. - #[inline] - pub fn all() -> Usage { - Usage { - transfer_source: true, - transfer_dest: true, - sampled: true, - storage: true, - color_attachment: true, - depth_stencil_attachment: true, - transient_attachment: true, - input_attachment: true, - } - } - - /// Builds a `Usage` with all values set to false. Useful as a default value. - /// - /// # Example - /// - /// ```rust - /// use vulkano::image::Usage as ImageUsage; - /// - /// let _usage = ImageUsage { - /// transfer_dest: true, - /// sampled: true, - /// .. ImageUsage::none() - /// }; - /// ``` - #[inline] - pub fn none() -> Usage { - Usage { - transfer_source: false, - transfer_dest: false, - sampled: false, - storage: false, - color_attachment: false, - depth_stencil_attachment: false, - transient_attachment: false, - input_attachment: false, - } - } - - #[doc(hidden)] - #[inline] - pub fn to_usage_bits(&self) -> vk::ImageUsageFlagBits { - let mut result = 0; - if self.transfer_source { result |= vk::IMAGE_USAGE_TRANSFER_SRC_BIT; } - if self.transfer_dest { result |= vk::IMAGE_USAGE_TRANSFER_DST_BIT; } - if self.sampled { result |= vk::IMAGE_USAGE_SAMPLED_BIT; } - if self.storage { result |= vk::IMAGE_USAGE_STORAGE_BIT; } - if self.color_attachment { result |= vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT; } - if self.depth_stencil_attachment { result |= vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } - if self.transient_attachment { result |= vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; } - if self.input_attachment { result |= vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT; } - result - } - - #[inline] - #[doc(hidden)] - pub fn from_bits(val: u32) -> Usage { - Usage { - transfer_source: (val & vk::IMAGE_USAGE_TRANSFER_SRC_BIT) != 0, - transfer_dest: (val & vk::IMAGE_USAGE_TRANSFER_DST_BIT) != 0, - sampled: (val & vk::IMAGE_USAGE_SAMPLED_BIT) != 0, - storage: (val & vk::IMAGE_USAGE_STORAGE_BIT) != 0, - color_attachment: (val & vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0, - depth_stencil_attachment: (val & vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0, - transient_attachment: (val & vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) != 0, - input_attachment: (val & vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT) != 0, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[repr(u32)] -pub enum Layout { - Undefined = vk::IMAGE_LAYOUT_UNDEFINED, - General = vk::IMAGE_LAYOUT_GENERAL, - ColorAttachmentOptimal = vk::IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - DepthStencilAttachmentOptimal = vk::IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - DepthStencilReadOnlyOptimal = vk::IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, - ShaderReadOnlyOptimal = vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - TransferSrcOptimal = vk::IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - TransferDstOptimal = vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - Preinitialized = vk::IMAGE_LAYOUT_PREINITIALIZED, - PresentSrc = vk::IMAGE_LAYOUT_PRESENT_SRC_KHR, -} - #[cfg(test)] mod tests { use std::iter::Empty; @@ -1108,7 +980,7 @@ mod tests { use super::ImageCreationError; use super::UnsafeImage; - use super::Usage; + use super::ImageUsage; use image::ImageDimensions; use format::Format; @@ -1118,13 +990,13 @@ mod tests { fn create_sampled() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { sampled: true, - .. Usage::none() + .. ImageUsage::none() }; let (_img, _) = unsafe { - UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm, + UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm, ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1, cubemap_compatible: false }, 1, 1, Sharing::Exclusive::>, false, false) @@ -1135,14 +1007,14 @@ mod tests { fn create_transient() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { transient_attachment: true, color_attachment: true, - .. Usage::none() + .. ImageUsage::none() }; let (_img, _) = unsafe { - UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm, + UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm, ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1, cubemap_compatible: false }, 1, 1, Sharing::Exclusive::>, false, false) @@ -1153,13 +1025,13 @@ mod tests { fn zero_sample() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { sampled: true, - .. Usage::none() + .. ImageUsage::none() }; let res = unsafe { - UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm, + UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm, ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1, cubemap_compatible: false }, 0, 1, Sharing::Exclusive::>, false, false) @@ -1175,13 +1047,13 @@ mod tests { fn non_po2_sample() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { sampled: true, - .. Usage::none() + .. ImageUsage::none() }; let res = unsafe { - UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm, + UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm, ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1, cubemap_compatible: false }, 5, 1, Sharing::Exclusive::>, false, false) @@ -1197,13 +1069,13 @@ mod tests { fn zero_mipmap() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { sampled: true, - .. Usage::none() + .. ImageUsage::none() }; let res = unsafe { - UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm, + UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm, ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1, cubemap_compatible: false }, 1, 0, Sharing::Exclusive::>, false, false) @@ -1220,13 +1092,13 @@ mod tests { fn mipmaps_too_high() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { sampled: true, - .. Usage::none() + .. ImageUsage::none() }; let res = unsafe { - UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm, + UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm, ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1, cubemap_compatible: false }, 1, u32::MAX, Sharing::Exclusive::>, false, false) @@ -1245,13 +1117,13 @@ mod tests { fn shader_storage_image_multisample() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { storage: true, - .. Usage::none() + .. ImageUsage::none() }; let res = unsafe { - UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm, + UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm, ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1, cubemap_compatible: false }, 2, 1, Sharing::Exclusive::>, false, false) @@ -1268,13 +1140,13 @@ mod tests { fn compressed_not_color_attachment() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { color_attachment: true, - .. Usage::none() + .. ImageUsage::none() }; let res = unsafe { - UnsafeImage::new(&device, &usage, Format::ASTC_5x4UnormBlock, + UnsafeImage::new(device, usage, Format::ASTC_5x4UnormBlock, ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1, cubemap_compatible: false }, 1, u32::MAX, Sharing::Exclusive::>, false, false) @@ -1291,14 +1163,14 @@ mod tests { fn transient_forbidden_with_some_usages() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { transient_attachment: true, sampled: true, - .. Usage::none() + .. ImageUsage::none() }; let res = unsafe { - UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm, + UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm, ImageDimensions::Dim2d { width: 32, height: 32, array_layers: 1, cubemap_compatible: false }, 1, 1, Sharing::Exclusive::>, false, false) @@ -1314,13 +1186,13 @@ mod tests { fn cubecompatible_dims_mismatch() { let (device, _) = gfx_dev_and_queue!(); - let usage = Usage { + let usage = ImageUsage { sampled: true, - .. Usage::none() + .. ImageUsage::none() }; let res = unsafe { - UnsafeImage::new(&device, &usage, Format::R8G8B8A8Unorm, + UnsafeImage::new(device, usage, Format::R8G8B8A8Unorm, ImageDimensions::Dim2d { width: 32, height: 64, array_layers: 1, cubemap_compatible: true }, 1, 1, Sharing::Exclusive::>, false, false) diff --git a/vulkano/src/image/traits.rs b/vulkano/src/image/traits.rs index b1056430..36b5075d 100644 --- a/vulkano/src/image/traits.rs +++ b/vulkano/src/image/traits.rs @@ -19,10 +19,11 @@ use format::PossibleStencilFormatDesc; use format::PossibleDepthStencilFormatDesc; use image::Dimensions; use image::ImageDimensions; -use image::sys::Layout; +use image::ImageLayout; use image::sys::UnsafeImage; use image::sys::UnsafeImageView; use sampler::Sampler; +use sync::AccessError; use SafeDeref; use VulkanObject; @@ -110,10 +111,10 @@ pub unsafe trait ImageAccess { } /// Returns the layout that the image has when it is first used in a primary command buffer. - fn initial_layout_requirement(&self) -> Layout; + fn initial_layout_requirement(&self) -> ImageLayout; /// Returns the layout that the image must be returned to before the end of the command buffer. - fn final_layout_requirement(&self) -> Layout; + fn final_layout_requirement(&self) -> ImageLayout; /// Wraps around this `ImageAccess` and returns an identical `ImageAccess` but whose initial /// layout requirement is either `Undefined` or `Preinitialized`. @@ -208,7 +209,7 @@ pub unsafe trait ImageAccess { /// The only way to know that the GPU has stopped accessing a queue is when the image object /// gets destroyed. Therefore you are encouraged to use temporary objects or handles (similar /// to a lock) in order to represent a GPU access. - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool; + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError>; /// Locks the resource for usage on the GPU. Supposes that the resource is already locked, and /// simply increases the lock by one. @@ -224,12 +225,12 @@ unsafe impl ImageAccess for T where T: SafeDeref, T::Target: ImageAccess { } #[inline] - fn initial_layout_requirement(&self) -> Layout { + fn initial_layout_requirement(&self) -> ImageLayout { (**self).initial_layout_requirement() } #[inline] - fn final_layout_requirement(&self) -> Layout { + fn final_layout_requirement(&self) -> ImageLayout { (**self).final_layout_requirement() } @@ -241,7 +242,7 @@ unsafe impl ImageAccess for T where T: SafeDeref, T::Target: ImageAccess { } #[inline] - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool { + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> { (**self).try_gpu_lock(exclusive_access, queue) } @@ -268,16 +269,16 @@ unsafe impl ImageAccess for ImageAccessFromUndefinedLayout } #[inline] - fn initial_layout_requirement(&self) -> Layout { + fn initial_layout_requirement(&self) -> ImageLayout { if self.preinitialized { - Layout::Preinitialized + ImageLayout::Preinitialized } else { - Layout::Undefined + ImageLayout::Undefined } } #[inline] - fn final_layout_requirement(&self) -> Layout { + fn final_layout_requirement(&self) -> ImageLayout { self.image.final_layout_requirement() } @@ -289,7 +290,7 @@ unsafe impl ImageAccess for ImageAccessFromUndefinedLayout } #[inline] - fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> bool { + fn try_gpu_lock(&self, exclusive_access: bool, queue: &Queue) -> Result<(), AccessError> { self.image.try_gpu_lock(exclusive_access, queue) } @@ -342,13 +343,13 @@ pub unsafe trait ImageViewAccess { } /// Returns the image layout to use in a descriptor with the given subresource. - fn descriptor_set_storage_image_layout(&self) -> Layout; + fn descriptor_set_storage_image_layout(&self) -> ImageLayout; /// Returns the image layout to use in a descriptor with the given subresource. - fn descriptor_set_combined_image_sampler_layout(&self) -> Layout; + fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout; /// Returns the image layout to use in a descriptor with the given subresource. - fn descriptor_set_sampled_image_layout(&self) -> Layout; + fn descriptor_set_sampled_image_layout(&self) -> ImageLayout; /// Returns the image layout to use in a descriptor with the given subresource. - fn descriptor_set_input_attachment_layout(&self) -> Layout; + fn descriptor_set_input_attachment_layout(&self) -> ImageLayout; /// Returns true if the view doesn't use components swizzling. /// @@ -383,19 +384,19 @@ unsafe impl ImageViewAccess for T where T: SafeDeref, T::Target: ImageViewAcc } #[inline] - fn descriptor_set_storage_image_layout(&self) -> Layout { + fn descriptor_set_storage_image_layout(&self) -> ImageLayout { (**self).descriptor_set_storage_image_layout() } #[inline] - fn descriptor_set_combined_image_sampler_layout(&self) -> Layout { + fn descriptor_set_combined_image_sampler_layout(&self) -> ImageLayout { (**self).descriptor_set_combined_image_sampler_layout() } #[inline] - fn descriptor_set_sampled_image_layout(&self) -> Layout { + fn descriptor_set_sampled_image_layout(&self) -> ImageLayout { (**self).descriptor_set_sampled_image_layout() } #[inline] - fn descriptor_set_input_attachment_layout(&self) -> Layout { + fn descriptor_set_input_attachment_layout(&self) -> ImageLayout { (**self).descriptor_set_input_attachment_layout() } @@ -411,5 +412,5 @@ unsafe impl ImageViewAccess for T where T: SafeDeref, T::Target: ImageViewAcc } pub unsafe trait AttachmentImageView: ImageViewAccess { - fn accept(&self, initial_layout: Layout, final_layout: Layout) -> bool; + fn accept(&self, initial_layout: ImageLayout, final_layout: ImageLayout) -> bool; } diff --git a/vulkano/src/image/usage.rs b/vulkano/src/image/usage.rs new file mode 100644 index 00000000..7c9a5aa9 --- /dev/null +++ b/vulkano/src/image/usage.rs @@ -0,0 +1,145 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::ops::BitOr; +use vk; + +/// Describes how an image is going to be used. This is **not** an optimization. +/// +/// If you try to use an image in a way that you didn't declare, a panic will happen. +/// +/// If `transient_attachment` is true, then only `color_attachment`, `depth_stencil_attachment` +/// and `input_attachment` can be true as well. The rest must be false or an error will be returned +/// when creating the image. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct ImageUsage { + /// Can be used a source for transfers. Includes blits. + pub transfer_source: bool, + + /// Can be used a destination for transfers. Includes blits. + pub transfer_dest: bool, + + /// Can be sampled from a shader. + pub sampled: bool, + + /// Can be used as an image storage in a shader. + pub storage: bool, + + /// Can be attached as a color attachment to a framebuffer. + pub color_attachment: bool, + + /// Can be attached as a depth, stencil or depth-stencil attachment to a framebuffer. + pub depth_stencil_attachment: bool, + + /// Indicates that this image will only ever be used as a temporary framebuffer attachment. + /// As soon as you leave a render pass, the content of transient images becomes undefined. + /// + /// This is a hint to the Vulkan implementation that it may not need allocate any memory for + /// this image if the image can live entirely in some cache. + pub transient_attachment: bool, + + /// Can be used as an input attachment. In other words, you can draw to it in a subpass then + /// read from it in a following pass. + pub input_attachment: bool, +} + +impl ImageUsage { + /// Builds a `ImageUsage` with all values set to true. Note that using the returned value will + /// produce an error because of `transient_attachment` being true. + #[inline] + pub fn all() -> ImageUsage { + ImageUsage { + transfer_source: true, + transfer_dest: true, + sampled: true, + storage: true, + color_attachment: true, + depth_stencil_attachment: true, + transient_attachment: true, + input_attachment: true, + } + } + + /// Builds a `ImageUsage` with all values set to false. Useful as a default value. + /// + /// # Example + /// + /// ```rust + /// use vulkano::image::ImageUsage as ImageUsage; + /// + /// let _usage = ImageUsage { + /// transfer_dest: true, + /// sampled: true, + /// .. ImageUsage::none() + /// }; + /// ``` + #[inline] + pub fn none() -> ImageUsage { + ImageUsage { + transfer_source: false, + transfer_dest: false, + sampled: false, + storage: false, + color_attachment: false, + depth_stencil_attachment: false, + transient_attachment: false, + input_attachment: false, + } + } + + // TODO: these functions shouldn't be public-hidden + #[doc(hidden)] + #[inline] + pub fn to_usage_bits(&self) -> vk::ImageUsageFlagBits { + let mut result = 0; + if self.transfer_source { result |= vk::IMAGE_USAGE_TRANSFER_SRC_BIT; } + if self.transfer_dest { result |= vk::IMAGE_USAGE_TRANSFER_DST_BIT; } + if self.sampled { result |= vk::IMAGE_USAGE_SAMPLED_BIT; } + if self.storage { result |= vk::IMAGE_USAGE_STORAGE_BIT; } + if self.color_attachment { result |= vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT; } + if self.depth_stencil_attachment { result |= vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } + if self.transient_attachment { result |= vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; } + if self.input_attachment { result |= vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT; } + result + } + + // TODO: these functions shouldn't be public-hidden + #[inline] + #[doc(hidden)] + pub fn from_bits(val: u32) -> ImageUsage { + ImageUsage { + transfer_source: (val & vk::IMAGE_USAGE_TRANSFER_SRC_BIT) != 0, + transfer_dest: (val & vk::IMAGE_USAGE_TRANSFER_DST_BIT) != 0, + sampled: (val & vk::IMAGE_USAGE_SAMPLED_BIT) != 0, + storage: (val & vk::IMAGE_USAGE_STORAGE_BIT) != 0, + color_attachment: (val & vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0, + depth_stencil_attachment: (val & vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0, + transient_attachment: (val & vk::IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) != 0, + input_attachment: (val & vk::IMAGE_USAGE_INPUT_ATTACHMENT_BIT) != 0, + } + } +} + +impl BitOr for ImageUsage { + type Output = Self; + + #[inline] + fn bitor(self, rhs: Self) -> Self { + ImageUsage { + transfer_source: self.transfer_source || rhs.transfer_source, + transfer_dest: self.transfer_dest || rhs.transfer_dest, + sampled: self.sampled || rhs.sampled, + storage: self.storage || rhs.storage, + color_attachment: self.color_attachment || rhs.color_attachment, + depth_stencil_attachment: self.depth_stencil_attachment || rhs.depth_stencil_attachment, + transient_attachment: self.transient_attachment || rhs.transient_attachment, + input_attachment: self.input_attachment || rhs.input_attachment, + } + } +} diff --git a/vulkano/src/memory/device_memory.rs b/vulkano/src/memory/device_memory.rs index 45eb94d2..e7800db3 100644 --- a/vulkano/src/memory/device_memory.rs +++ b/vulkano/src/memory/device_memory.rs @@ -417,6 +417,7 @@ mod tests { } #[test] + #[ignore] // TODO: test fails for now on Mesa+Intel fn oom_multi() { let (device, _) = gfx_dev_and_queue!(); let mem_ty = device.physical_device().memory_types().filter(|m| !m.is_lazily_allocated()) diff --git a/vulkano/src/memory/pool/host_visible.rs b/vulkano/src/memory/pool/host_visible.rs index 8925eedc..8934edeb 100644 --- a/vulkano/src/memory/pool/host_visible.rs +++ b/vulkano/src/memory/pool/host_visible.rs @@ -36,7 +36,7 @@ impl StdHostVisibleMemoryTypePool { /// - Panics if the `device` and `memory_type` don't belong to the same physical device. /// #[inline] - pub fn new(device: &Arc, memory_type: MemoryType) + pub fn new(device: Arc, memory_type: MemoryType) -> Arc { assert_eq!(&**device.physical_device().instance() as *const Instance, diff --git a/vulkano/src/memory/pool/non_host_visible.rs b/vulkano/src/memory/pool/non_host_visible.rs index 2ea80d0c..ec57a63c 100644 --- a/vulkano/src/memory/pool/non_host_visible.rs +++ b/vulkano/src/memory/pool/non_host_visible.rs @@ -35,7 +35,7 @@ impl StdNonHostVisibleMemoryTypePool { /// - Panics if the `device` and `memory_type` don't belong to the same physical device. /// #[inline] - pub fn new(device: &Arc, memory_type: MemoryType) + pub fn new(device: Arc, memory_type: MemoryType) -> Arc { assert_eq!(&**device.physical_device().instance() as *const Instance, diff --git a/vulkano/src/memory/pool/pool.rs b/vulkano/src/memory/pool/pool.rs index bf1b208a..5740005d 100644 --- a/vulkano/src/memory/pool/pool.rs +++ b/vulkano/src/memory/pool/pool.rs @@ -38,7 +38,7 @@ pub struct StdMemoryPool { impl StdMemoryPool { /// Creates a new pool. #[inline] - pub fn new(device: &Arc) -> Arc { + pub fn new(device: Arc) -> Arc { let cap = device.physical_device().memory_types().len(); let hasher = BuildHasherDefault::::default(); @@ -76,14 +76,14 @@ unsafe impl MemoryPool for Arc { Entry::Vacant(entry) => { match memory_type.is_host_visible() { true => { - let pool = StdHostVisibleMemoryTypePool::new(&self.device, memory_type); + let pool = StdHostVisibleMemoryTypePool::new(self.device.clone(), memory_type); entry.insert(Pool::HostVisible(pool.clone())); let alloc = try!(StdHostVisibleMemoryTypePool::alloc(&pool, size, alignment)); let inner = StdMemoryPoolAllocInner::HostVisible(alloc); Ok(StdMemoryPoolAlloc { inner: inner, pool: self.clone() }) }, false => { - let pool = StdNonHostVisibleMemoryTypePool::new(&self.device, memory_type); + let pool = StdNonHostVisibleMemoryTypePool::new(self.device.clone(), memory_type); entry.insert(Pool::NonHostVisible(pool.clone())); let alloc = try!(StdNonHostVisibleMemoryTypePool::alloc(&pool, size, alignment)); let inner = StdMemoryPoolAllocInner::NonHostVisible(alloc); diff --git a/vulkano/src/pipeline/cache.rs b/vulkano/src/pipeline/cache.rs index cbf7b9a8..aa002c0c 100644 --- a/vulkano/src/pipeline/cache.rs +++ b/vulkano/src/pipeline/cache.rs @@ -76,13 +76,13 @@ impl PipelineCache { /// /// let cache = if let Some(data) = data { /// // This is unsafe because there is no way to be sure that the file contains valid data. - /// unsafe { PipelineCache::with_data(&device, &data).unwrap() } + /// unsafe { PipelineCache::with_data(device.clone(), &data).unwrap() } /// } else { - /// PipelineCache::empty(&device).unwrap() + /// PipelineCache::empty(device.clone()).unwrap() /// }; /// ``` #[inline] - pub unsafe fn with_data(device: &Arc, initial_data: &[u8]) + pub unsafe fn with_data(device: Arc, initial_data: &[u8]) -> Result, OomError> { PipelineCache::new_impl(device, Some(initial_data)) @@ -97,15 +97,15 @@ impl PipelineCache { /// # use vulkano::device::Device; /// use vulkano::pipeline::cache::PipelineCache; /// # let device: Arc = return; - /// let cache = PipelineCache::empty(&device).unwrap(); + /// let cache = PipelineCache::empty(device.clone()).unwrap(); /// ``` #[inline] - pub fn empty(device: &Arc) -> Result, OomError> { + pub fn empty(device: Arc) -> Result, OomError> { unsafe { PipelineCache::new_impl(device, None) } } // Actual implementation of the constructor. - unsafe fn new_impl(device: &Arc, initial_data: Option<&[u8]>) + unsafe fn new_impl(device: Arc, initial_data: Option<&[u8]>) -> Result, OomError> { let vk = device.pointers(); @@ -232,7 +232,7 @@ mod tests { #[should_panic] fn merge_self_forbidden() { let (device, queue) = gfx_dev_and_queue!(); - let pipeline = PipelineCache::empty(&device).unwrap(); + let pipeline = PipelineCache::empty(device).unwrap(); pipeline.merge(&[&pipeline]).unwrap(); } } diff --git a/vulkano/src/pipeline/compute_pipeline.rs b/vulkano/src/pipeline/compute_pipeline.rs index f9b33b80..c48eea42 100644 --- a/vulkano/src/pipeline/compute_pipeline.rs +++ b/vulkano/src/pipeline/compute_pipeline.rs @@ -56,7 +56,7 @@ struct Inner { impl ComputePipeline<()> { /// Builds a new `ComputePipeline`. - pub fn new(device: &Arc, shader: &ComputeShaderEntryPoint, + pub fn new(device: Arc, shader: &ComputeShaderEntryPoint, specialization: &Css) -> Result>, ComputePipelineCreationError> where Csl: PipelineLayoutDescNames + Clone, @@ -64,7 +64,7 @@ impl ComputePipeline<()> { { let vk = device.pointers(); - let pipeline_layout = shader.layout().clone().build(device).unwrap(); // TODO: error + let pipeline_layout = shader.layout().clone().build(device.clone()).unwrap(); // TODO: error PipelineLayoutSuperset::ensure_superset_of(pipeline_layout.desc(), shader.layout())?; diff --git a/vulkano/src/pipeline/graphics_pipeline/mod.rs b/vulkano/src/pipeline/graphics_pipeline/mod.rs index 0a57c2c7..e7ecf745 100644 --- a/vulkano/src/pipeline/graphics_pipeline/mod.rs +++ b/vulkano/src/pipeline/graphics_pipeline/mod.rs @@ -33,14 +33,11 @@ use descriptor::pipeline_layout::PipelineLayoutNotSupersetError; use descriptor::pipeline_layout::PipelineLayoutSys; use descriptor::pipeline_layout::EmptyPipelineDesc; use format::ClearValue; -use framebuffer::AttachmentsList; use framebuffer::LayoutAttachmentDescription; use framebuffer::LayoutPassDescription; use framebuffer::LayoutPassDependencyDescription; -use framebuffer::FramebufferCreationError; use framebuffer::RenderPassAbstract; use framebuffer::RenderPassDesc; -use framebuffer::RenderPassDescAttachmentsList; use framebuffer::RenderPassDescClearValues; use framebuffer::RenderPassSubpassInterface; use framebuffer::RenderPassSys; @@ -184,7 +181,7 @@ impl GraphicsPipeline /// the other constructors for other possibilities. #[inline] pub fn new<'a, Vsp, Vi, Vo, Vl, Fs, Fi, Fo, Fl> - (device: &Arc, + (device: Arc, params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, (), (), (), EmptyPipelineDesc, (), (), (), EmptyPipelineDesc, (), (), (), EmptyPipelineDesc, Fs, Fi, Fo, Fl, Rp>) @@ -203,7 +200,7 @@ impl GraphicsPipeline let pl = params.vertex_shader.layout().clone() .union(params.fragment_shader.layout().clone()) - .build(device).unwrap(); // TODO: error + .build(device.clone()).unwrap(); // TODO: error GraphicsPipeline::new_inner::<_, _, _, _, (), (), (), EmptyPipelineDesc, (), (), (), EmptyPipelineDesc, (), (), (), EmptyPipelineDesc, _, _, _, _> @@ -219,7 +216,7 @@ impl GraphicsPipeline /// shader. See the other constructors for other possibilities. #[inline] pub fn with_geometry_shader<'a, Vsp, Vi, Vo, Vl, Gsp, Gi, Go, Gl, Fs, Fi, Fo, Fl> - (device: &Arc, + (device: Arc, params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, (), (), (), EmptyPipelineDesc, (), (), (), EmptyPipelineDesc, Gsp, Gi, Go, Gl, Fs, Fi, Fo, Fl, Rp>) @@ -252,9 +249,9 @@ impl GraphicsPipeline let pl = params.vertex_shader.layout().clone() .union(params.fragment_shader.layout().clone()) .union(params.geometry_shader.as_ref().unwrap().layout().clone()) // FIXME: unwrap() - .build(device).unwrap(); // TODO: error + .build(device.clone()).unwrap(); // TODO: error - GraphicsPipeline::new_inner(device, params, pl) + GraphicsPipeline::new_inner(device.clone(), params, pl) } /// Builds a new graphics pipeline object with tessellation shaders. @@ -268,7 +265,7 @@ impl GraphicsPipeline #[inline] pub fn with_tessellation<'a, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes, Tei, Teo, Tel, Fs, Fi, Fo, Fl> - (device: &Arc, + (device: Arc, params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes, Tei, Teo, Tel, (), (), (), EmptyPipelineDesc, Fs, Fi, Fo, Fl, Rp>) @@ -308,7 +305,7 @@ impl GraphicsPipeline .union(params.fragment_shader.layout().clone()) .union(params.tessellation.as_ref().unwrap().tessellation_control_shader.layout().clone()) // FIXME: unwrap() .union(params.tessellation.as_ref().unwrap().tessellation_evaluation_shader.layout().clone()) // FIXME: unwrap() - .build(device).unwrap(); // TODO: error + .build(device.clone()).unwrap(); // TODO: error GraphicsPipeline::new_inner(device, params, pl) } @@ -324,7 +321,7 @@ impl GraphicsPipeline #[inline] pub fn with_tessellation_and_geometry<'a, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes, Tei, Teo, Tel, Gsp, Gi, Go, Gl, Fs, Fi, Fo, Fl> - (device: &Arc, + (device: Arc, params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes, Tei, Teo, Tel, Gsp, Gi, Go, Gl, Fs, Fi, Fo, Fl, Rp>) @@ -377,7 +374,7 @@ impl GraphicsPipeline .union(params.tessellation.as_ref().unwrap().tessellation_control_shader.layout().clone()) // FIXME: unwrap() .union(params.tessellation.as_ref().unwrap().tessellation_evaluation_shader.layout().clone()) // FIXME: unwrap() .union(params.geometry_shader.as_ref().unwrap().layout().clone()) // FIXME: unwrap() - .build(device).unwrap(); // TODO: error + .build(device.clone()).unwrap(); // TODO: error GraphicsPipeline::new_inner(device, params, pl) } @@ -388,7 +385,7 @@ impl GraphicsPipeline { fn new_inner<'a, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes, Tei, Teo, Tel, Gsp, Gi, Go, Gl, Fs, Fi, Fo, Fl> - (device: &Arc, + (device: Arc, params: GraphicsPipelineParams<'a, Vdef, Vsp, Vi, Vo, Vl, Tcs, Tci, Tco, Tcl, Tes, Tei, Teo, Tel, Gsp, Gi, Go, Gl, Fs, Fi, Fo, Fl, Rp>, pipeline_layout: L) @@ -1153,8 +1150,8 @@ unsafe impl RenderPassDesc for GraphicsPipeline } #[inline] - fn attachment(&self, num: usize) -> Option { - self.render_pass.attachment(num) + fn attachment_desc(&self, num: usize) -> Option { + self.render_pass.attachment_desc(num) } #[inline] @@ -1163,8 +1160,8 @@ unsafe impl RenderPassDesc for GraphicsPipeline } #[inline] - fn subpass(&self, num: usize) -> Option { - self.render_pass.subpass(num) + fn subpass_desc(&self, num: usize) -> Option { + self.render_pass.subpass_desc(num) } #[inline] @@ -1173,17 +1170,8 @@ unsafe impl RenderPassDesc for GraphicsPipeline } #[inline] - fn dependency(&self, num: usize) -> Option { - self.render_pass.dependency(num) - } -} - -unsafe impl RenderPassDescAttachmentsList for GraphicsPipeline - where Rp: RenderPassDescAttachmentsList -{ - #[inline] - fn check_attachments_list(&self, atch: A) -> Result, FramebufferCreationError> { - self.render_pass.check_attachments_list(atch) + fn dependency_desc(&self, num: usize) -> Option { + self.render_pass.dependency_desc(num) } } diff --git a/vulkano/src/pipeline/vertex.rs b/vulkano/src/pipeline/vertex.rs deleted file mode 100644 index ce1ee30c..00000000 --- a/vulkano/src/pipeline/vertex.rs +++ /dev/null @@ -1,664 +0,0 @@ -// Copyright (c) 2016 The vulkano developers -// Licensed under the Apache License, Version 2.0 -// or the MIT -// license , -// at your option. All files in the project carrying such -// notice may not be copied, modified, or distributed except -// according to those terms. - -//! # Vertex sources definition -//! -//! When you create a graphics pipeline object, you need to pass an object which indicates the -//! layout of the vertex buffer(s) that will serve as input for the vertex shader. This is done -//! by passing an implementation of the `VertexDefinition` trait. -//! -//! In addition to this, the object that you pass when you create the graphics pipeline must also -//! implement the `VertexSource` trait. This trait has a template parameter which corresponds to the -//! list of vertex buffers. -//! -//! The vulkano library provides some structs that already implement these traits. -//! The most common situation is a single vertex buffer and no instancing, in which case you can -//! pass a `SingleBufferDefinition` when you create the pipeline. -//! -//! # Implementing `Vertex` -//! -//! The implementations of the `VertexDefinition` trait that are provided by vulkano (like -//! `SingleBufferDefinition`) require you to use a buffer whose content is `[V]` where `V` -//! implements the `Vertex` trait. -//! -//! The `Vertex` trait is unsafe, but can be implemented on a struct with the `impl_vertex!` -//! macro. -//! -//! # Example -//! -//! ```ignore // TODO: -//! # #[macro_use] extern crate vulkano -//! # fn main() { -//! # use std::sync::Arc; -//! # use vulkano::device::Device; -//! # use vulkano::device::Queue; -//! use vulkano::buffer::BufferAccess; -//! use vulkano::buffer::Usage as BufferUsage; -//! use vulkano::memory::HostVisible; -//! use vulkano::pipeline::vertex::; -//! # let device: Arc = return; -//! # let queue: Arc = return; -//! -//! struct Vertex { -//! position: [f32; 2] -//! } -//! -//! impl_vertex!(Vertex, position); -//! -//! let usage = BufferUsage { -//! vertex_buffer: true, -//! .. BufferUsage::none() -//! }; -//! -//! let vertex_buffer = BufferAccess::<[Vertex], _>::array(&device, 128, &usage, HostVisible, &queue) -//! .expect("failed to create buffer"); -//! -//! // TODO: finish example -//! # } -//! ``` - -use std::error; -use std::fmt; -use std::marker::PhantomData; -use std::mem; -use std::option::IntoIter as OptionIntoIter; -use std::sync::Arc; -use std::vec::IntoIter as VecIntoIter; - -use buffer::BufferAccess; -use buffer::BufferInner; -use buffer::TypedBufferAccess; -use format::Format; -use pipeline::shader::ShaderInterfaceDef; -use SafeDeref; -use vk; - -/// How the vertex source should be unrolled. -#[derive(Copy, Clone, Debug)] -#[repr(u32)] -pub enum InputRate { - /// Each element of the source corresponds to a vertex. - Vertex = vk::VERTEX_INPUT_RATE_VERTEX, - /// Each element of the source corresponds to an instance. - Instance = vk::VERTEX_INPUT_RATE_INSTANCE, -} - -/// Describes an individual `Vertex`. In other words a collection of attributes that can be read -/// from a vertex shader. -/// -/// At this stage, the vertex is in a "raw" format. For example a `[f32; 4]` can match both a -/// `vec4` or a `float[4]`. The way the things are binded depends on the shader. -pub unsafe trait Vertex: 'static + Send + Sync { - /// Returns the characteristics of a vertex member by its name. - fn member(name: &str) -> Option; -} - -unsafe impl Vertex for () { - #[inline] - fn member(_: &str) -> Option { - None - } -} - -/// Information about a member of a vertex struct. -pub struct VertexMemberInfo { - /// Offset of the member in bytes from the start of the struct. - pub offset: usize, - /// Type of data. This is used to check that the interface is matching. - pub ty: VertexMemberTy, - /// Number of consecutive elements of that type. - pub array_size: usize, -} - -/// Type of a member of a vertex struct. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[allow(missing_docs)] -pub enum VertexMemberTy { - I8, - U8, - I16, - U16, - I32, - U32, - F32, - F64, -} - -impl VertexMemberTy { - /// Returns true if a combination of `(type, array_size)` matches a format. - #[inline] - pub fn matches(&self, array_size: usize, format: Format, num_locs: u32) -> bool { - // TODO: implement correctly - let my_size = match *self { - VertexMemberTy::I8 => 1, - VertexMemberTy::U8 => 1, - VertexMemberTy::I16 => 2, - VertexMemberTy::U16 => 2, - VertexMemberTy::I32 => 4, - VertexMemberTy::U32 => 4, - VertexMemberTy::F32 => 4, - VertexMemberTy::F64 => 8, - }; - - let format_size = match format.size() { - None => return false, - Some(s) => s, - }; - - array_size * my_size == format_size * num_locs as usize - } -} - -/// Information about a single attribute within a vertex. -/// TODO: change that API -pub struct AttributeInfo { - /// Number of bytes between the start of a vertex and the location of attribute. - pub offset: usize, - /// VertexMember type of the attribute. - pub format: Format, -} - -/// Trait for types that describe the definition of the vertex input used by a graphics pipeline. -pub unsafe trait VertexDefinition: VertexSource>> { - /// Iterator that returns the offset, the stride (in bytes) and input rate of each buffer. - type BuffersIter: ExactSizeIterator; - /// Iterator that returns the attribute location, buffer id, and infos. - type AttribsIter: ExactSizeIterator; - - /// Builds the vertex definition to use to link this definition to a vertex shader's input - /// interface. - fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), - IncompatibleVertexDefinitionError>; -} - -unsafe impl VertexDefinition for T where T: SafeDeref, T::Target: VertexDefinition { - type BuffersIter = >::BuffersIter; - type AttribsIter = >::AttribsIter; - - #[inline] - fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), - IncompatibleVertexDefinitionError> - { - (**self).definition(interface) - } -} - -/// Error that can happen when the vertex definition doesn't match the input of the vertex shader. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum IncompatibleVertexDefinitionError { - /// An attribute of the vertex shader is missing in the vertex source. - MissingAttribute { - /// Name of the missing attribute. - attribute: String, - }, - - /// The format of an attribute does not match. - FormatMismatch { - /// Name of the attribute. - attribute: String, - /// The format in the vertex shader. - shader: (Format, usize), - /// The format in the vertex definition. - definition: (VertexMemberTy, usize), - }, -} - -impl error::Error for IncompatibleVertexDefinitionError { - #[inline] - fn description(&self) -> &str { - match *self { - IncompatibleVertexDefinitionError::MissingAttribute { .. } => "an attribute is missing", - IncompatibleVertexDefinitionError::FormatMismatch { .. } => { - "the format of an attribute does not match" - }, - } - } -} - -impl fmt::Display for IncompatibleVertexDefinitionError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "{}", error::Error::description(self)) - } -} - - -/// Extension trait of `VertexDefinition`. The `L` parameter is an acceptable vertex source for this -/// vertex definition. -pub unsafe trait VertexSource { - /// Checks and returns the list of buffers with offsets, number of vertices and number of instances. - // TODO: return error if problem - // TODO: better than a Vec - // TODO: return a struct instead - fn decode<'l>(&self, &'l L) -> (Vec>, usize, usize); -} - -unsafe impl VertexSource for T where T: SafeDeref, T::Target: VertexSource { - #[inline] - fn decode<'l>(&self, list: &'l L) -> (Vec>, usize, usize) { - (**self).decode(list) - } -} - -/// Implementation of `VertexDefinition` for a single vertex buffer. -pub struct SingleBufferDefinition(pub PhantomData); - -impl SingleBufferDefinition { - #[inline] - pub fn new() -> SingleBufferDefinition { SingleBufferDefinition(PhantomData) } -} - -unsafe impl VertexDefinition for SingleBufferDefinition - where T: Vertex, I: ShaderInterfaceDef -{ - type BuffersIter = OptionIntoIter<(u32, usize, InputRate)>; - type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>; - - fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), - IncompatibleVertexDefinitionError> - { - let attrib = { - let mut attribs = Vec::with_capacity(interface.elements().len()); - for e in interface.elements() { - let name = e.name.as_ref().unwrap(); - - let infos = match ::member(name) { - Some(m) => m, - None => return Err(IncompatibleVertexDefinitionError::MissingAttribute { - attribute: name.clone().into_owned() - }) - }; - - if !infos.ty.matches(infos.array_size, e.format, - e.location.end - e.location.start) - { - return Err(IncompatibleVertexDefinitionError::FormatMismatch { - attribute: name.clone().into_owned(), - shader: (e.format, (e.location.end - e.location.start) as usize), - definition: (infos.ty, infos.array_size), - }) - } - - let mut offset = infos.offset; - for loc in e.location.clone() { - attribs.push((loc, 0, AttributeInfo { offset: offset, format: e.format })); - offset += e.format.size().unwrap(); - } - } - attribs - }.into_iter(); // TODO: meh - - let buffers = Some((0, mem::size_of::(), InputRate::Vertex)).into_iter(); - Ok((buffers, attrib)) - } -} - -unsafe impl VertexSource>> for SingleBufferDefinition - where V: Vertex -{ - #[inline] - fn decode<'l>(&self, source: &'l Vec>) -> (Vec>, usize, usize) { - // FIXME: safety - assert_eq!(source.len(), 1); - let len = source[0].size() / mem::size_of::(); - (vec![source[0].inner()], len, 1) - } -} - -unsafe impl<'a, B, V> VertexSource for SingleBufferDefinition - where B: TypedBufferAccess, V: Vertex -{ - #[inline] - fn decode<'l>(&self, source: &'l B) -> (Vec>, usize, usize) { - (vec![source.inner()], source.len(), 1) - } -} - -/// Unstable. -// TODO: shouldn't be just `Two` but `Multi` -pub struct TwoBuffersDefinition(pub PhantomData<(T, U)>); - -impl TwoBuffersDefinition { - #[inline] - pub fn new() -> TwoBuffersDefinition { TwoBuffersDefinition(PhantomData) } -} - -unsafe impl VertexDefinition for TwoBuffersDefinition - where T: Vertex, U: Vertex, I: ShaderInterfaceDef -{ - type BuffersIter = VecIntoIter<(u32, usize, InputRate)>; - type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>; - - fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), - IncompatibleVertexDefinitionError> - { - let attrib = { - let mut attribs = Vec::with_capacity(interface.elements().len()); - for e in interface.elements() { - let name = e.name.as_ref().unwrap(); - - let (infos, buf_offset) = if let Some(infos) = ::member(name) { - (infos, 0) - } else if let Some(infos) = ::member(name) { - (infos, 1) - } else { - return Err(IncompatibleVertexDefinitionError::MissingAttribute { - attribute: name.clone().into_owned() - }); - }; - - if !infos.ty.matches(infos.array_size, e.format, - e.location.end - e.location.start) - { - return Err(IncompatibleVertexDefinitionError::FormatMismatch { - attribute: name.clone().into_owned(), - shader: (e.format, (e.location.end - e.location.start) as usize), - definition: (infos.ty, infos.array_size), - }) - } - - let mut offset = infos.offset; - for loc in e.location.clone() { - attribs.push((loc, buf_offset, AttributeInfo { offset: offset, format: e.format })); - offset += e.format.size().unwrap(); - } - } - attribs - }.into_iter(); // TODO: meh - - let buffers = vec![ - (0, mem::size_of::(), InputRate::Vertex), - (1, mem::size_of::(), InputRate::Vertex) - ].into_iter(); - - Ok((buffers, attrib)) - } -} - -unsafe impl VertexSource>> for TwoBuffersDefinition - where T: Vertex, U: Vertex -{ - #[inline] - fn decode<'l>(&self, source: &'l Vec>) -> (Vec>, usize, usize) { - unimplemented!() // FIXME: implement - } -} - -unsafe impl<'a, T, U, Bt, Bu> VertexSource<(Bt, Bu)> for TwoBuffersDefinition - where T: Vertex, Bt: TypedBufferAccess, - U: Vertex, Bu: TypedBufferAccess -{ - #[inline] - fn decode<'l>(&self, source: &'l (Bt, Bu)) -> (Vec>, usize, usize) { - let vertices = [source.0.len(), source.1.len()].iter().cloned().min().unwrap(); - (vec![source.0.inner(), source.1.inner()], vertices, 1) - } -} - -/// Unstable. -// TODO: bad way to do things -pub struct OneVertexOneInstanceDefinition(pub PhantomData<(T, U)>); - -impl OneVertexOneInstanceDefinition { - #[inline] - pub fn new() -> OneVertexOneInstanceDefinition { OneVertexOneInstanceDefinition(PhantomData) } -} - -unsafe impl VertexDefinition for OneVertexOneInstanceDefinition - where T: Vertex, U: Vertex, I: ShaderInterfaceDef -{ - type BuffersIter = VecIntoIter<(u32, usize, InputRate)>; - type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>; - - fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), - IncompatibleVertexDefinitionError> - { - let attrib = { - let mut attribs = Vec::with_capacity(interface.elements().len()); - for e in interface.elements() { - let name = e.name.as_ref().unwrap(); - - let (infos, buf_offset) = if let Some(infos) = ::member(name) { - (infos, 0) - } else if let Some(infos) = ::member(name) { - (infos, 1) - } else { - return Err(IncompatibleVertexDefinitionError::MissingAttribute { - attribute: name.clone().into_owned() - }); - }; - - if !infos.ty.matches(infos.array_size, e.format, - e.location.end - e.location.start) - { - return Err(IncompatibleVertexDefinitionError::FormatMismatch { - attribute: name.clone().into_owned(), - shader: (e.format, (e.location.end - e.location.start) as usize), - definition: (infos.ty, infos.array_size), - }) - } - - let mut offset = infos.offset; - for loc in e.location.clone() { - attribs.push((loc, buf_offset, AttributeInfo { offset: offset, format: e.format })); - offset += e.format.size().unwrap(); - } - } - attribs - }.into_iter(); // TODO: meh - - let buffers = vec![ - (0, mem::size_of::(), InputRate::Vertex), - (1, mem::size_of::(), InputRate::Instance) - ].into_iter(); - - Ok((buffers, attrib)) - } -} - -unsafe impl VertexSource>> for OneVertexOneInstanceDefinition - where T: Vertex, U: Vertex -{ - #[inline] - fn decode<'l>(&self, source: &'l Vec>) -> (Vec>, usize, usize) { - // FIXME: safety - assert_eq!(source.len(), 2); - let len = source[0].size() / mem::size_of::(); - let inst = source[0].size() / mem::size_of::(); - (vec![source[0].inner(), source[1].inner()], len, inst) - } -} - -unsafe impl<'a, T, U, Bt, Bu> VertexSource<(Bt, Bu)> for OneVertexOneInstanceDefinition - where T: Vertex, Bt: TypedBufferAccess, - U: Vertex, Bu: TypedBufferAccess -{ - #[inline] - fn decode<'l>(&self, source: &'l (Bt, Bu)) -> (Vec>, usize, usize) { - (vec![source.0.inner(), source.1.inner()], source.0.len(), source.1.len()) - } -} - -/// Implements the `Vertex` trait on a struct. -// TODO: add example -#[macro_export] -macro_rules! impl_vertex { - ($out:ident $(, $member:ident)*) => ( - #[allow(unsafe_code)] - unsafe impl $crate::pipeline::vertex::Vertex for $out { - #[inline(always)] - fn member(name: &str) -> Option<$crate::pipeline::vertex::VertexMemberInfo> { - use std::ptr; - #[allow(unused_imports)] - use $crate::format::Format; - use $crate::pipeline::vertex::VertexMemberInfo; - use $crate::pipeline::vertex::VertexMemberTy; - use $crate::pipeline::vertex::VertexMember; - - $( - if name == stringify!($member) { - let (ty, array_size) = unsafe { - #[inline] fn f(_: &T) -> (VertexMemberTy, usize) - { T::format() } - let dummy: *const $out = ptr::null(); - f(&(&*dummy).$member) - }; - - return Some(VertexMemberInfo { - offset: unsafe { - let dummy: *const $out = ptr::null(); - let member = (&(&*dummy).$member) as *const _; - member as usize - }, - - ty: ty, - array_size: array_size, - }); - } - )* - - None - } - } - ) -} - -/// Trait for data types that can be used as vertex members. Used by the `impl_vertex!` macro. -pub unsafe trait VertexMember { - /// Returns the format and array size of the member. - fn format() -> (VertexMemberTy, usize); -} - -unsafe impl VertexMember for i8 { - #[inline] - fn format() -> (VertexMemberTy, usize) { - (VertexMemberTy::I8, 1) - } -} - -unsafe impl VertexMember for u8 { - #[inline] - fn format() -> (VertexMemberTy, usize) { - (VertexMemberTy::U8, 1) - } -} - -unsafe impl VertexMember for i16 { - #[inline] - fn format() -> (VertexMemberTy, usize) { - (VertexMemberTy::I16, 1) - } -} - -unsafe impl VertexMember for u16 { - #[inline] - fn format() -> (VertexMemberTy, usize) { - (VertexMemberTy::U16, 1) - } -} - -unsafe impl VertexMember for i32 { - #[inline] - fn format() -> (VertexMemberTy, usize) { - (VertexMemberTy::I32, 1) - } -} - -unsafe impl VertexMember for u32 { - #[inline] - fn format() -> (VertexMemberTy, usize) { - (VertexMemberTy::U32, 1) - } -} - -unsafe impl VertexMember for f32 { - #[inline] - fn format() -> (VertexMemberTy, usize) { - (VertexMemberTy::F32, 1) - } -} - -unsafe impl VertexMember for f64 { - #[inline] - fn format() -> (VertexMemberTy, usize) { - (VertexMemberTy::F64, 1) - } -} - -unsafe impl VertexMember for (T,) - where T: VertexMember -{ - #[inline] - fn format() -> (VertexMemberTy, usize) { - ::format() - } -} - -unsafe impl VertexMember for (T, T) - where T: VertexMember -{ - #[inline] - fn format() -> (VertexMemberTy, usize) { - let (ty, sz) = ::format(); - (ty, sz * 2) - } -} - -unsafe impl VertexMember for (T, T, T) - where T: VertexMember -{ - #[inline] - fn format() -> (VertexMemberTy, usize) { - let (ty, sz) = ::format(); - (ty, sz * 3) - } -} - -unsafe impl VertexMember for (T, T, T, T) - where T: VertexMember -{ - #[inline] - fn format() -> (VertexMemberTy, usize) { - let (ty, sz) = ::format(); - (ty, sz * 4) - } -} - -macro_rules! impl_vm_array { - ($sz:expr) => ( - unsafe impl VertexMember for [T; $sz] - where T: VertexMember - { - #[inline] - fn format() -> (VertexMemberTy, usize) { - let (ty, sz) = ::format(); - (ty, sz * $sz) - } - } - ); -} - -impl_vm_array!(1); -impl_vm_array!(2); -impl_vm_array!(3); -impl_vm_array!(4); -impl_vm_array!(5); -impl_vm_array!(6); -impl_vm_array!(7); -impl_vm_array!(8); -impl_vm_array!(9); -impl_vm_array!(10); -impl_vm_array!(11); -impl_vm_array!(12); -impl_vm_array!(13); -impl_vm_array!(14); -impl_vm_array!(15); -impl_vm_array!(16); -impl_vm_array!(32); -impl_vm_array!(64); diff --git a/vulkano/src/pipeline/vertex/definition.rs b/vulkano/src/pipeline/vertex/definition.rs new file mode 100644 index 00000000..21ae9f04 --- /dev/null +++ b/vulkano/src/pipeline/vertex/definition.rs @@ -0,0 +1,120 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::error; +use std::fmt; +use std::sync::Arc; + +use buffer::BufferAccess; +use buffer::BufferInner; +use format::Format; +use pipeline::vertex::VertexMemberTy; +use SafeDeref; +use vk; + +/// Trait for types that describe the definition of the vertex input used by a graphics pipeline. +pub unsafe trait VertexDefinition: VertexSource>> { + /// Iterator that returns the offset, the stride (in bytes) and input rate of each buffer. + type BuffersIter: ExactSizeIterator; + /// Iterator that returns the attribute location, buffer id, and infos. + type AttribsIter: ExactSizeIterator; + + /// Builds the vertex definition to use to link this definition to a vertex shader's input + /// interface. + fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), + IncompatibleVertexDefinitionError>; +} + +unsafe impl VertexDefinition for T where T: SafeDeref, T::Target: VertexDefinition { + type BuffersIter = >::BuffersIter; + type AttribsIter = >::AttribsIter; + + #[inline] + fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), + IncompatibleVertexDefinitionError> + { + (**self).definition(interface) + } +} + +/// How the vertex source should be unrolled. +#[derive(Copy, Clone, Debug)] +#[repr(u32)] +pub enum InputRate { + /// Each element of the source corresponds to a vertex. + Vertex = vk::VERTEX_INPUT_RATE_VERTEX, + /// Each element of the source corresponds to an instance. + Instance = vk::VERTEX_INPUT_RATE_INSTANCE, +} + +/// Information about a single attribute within a vertex. +/// TODO: change that API +pub struct AttributeInfo { + /// Number of bytes between the start of a vertex and the location of attribute. + pub offset: usize, + /// VertexMember type of the attribute. + pub format: Format, +} + +/// Error that can happen when the vertex definition doesn't match the input of the vertex shader. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum IncompatibleVertexDefinitionError { + /// An attribute of the vertex shader is missing in the vertex source. + MissingAttribute { + /// Name of the missing attribute. + attribute: String, + }, + + /// The format of an attribute does not match. + FormatMismatch { + /// Name of the attribute. + attribute: String, + /// The format in the vertex shader. + shader: (Format, usize), + /// The format in the vertex definition. + definition: (VertexMemberTy, usize), + }, +} + +impl error::Error for IncompatibleVertexDefinitionError { + #[inline] + fn description(&self) -> &str { + match *self { + IncompatibleVertexDefinitionError::MissingAttribute { .. } => "an attribute is missing", + IncompatibleVertexDefinitionError::FormatMismatch { .. } => { + "the format of an attribute does not match" + }, + } + } +} + +impl fmt::Display for IncompatibleVertexDefinitionError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "{}", error::Error::description(self)) + } +} + + +/// Extension trait of `VertexDefinition`. The `L` parameter is an acceptable vertex source for this +/// vertex definition. +pub unsafe trait VertexSource { + /// Checks and returns the list of buffers with offsets, number of vertices and number of instances. + // TODO: return error if problem + // TODO: better than a Vec + // TODO: return a struct instead + fn decode<'l>(&self, &'l L) -> (Vec>, usize, usize); +} + +unsafe impl VertexSource for T where T: SafeDeref, T::Target: VertexSource { + #[inline] + fn decode<'l>(&self, list: &'l L) -> (Vec>, usize, usize) { + (**self).decode(list) + } +} diff --git a/vulkano/src/pipeline/vertex/impl_vertex.rs b/vulkano/src/pipeline/vertex/impl_vertex.rs new file mode 100644 index 00000000..da71da08 --- /dev/null +++ b/vulkano/src/pipeline/vertex/impl_vertex.rs @@ -0,0 +1,188 @@ +// Copyright (c) 2017 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use pipeline::vertex::VertexMemberTy; + +/// Implements the `Vertex` trait on a struct. +// TODO: add example +#[macro_export] +macro_rules! impl_vertex { + ($out:ident $(, $member:ident)*) => ( + #[allow(unsafe_code)] + unsafe impl $crate::pipeline::vertex::Vertex for $out { + #[inline(always)] + fn member(name: &str) -> Option<$crate::pipeline::vertex::VertexMemberInfo> { + use std::ptr; + #[allow(unused_imports)] + use $crate::format::Format; + use $crate::pipeline::vertex::VertexMemberInfo; + use $crate::pipeline::vertex::VertexMemberTy; + use $crate::pipeline::vertex::VertexMember; + + $( + if name == stringify!($member) { + let (ty, array_size) = unsafe { + #[inline] fn f(_: &T) -> (VertexMemberTy, usize) + { T::format() } + let dummy: *const $out = ptr::null(); + f(&(&*dummy).$member) + }; + + return Some(VertexMemberInfo { + offset: unsafe { + let dummy: *const $out = ptr::null(); + let member = (&(&*dummy).$member) as *const _; + member as usize + }, + + ty: ty, + array_size: array_size, + }); + } + )* + + None + } + } + ) +} + +/// Trait for data types that can be used as vertex members. Used by the `impl_vertex!` macro. +pub unsafe trait VertexMember { + /// Returns the format and array size of the member. + fn format() -> (VertexMemberTy, usize); +} + +unsafe impl VertexMember for i8 { + #[inline] + fn format() -> (VertexMemberTy, usize) { + (VertexMemberTy::I8, 1) + } +} + +unsafe impl VertexMember for u8 { + #[inline] + fn format() -> (VertexMemberTy, usize) { + (VertexMemberTy::U8, 1) + } +} + +unsafe impl VertexMember for i16 { + #[inline] + fn format() -> (VertexMemberTy, usize) { + (VertexMemberTy::I16, 1) + } +} + +unsafe impl VertexMember for u16 { + #[inline] + fn format() -> (VertexMemberTy, usize) { + (VertexMemberTy::U16, 1) + } +} + +unsafe impl VertexMember for i32 { + #[inline] + fn format() -> (VertexMemberTy, usize) { + (VertexMemberTy::I32, 1) + } +} + +unsafe impl VertexMember for u32 { + #[inline] + fn format() -> (VertexMemberTy, usize) { + (VertexMemberTy::U32, 1) + } +} + +unsafe impl VertexMember for f32 { + #[inline] + fn format() -> (VertexMemberTy, usize) { + (VertexMemberTy::F32, 1) + } +} + +unsafe impl VertexMember for f64 { + #[inline] + fn format() -> (VertexMemberTy, usize) { + (VertexMemberTy::F64, 1) + } +} + +unsafe impl VertexMember for (T,) + where T: VertexMember +{ + #[inline] + fn format() -> (VertexMemberTy, usize) { + ::format() + } +} + +unsafe impl VertexMember for (T, T) + where T: VertexMember +{ + #[inline] + fn format() -> (VertexMemberTy, usize) { + let (ty, sz) = ::format(); + (ty, sz * 2) + } +} + +unsafe impl VertexMember for (T, T, T) + where T: VertexMember +{ + #[inline] + fn format() -> (VertexMemberTy, usize) { + let (ty, sz) = ::format(); + (ty, sz * 3) + } +} + +unsafe impl VertexMember for (T, T, T, T) + where T: VertexMember +{ + #[inline] + fn format() -> (VertexMemberTy, usize) { + let (ty, sz) = ::format(); + (ty, sz * 4) + } +} + +macro_rules! impl_vm_array { + ($sz:expr) => ( + unsafe impl VertexMember for [T; $sz] + where T: VertexMember + { + #[inline] + fn format() -> (VertexMemberTy, usize) { + let (ty, sz) = ::format(); + (ty, sz * $sz) + } + } + ); +} + +impl_vm_array!(1); +impl_vm_array!(2); +impl_vm_array!(3); +impl_vm_array!(4); +impl_vm_array!(5); +impl_vm_array!(6); +impl_vm_array!(7); +impl_vm_array!(8); +impl_vm_array!(9); +impl_vm_array!(10); +impl_vm_array!(11); +impl_vm_array!(12); +impl_vm_array!(13); +impl_vm_array!(14); +impl_vm_array!(15); +impl_vm_array!(16); +impl_vm_array!(32); +impl_vm_array!(64); diff --git a/vulkano/src/pipeline/vertex/mod.rs b/vulkano/src/pipeline/vertex/mod.rs new file mode 100644 index 00000000..62c28fd5 --- /dev/null +++ b/vulkano/src/pipeline/vertex/mod.rs @@ -0,0 +1,84 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +//! # Vertex sources definition +//! +//! When you create a graphics pipeline object, you need to pass an object which indicates the +//! layout of the vertex buffer(s) that will serve as input for the vertex shader. This is done +//! by passing an implementation of the `VertexDefinition` trait. +//! +//! In addition to this, the object that you pass when you create the graphics pipeline must also +//! implement the `VertexSource` trait. This trait has a template parameter which corresponds to the +//! list of vertex buffers. +//! +//! The vulkano library provides some structs that already implement these traits. +//! The most common situation is a single vertex buffer and no instancing, in which case you can +//! pass a `SingleBufferDefinition` when you create the pipeline. +//! +//! # Implementing `Vertex` +//! +//! The implementations of the `VertexDefinition` trait that are provided by vulkano (like +//! `SingleBufferDefinition`) require you to use a buffer whose content is `[V]` where `V` +//! implements the `Vertex` trait. +//! +//! The `Vertex` trait is unsafe, but can be implemented on a struct with the `impl_vertex!` +//! macro. +//! +//! # Example +//! +//! ```ignore // TODO: +//! # #[macro_use] extern crate vulkano +//! # fn main() { +//! # use std::sync::Arc; +//! # use vulkano::device::Device; +//! # use vulkano::device::Queue; +//! use vulkano::buffer::BufferAccess; +//! use vulkano::buffer::BufferUsage; +//! use vulkano::memory::HostVisible; +//! use vulkano::pipeline::vertex::; +//! # let device: Arc = return; +//! # let queue: Arc = return; +//! +//! struct Vertex { +//! position: [f32; 2] +//! } +//! +//! impl_vertex!(Vertex, position); +//! +//! let usage = BufferUsage { +//! vertex_buffer: true, +//! .. BufferUsage::none() +//! }; +//! +//! let vertex_buffer = BufferAccess::<[Vertex], _>::array(&device, 128, &usage, HostVisible, &queue) +//! .expect("failed to create buffer"); +//! +//! // TODO: finish example +//! # } +//! ``` + +pub use self::definition::AttributeInfo; +pub use self::definition::IncompatibleVertexDefinitionError; +pub use self::definition::InputRate; +pub use self::definition::VertexDefinition; +pub use self::definition::VertexSource; +pub use self::impl_vertex::VertexMember; +pub use self::one_one::OneVertexOneInstanceDefinition; +pub use self::single::SingleBufferDefinition; +pub use self::two::TwoBuffersDefinition; +pub use self::vertex::Vertex; +pub use self::vertex::VertexMemberInfo; +pub use self::vertex::VertexMemberTy; + +mod definition; +mod impl_vertex; +mod one_one; +mod single; +mod two; +mod vertex; diff --git a/vulkano/src/pipeline/vertex/one_one.rs b/vulkano/src/pipeline/vertex/one_one.rs new file mode 100644 index 00000000..3715d48e --- /dev/null +++ b/vulkano/src/pipeline/vertex/one_one.rs @@ -0,0 +1,108 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::marker::PhantomData; +use std::mem; +use std::sync::Arc; +use std::vec::IntoIter as VecIntoIter; + +use buffer::BufferAccess; +use buffer::BufferInner; +use buffer::TypedBufferAccess; +use pipeline::shader::ShaderInterfaceDef; +use pipeline::vertex::AttributeInfo; +use pipeline::vertex::IncompatibleVertexDefinitionError; +use pipeline::vertex::InputRate; +use pipeline::vertex::Vertex; +use pipeline::vertex::VertexDefinition; +use pipeline::vertex::VertexSource; + +/// Unstable. +// TODO: bad way to do things +pub struct OneVertexOneInstanceDefinition(pub PhantomData<(T, U)>); + +impl OneVertexOneInstanceDefinition { + #[inline] + pub fn new() -> OneVertexOneInstanceDefinition { OneVertexOneInstanceDefinition(PhantomData) } +} + +unsafe impl VertexDefinition for OneVertexOneInstanceDefinition + where T: Vertex, U: Vertex, I: ShaderInterfaceDef +{ + type BuffersIter = VecIntoIter<(u32, usize, InputRate)>; + type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>; + + fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), + IncompatibleVertexDefinitionError> + { + let attrib = { + let mut attribs = Vec::with_capacity(interface.elements().len()); + for e in interface.elements() { + let name = e.name.as_ref().unwrap(); + + let (infos, buf_offset) = if let Some(infos) = ::member(name) { + (infos, 0) + } else if let Some(infos) = ::member(name) { + (infos, 1) + } else { + return Err(IncompatibleVertexDefinitionError::MissingAttribute { + attribute: name.clone().into_owned() + }); + }; + + if !infos.ty.matches(infos.array_size, e.format, + e.location.end - e.location.start) + { + return Err(IncompatibleVertexDefinitionError::FormatMismatch { + attribute: name.clone().into_owned(), + shader: (e.format, (e.location.end - e.location.start) as usize), + definition: (infos.ty, infos.array_size), + }) + } + + let mut offset = infos.offset; + for loc in e.location.clone() { + attribs.push((loc, buf_offset, AttributeInfo { offset: offset, format: e.format })); + offset += e.format.size().unwrap(); + } + } + attribs + }.into_iter(); // TODO: meh + + let buffers = vec![ + (0, mem::size_of::(), InputRate::Vertex), + (1, mem::size_of::(), InputRate::Instance) + ].into_iter(); + + Ok((buffers, attrib)) + } +} + +unsafe impl VertexSource>> for OneVertexOneInstanceDefinition + where T: Vertex, U: Vertex +{ + #[inline] + fn decode<'l>(&self, source: &'l Vec>) -> (Vec>, usize, usize) { + // FIXME: safety + assert_eq!(source.len(), 2); + let len = source[0].size() / mem::size_of::(); + let inst = source[0].size() / mem::size_of::(); + (vec![source[0].inner(), source[1].inner()], len, inst) + } +} + +unsafe impl<'a, T, U, Bt, Bu> VertexSource<(Bt, Bu)> for OneVertexOneInstanceDefinition + where T: Vertex, Bt: TypedBufferAccess, + U: Vertex, Bu: TypedBufferAccess +{ + #[inline] + fn decode<'l>(&self, source: &'l (Bt, Bu)) -> (Vec>, usize, usize) { + (vec![source.0.inner(), source.1.inner()], source.0.len(), source.1.len()) + } +} diff --git a/vulkano/src/pipeline/vertex/single.rs b/vulkano/src/pipeline/vertex/single.rs new file mode 100644 index 00000000..d1b6ba41 --- /dev/null +++ b/vulkano/src/pipeline/vertex/single.rs @@ -0,0 +1,99 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::marker::PhantomData; +use std::mem; +use std::option::IntoIter as OptionIntoIter; +use std::sync::Arc; +use std::vec::IntoIter as VecIntoIter; + +use buffer::BufferAccess; +use buffer::BufferInner; +use buffer::TypedBufferAccess; +use pipeline::shader::ShaderInterfaceDef; +use pipeline::vertex::AttributeInfo; +use pipeline::vertex::IncompatibleVertexDefinitionError; +use pipeline::vertex::InputRate; +use pipeline::vertex::Vertex; +use pipeline::vertex::VertexDefinition; +use pipeline::vertex::VertexSource; + +/// Implementation of `VertexDefinition` for a single vertex buffer. +pub struct SingleBufferDefinition(pub PhantomData); + +impl SingleBufferDefinition { + #[inline] + pub fn new() -> SingleBufferDefinition { SingleBufferDefinition(PhantomData) } +} + +unsafe impl VertexDefinition for SingleBufferDefinition + where T: Vertex, I: ShaderInterfaceDef +{ + type BuffersIter = OptionIntoIter<(u32, usize, InputRate)>; + type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>; + + fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), + IncompatibleVertexDefinitionError> + { + let attrib = { + let mut attribs = Vec::with_capacity(interface.elements().len()); + for e in interface.elements() { + let name = e.name.as_ref().unwrap(); + + let infos = match ::member(name) { + Some(m) => m, + None => return Err(IncompatibleVertexDefinitionError::MissingAttribute { + attribute: name.clone().into_owned() + }) + }; + + if !infos.ty.matches(infos.array_size, e.format, + e.location.end - e.location.start) + { + return Err(IncompatibleVertexDefinitionError::FormatMismatch { + attribute: name.clone().into_owned(), + shader: (e.format, (e.location.end - e.location.start) as usize), + definition: (infos.ty, infos.array_size), + }) + } + + let mut offset = infos.offset; + for loc in e.location.clone() { + attribs.push((loc, 0, AttributeInfo { offset: offset, format: e.format })); + offset += e.format.size().unwrap(); + } + } + attribs + }.into_iter(); // TODO: meh + + let buffers = Some((0, mem::size_of::(), InputRate::Vertex)).into_iter(); + Ok((buffers, attrib)) + } +} + +unsafe impl VertexSource>> for SingleBufferDefinition + where V: Vertex +{ + #[inline] + fn decode<'l>(&self, source: &'l Vec>) -> (Vec>, usize, usize) { + // FIXME: safety + assert_eq!(source.len(), 1); + let len = source[0].size() / mem::size_of::(); + (vec![source[0].inner()], len, 1) + } +} + +unsafe impl<'a, B, V> VertexSource for SingleBufferDefinition + where B: TypedBufferAccess, V: Vertex +{ + #[inline] + fn decode<'l>(&self, source: &'l B) -> (Vec>, usize, usize) { + (vec![source.inner()], source.len(), 1) + } +} diff --git a/vulkano/src/pipeline/vertex/two.rs b/vulkano/src/pipeline/vertex/two.rs new file mode 100644 index 00000000..2306e4fc --- /dev/null +++ b/vulkano/src/pipeline/vertex/two.rs @@ -0,0 +1,105 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::marker::PhantomData; +use std::mem; +use std::sync::Arc; +use std::vec::IntoIter as VecIntoIter; + +use buffer::BufferAccess; +use buffer::BufferInner; +use buffer::TypedBufferAccess; +use pipeline::shader::ShaderInterfaceDef; +use pipeline::vertex::AttributeInfo; +use pipeline::vertex::IncompatibleVertexDefinitionError; +use pipeline::vertex::InputRate; +use pipeline::vertex::Vertex; +use pipeline::vertex::VertexDefinition; +use pipeline::vertex::VertexSource; + +/// Unstable. +// TODO: shouldn't be just `Two` but `Multi` +pub struct TwoBuffersDefinition(pub PhantomData<(T, U)>); + +impl TwoBuffersDefinition { + #[inline] + pub fn new() -> TwoBuffersDefinition { TwoBuffersDefinition(PhantomData) } +} + +unsafe impl VertexDefinition for TwoBuffersDefinition + where T: Vertex, U: Vertex, I: ShaderInterfaceDef +{ + type BuffersIter = VecIntoIter<(u32, usize, InputRate)>; + type AttribsIter = VecIntoIter<(u32, u32, AttributeInfo)>; + + fn definition(&self, interface: &I) -> Result<(Self::BuffersIter, Self::AttribsIter), + IncompatibleVertexDefinitionError> + { + let attrib = { + let mut attribs = Vec::with_capacity(interface.elements().len()); + for e in interface.elements() { + let name = e.name.as_ref().unwrap(); + + let (infos, buf_offset) = if let Some(infos) = ::member(name) { + (infos, 0) + } else if let Some(infos) = ::member(name) { + (infos, 1) + } else { + return Err(IncompatibleVertexDefinitionError::MissingAttribute { + attribute: name.clone().into_owned() + }); + }; + + if !infos.ty.matches(infos.array_size, e.format, + e.location.end - e.location.start) + { + return Err(IncompatibleVertexDefinitionError::FormatMismatch { + attribute: name.clone().into_owned(), + shader: (e.format, (e.location.end - e.location.start) as usize), + definition: (infos.ty, infos.array_size), + }) + } + + let mut offset = infos.offset; + for loc in e.location.clone() { + attribs.push((loc, buf_offset, AttributeInfo { offset: offset, format: e.format })); + offset += e.format.size().unwrap(); + } + } + attribs + }.into_iter(); // TODO: meh + + let buffers = vec![ + (0, mem::size_of::(), InputRate::Vertex), + (1, mem::size_of::(), InputRate::Vertex) + ].into_iter(); + + Ok((buffers, attrib)) + } +} + +unsafe impl VertexSource>> for TwoBuffersDefinition + where T: Vertex, U: Vertex +{ + #[inline] + fn decode<'l>(&self, source: &'l Vec>) -> (Vec>, usize, usize) { + unimplemented!() // FIXME: implement + } +} + +unsafe impl<'a, T, U, Bt, Bu> VertexSource<(Bt, Bu)> for TwoBuffersDefinition + where T: Vertex, Bt: TypedBufferAccess, + U: Vertex, Bu: TypedBufferAccess +{ + #[inline] + fn decode<'l>(&self, source: &'l (Bt, Bu)) -> (Vec>, usize, usize) { + let vertices = [source.0.len(), source.1.len()].iter().cloned().min().unwrap(); + (vec![source.0.inner(), source.1.inner()], vertices, 1) + } +} diff --git a/vulkano/src/pipeline/vertex/vertex.rs b/vulkano/src/pipeline/vertex/vertex.rs new file mode 100644 index 00000000..ae8002cc --- /dev/null +++ b/vulkano/src/pipeline/vertex/vertex.rs @@ -0,0 +1,76 @@ +// Copyright (c) 2017 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use format::Format; + +/// Describes an individual `Vertex`. In other words a collection of attributes that can be read +/// from a vertex shader. +/// +/// At this stage, the vertex is in a "raw" format. For example a `[f32; 4]` can match both a +/// `vec4` or a `float[4]`. The way the things are binded depends on the shader. +pub unsafe trait Vertex: 'static + Send + Sync { + /// Returns the characteristics of a vertex member by its name. + fn member(name: &str) -> Option; +} + +unsafe impl Vertex for () { + #[inline] + fn member(_: &str) -> Option { + None + } +} + +/// Information about a member of a vertex struct. +pub struct VertexMemberInfo { + /// Offset of the member in bytes from the start of the struct. + pub offset: usize, + /// Type of data. This is used to check that the interface is matching. + pub ty: VertexMemberTy, + /// Number of consecutive elements of that type. + pub array_size: usize, +} + +/// Type of a member of a vertex struct. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[allow(missing_docs)] +pub enum VertexMemberTy { + I8, + U8, + I16, + U16, + I32, + U32, + F32, + F64, +} + +impl VertexMemberTy { + /// Returns true if a combination of `(type, array_size)` matches a format. + #[inline] + pub fn matches(&self, array_size: usize, format: Format, num_locs: u32) -> bool { + // TODO: implement correctly + let my_size = match *self { + VertexMemberTy::I8 => 1, + VertexMemberTy::U8 => 1, + VertexMemberTy::I16 => 2, + VertexMemberTy::U16 => 2, + VertexMemberTy::I32 => 4, + VertexMemberTy::U32 => 4, + VertexMemberTy::F32 => 4, + VertexMemberTy::F64 => 8, + }; + + let format_size = match format.size() { + None => return false, + Some(s) => s, + }; + + array_size * my_size == format_size * num_locs as usize + } +} diff --git a/vulkano/src/query.rs b/vulkano/src/query.rs index 514ed4f0..78d9acf7 100644 --- a/vulkano/src/query.rs +++ b/vulkano/src/query.rs @@ -241,11 +241,11 @@ pub struct OcclusionQueriesPool { impl OcclusionQueriesPool { /// See the docs of new(). - pub fn raw(device: &Arc, num_slots: u32) + pub fn raw(device: Arc, num_slots: u32) -> Result { Ok(OcclusionQueriesPool { - inner: match UnsafeQueryPool::new(device.clone(), QueryType::Occlusion, num_slots) { + inner: match UnsafeQueryPool::new(device, QueryType::Occlusion, num_slots) { Ok(q) => q, Err(QueryPoolCreationError::OomError(err)) => return Err(err), Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => { @@ -262,7 +262,7 @@ impl OcclusionQueriesPool { /// - Panics if the device or host ran out of memory. /// #[inline] - pub fn new(device: &Arc, num_slots: u32) + pub fn new(device: Arc, num_slots: u32) -> Arc { Arc::new(OcclusionQueriesPool::raw(device, num_slots).unwrap()) @@ -292,7 +292,7 @@ mod tests { #[test] fn occlusion_create() { let (device, _) = gfx_dev_and_queue!(); - let _ = OcclusionQueriesPool::new(&device, 256); + let _ = OcclusionQueriesPool::new(device, 256); } #[test] diff --git a/vulkano/src/sampler.rs b/vulkano/src/sampler.rs index b926f28b..a3803261 100644 --- a/vulkano/src/sampler.rs +++ b/vulkano/src/sampler.rs @@ -25,7 +25,7 @@ //! use vulkano::sampler::Sampler; //! //! # let device: std::sync::Arc = return; -//! let _sampler = Sampler::simple_repeat_linear_no_mipmap(&device); +//! let _sampler = Sampler::simple_repeat_linear_no_mipmap(device.clone()); //! ``` //! //! More detailed sampler creation: @@ -34,7 +34,8 @@ //! use vulkano::sampler; //! //! # let device: std::sync::Arc = return; -//! let _sampler = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear, +//! let _sampler = sampler::Sampler::new(device.clone(), sampler::Filter::Linear, +//! sampler::Filter::Linear, //! sampler::MipmapMode::Nearest, //! sampler::SamplerAddressMode::Repeat, //! sampler::SamplerAddressMode::Repeat, @@ -98,7 +99,7 @@ impl Sampler { /// - Panics if out of memory or the maximum number of samplers has exceeded. /// #[inline] - pub fn simple_repeat_linear(device: &Arc) -> Arc { + pub fn simple_repeat_linear(device: Arc) -> Arc { Sampler::new(device, Filter::Linear, Filter::Linear, MipmapMode::Linear, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 1_000.0).unwrap() @@ -114,7 +115,7 @@ impl Sampler { /// - Panics if out of memory or the maximum number of samplers has exceeded. /// #[inline] - pub fn simple_repeat_linear_no_mipmap(device: &Arc) -> Arc { + pub fn simple_repeat_linear_no_mipmap(device: Arc) -> Arc { Sampler::new(device, Filter::Linear, Filter::Linear, MipmapMode::Nearest, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 1.0).unwrap() @@ -146,7 +147,7 @@ impl Sampler { /// - Panics if `min_lod > max_lod`. /// #[inline(always)] - pub fn new(device: &Arc, mag_filter: Filter, min_filter: Filter, + pub fn new(device: Arc, mag_filter: Filter, min_filter: Filter, mipmap_mode: MipmapMode, address_u: SamplerAddressMode, address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32, max_anisotropy: f32, min_lod: f32, max_lod: f32) @@ -173,7 +174,7 @@ impl Sampler { /// Same panic reasons as `new`. /// #[inline(always)] - pub fn compare(device: &Arc, mag_filter: Filter, min_filter: Filter, + pub fn compare(device: Arc, mag_filter: Filter, min_filter: Filter, mipmap_mode: MipmapMode, address_u: SamplerAddressMode, address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32, max_anisotropy: f32, min_lod: f32, max_lod: f32, compare: Compare) @@ -183,7 +184,7 @@ impl Sampler { address_w, mip_lod_bias, max_anisotropy, min_lod, max_lod, Some(compare)) } - fn new_impl(device: &Arc, mag_filter: Filter, min_filter: Filter, + fn new_impl(device: Arc, mag_filter: Filter, min_filter: Filter, mipmap_mode: MipmapMode, address_u: SamplerAddressMode, address_v: SamplerAddressMode, address_w: SamplerAddressMode, mip_lod_bias: f32, max_anisotropy: f32, min_lod: f32, max_lod: f32, compare: Option) @@ -309,7 +310,7 @@ impl Sampler { /// /// - Panics if multiple `ClampToBorder` values are passed and the border color is different. /// - pub fn unnormalized(device: &Arc, filter: Filter, + pub fn unnormalized(device: Arc, filter: Filter, address_u: UnnormalizedSamplerAddressMode, address_v: UnnormalizedSamplerAddressMode) -> Result, SamplerCreationError> @@ -672,7 +673,7 @@ mod tests { fn create_regular() { let (device, queue) = gfx_dev_and_queue!(); - let s = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear, + let s = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear, sampler::MipmapMode::Nearest, sampler::SamplerAddressMode::Repeat, sampler::SamplerAddressMode::Repeat, @@ -686,7 +687,7 @@ mod tests { fn create_compare() { let (device, queue) = gfx_dev_and_queue!(); - let s = sampler::Sampler::compare(&device, sampler::Filter::Linear, sampler::Filter::Linear, + let s = sampler::Sampler::compare(device, sampler::Filter::Linear, sampler::Filter::Linear, sampler::MipmapMode::Nearest, sampler::SamplerAddressMode::Repeat, sampler::SamplerAddressMode::Repeat, @@ -701,7 +702,7 @@ mod tests { fn create_unnormalized() { let (device, queue) = gfx_dev_and_queue!(); - let s = sampler::Sampler::unnormalized(&device, sampler::Filter::Linear, + let s = sampler::Sampler::unnormalized(device, sampler::Filter::Linear, sampler::UnnormalizedSamplerAddressMode::ClampToEdge, sampler::UnnormalizedSamplerAddressMode::ClampToEdge) .unwrap(); @@ -713,13 +714,13 @@ mod tests { #[test] fn simple_repeat_linear() { let (device, queue) = gfx_dev_and_queue!(); - let _ = sampler::Sampler::simple_repeat_linear(&device); + let _ = sampler::Sampler::simple_repeat_linear(device); } #[test] fn simple_repeat_linear_no_mipmap() { let (device, queue) = gfx_dev_and_queue!(); - let _ = sampler::Sampler::simple_repeat_linear_no_mipmap(&device); + let _ = sampler::Sampler::simple_repeat_linear_no_mipmap(device); } #[test] @@ -727,7 +728,7 @@ mod tests { fn min_lod_inferior() { let (device, queue) = gfx_dev_and_queue!(); - let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear, + let _ = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear, sampler::MipmapMode::Nearest, sampler::SamplerAddressMode::Repeat, sampler::SamplerAddressMode::Repeat, @@ -739,7 +740,7 @@ mod tests { fn max_anisotropy() { let (device, queue) = gfx_dev_and_queue!(); - let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear, + let _ = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear, sampler::MipmapMode::Nearest, sampler::SamplerAddressMode::Repeat, sampler::SamplerAddressMode::Repeat, @@ -754,7 +755,7 @@ mod tests { let b1 = sampler::BorderColor::IntTransparentBlack; let b2 = sampler::BorderColor::FloatOpaqueWhite; - let _ = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear, + let _ = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear, sampler::MipmapMode::Nearest, sampler::SamplerAddressMode::ClampToBorder(b1), sampler::SamplerAddressMode::ClampToBorder(b2), @@ -765,7 +766,7 @@ mod tests { fn anisotropy_feature() { let (device, queue) = gfx_dev_and_queue!(); - let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear, + let r = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear, sampler::MipmapMode::Nearest, sampler::SamplerAddressMode::Repeat, sampler::SamplerAddressMode::Repeat, @@ -781,7 +782,7 @@ mod tests { fn anisotropy_limit() { let (device, queue) = gfx_dev_and_queue!(sampler_anisotropy); - let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear, + let r = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear, sampler::MipmapMode::Nearest, sampler::SamplerAddressMode::Repeat, sampler::SamplerAddressMode::Repeat, @@ -798,7 +799,7 @@ mod tests { fn mip_lod_bias_limit() { let (device, queue) = gfx_dev_and_queue!(); - let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear, + let r = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear, sampler::MipmapMode::Nearest, sampler::SamplerAddressMode::Repeat, sampler::SamplerAddressMode::Repeat, @@ -815,7 +816,7 @@ mod tests { fn sampler_mirror_clamp_to_edge_extension() { let (device, queue) = gfx_dev_and_queue!(); - let r = sampler::Sampler::new(&device, sampler::Filter::Linear, sampler::Filter::Linear, + let r = sampler::Sampler::new(device, sampler::Filter::Linear, sampler::Filter::Linear, sampler::MipmapMode::Nearest, sampler::SamplerAddressMode::MirrorClampToEdge, sampler::SamplerAddressMode::MirrorClampToEdge, diff --git a/vulkano/src/swapchain/capabilities.rs b/vulkano/src/swapchain/capabilities.rs new file mode 100644 index 00000000..3f97a5dd --- /dev/null +++ b/vulkano/src/swapchain/capabilities.rs @@ -0,0 +1,500 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use format::Format; +use image::ImageUsage; +use vk; + +/// The capabilities of a surface when used by a physical device. +/// +/// You have to match these capabilities when you create a swapchain. +#[derive(Clone, Debug)] +pub struct Capabilities { + /// Minimum number of images that must be present in the swapchain. + pub min_image_count: u32, + + /// Maximum number of images that must be present in the swapchain, or `None` if there is no + /// maximum value. Note that "no maximum" doesn't mean that you can set a very high value, as + /// you may still get out of memory errors. + pub max_image_count: Option, + + /// The current dimensions of the surface. `None` means that the surface's dimensions will + /// depend on the dimensions of the swapchain that you are going to create. + pub current_extent: Option<[u32; 2]>, + + /// Minimum width and height of a swapchain that uses this surface. + pub min_image_extent: [u32; 2], + + /// Maximum width and height of a swapchain that uses this surface. + pub max_image_extent: [u32; 2], + + /// Maximum number of image layers if you create an image array. The minimum is 1. + pub max_image_array_layers: u32, + + /// List of transforms supported for the swapchain. + pub supported_transforms: SupportedSurfaceTransforms, + + /// Current transform used by the surface. + pub current_transform: SurfaceTransform, + + /// List of composite alpha modes supports for the swapchain. + pub supported_composite_alpha: SupportedCompositeAlpha, + + /// List of image usages that are supported for images of the swapchain. Only + /// the `color_attachment` usage is guaranteed to be supported. + pub supported_usage_flags: ImageUsage, + + /// List of formats supported for the swapchain. + pub supported_formats: Vec<(Format, ColorSpace)>, // TODO: https://github.com/KhronosGroup/Vulkan-Docs/issues/207 + + /// List of present modes that are supported. `Fifo` is always guaranteed to be supported. + pub present_modes: SupportedPresentModes, +} + +/// The way presenting a swapchain is accomplished. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum PresentMode { + /// Immediately shows the image to the user. May result in visible tearing. + Immediate = vk::PRESENT_MODE_IMMEDIATE_KHR, + + /// The action of presenting an image puts it in wait. When the next vertical blanking period + /// happens, the waiting image is effectively shown to the user. If an image is presented while + /// another one is waiting, it is replaced. + Mailbox = vk::PRESENT_MODE_MAILBOX_KHR, + + /// The action of presenting an image adds it to a queue of images. At each vertical blanking + /// period, the queue is poped and an image is presented. + /// + /// Guaranteed to be always supported. + /// + /// This is the equivalent of OpenGL's `SwapInterval` with a value of 1. + Fifo = vk::PRESENT_MODE_FIFO_KHR, + + /// Same as `Fifo`, except that if the queue was empty during the previous vertical blanking + /// period then it is equivalent to `Immediate`. + /// + /// This is the equivalent of OpenGL's `SwapInterval` with a value of -1. + Relaxed = vk::PRESENT_MODE_FIFO_RELAXED_KHR, +} + +/// List of `PresentMode`s that are supported. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SupportedPresentModes { + pub immediate: bool, + pub mailbox: bool, + pub fifo: bool, + pub relaxed: bool, +} + +pub fn supported_present_modes_from_list(elem: I) -> SupportedPresentModes + where I: Iterator +{ + let mut result = SupportedPresentModes::none(); + for e in elem { + match e { + vk::PRESENT_MODE_IMMEDIATE_KHR => result.immediate = true, + vk::PRESENT_MODE_MAILBOX_KHR => result.mailbox = true, + vk::PRESENT_MODE_FIFO_KHR => result.fifo = true, + vk::PRESENT_MODE_FIFO_RELAXED_KHR => result.relaxed = true, + _ => panic!("Wrong value for vk::PresentModeKHR") + } + } + result +} + +impl SupportedPresentModes { + /// Builds a `SupportedPresentModes` with all fields set to false. + #[inline] + pub fn none() -> SupportedPresentModes { + SupportedPresentModes { + immediate: false, + mailbox: false, + fifo: false, + relaxed: false, + } + } + + /// Returns true if the given present mode is in this list of supported modes. + #[inline] + pub fn supports(&self, mode: PresentMode) -> bool { + match mode { + PresentMode::Immediate => self.immediate, + PresentMode::Mailbox => self.mailbox, + PresentMode::Fifo => self.fifo, + PresentMode::Relaxed => self.relaxed, + } + } + + /// Returns an iterator to the list of supported present modes. + #[inline] + pub fn iter(&self) -> SupportedPresentModesIter { + SupportedPresentModesIter(self.clone()) + } +} + +/// Enumeration of the `PresentMode`s that are supported. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SupportedPresentModesIter(SupportedPresentModes); + +impl Iterator for SupportedPresentModesIter { + type Item = PresentMode; + + #[inline] + fn next(&mut self) -> Option { + if self.0.immediate { self.0.immediate = false; return Some(PresentMode::Immediate); } + if self.0.mailbox { self.0.mailbox = false; return Some(PresentMode::Mailbox); } + if self.0.fifo { self.0.fifo = false; return Some(PresentMode::Fifo); } + if self.0.relaxed { self.0.relaxed = false; return Some(PresentMode::Relaxed); } + None + } +} + +/// A transformation to apply to the image before showing it on the screen. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum SurfaceTransform { + /// Don't transform the image. + Identity = vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR, + /// Rotate 90 degrees. + Rotate90 = vk::SURFACE_TRANSFORM_ROTATE_90_BIT_KHR, + /// Rotate 180 degrees. + Rotate180 = vk::SURFACE_TRANSFORM_ROTATE_180_BIT_KHR, + /// Rotate 270 degrees. + Rotate270 = vk::SURFACE_TRANSFORM_ROTATE_270_BIT_KHR, + /// Mirror the image horizontally. + HorizontalMirror = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR, + /// Mirror the image horizontally and rotate 90 degrees. + HorizontalMirrorRotate90 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR, + /// Mirror the image horizontally and rotate 180 degrees. + HorizontalMirrorRotate180 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR, + /// Mirror the image horizontally and rotate 270 degrees. + HorizontalMirrorRotate270 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR, + /// Let the operating system or driver implementation choose. + Inherit = vk::SURFACE_TRANSFORM_INHERIT_BIT_KHR, +} + +/// How the alpha values of the pixels of the window are treated. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum CompositeAlpha { + /// The alpha channel of the image is ignored. All the pixels are considered as if they have a + /// value of 1.0. + Opaque = vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + + /// The alpha channel of the image is respected. The color channels are expected to have + /// already been multiplied by the alpha value. + PreMultiplied = vk::COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + + /// The alpha channel of the image is respected. The color channels will be multiplied by the + /// alpha value by the compositor before being added to what is behind. + PostMultiplied = vk::COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, + + /// Let the operating system or driver implementation choose. + Inherit = vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR, +} + +/// List of supported composite alpha modes. +/// +/// See the docs of `CompositeAlpha`. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[allow(missing_docs)] +pub struct SupportedCompositeAlpha { + pub opaque: bool, + pub pre_multiplied: bool, + pub post_multiplied: bool, + pub inherit: bool, +} + +pub fn supported_composite_alpha_from_bits(val: u32) -> SupportedCompositeAlpha { + let mut result = SupportedCompositeAlpha::none(); + if (val & vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0 { result.opaque = true; } + if (val & vk::COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) != 0 { result.pre_multiplied = true; } + if (val & vk::COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) != 0 { result.post_multiplied = true; } + if (val & vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR) != 0 { result.inherit = true; } + result +} + +impl SupportedCompositeAlpha { + /// Builds a `SupportedCompositeAlpha` with all fields set to false. + #[inline] + pub fn none() -> SupportedCompositeAlpha { + SupportedCompositeAlpha { + opaque: false, + pre_multiplied: false, + post_multiplied: false, + inherit: false, + } + } + + /// Returns true if the given `CompositeAlpha` is in this list. + #[inline] + pub fn supports(&self, value: CompositeAlpha) -> bool { + match value { + CompositeAlpha::Opaque => self.opaque, + CompositeAlpha::PreMultiplied => self.pre_multiplied, + CompositeAlpha::PostMultiplied => self.post_multiplied, + CompositeAlpha::Inherit => self.inherit, + } + } + + /// Returns an iterator to the list of supported composite alpha. + #[inline] + pub fn iter(&self) -> SupportedCompositeAlphaIter { + SupportedCompositeAlphaIter(self.clone()) + } +} + +/// Enumeration of the `CompositeAlpha` that are supported. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SupportedCompositeAlphaIter(SupportedCompositeAlpha); + +impl Iterator for SupportedCompositeAlphaIter { + type Item = CompositeAlpha; + + #[inline] + fn next(&mut self) -> Option { + if self.0.opaque { self.0.opaque = false; return Some(CompositeAlpha::Opaque); } + if self.0.pre_multiplied { self.0.pre_multiplied = false; return Some(CompositeAlpha::PreMultiplied); } + if self.0.post_multiplied { self.0.post_multiplied = false; return Some(CompositeAlpha::PostMultiplied); } + if self.0.inherit { self.0.inherit = false; return Some(CompositeAlpha::Inherit); } + None + } +} + +/// List of supported composite alpha modes. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SupportedSurfaceTransforms { + pub identity: bool, + pub rotate90: bool, + pub rotate180: bool, + pub rotate270: bool, + pub horizontal_mirror: bool, + pub horizontal_mirror_rotate90: bool, + pub horizontal_mirror_rotate180: bool, + pub horizontal_mirror_rotate270: bool, + pub inherit: bool, +} + +pub fn surface_transforms_from_bits(val: vk::SurfaceTransformFlagsKHR) -> SupportedSurfaceTransforms { + macro_rules! v { + ($val:expr, $out:ident, $e:expr, $f:ident) => ( + if ($val & $e) != 0 { $out.$f = true; } + ); + } + + let mut result = SupportedSurfaceTransforms::none(); + v!(val, result, vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR, identity); + v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_90_BIT_KHR, rotate90); + v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_180_BIT_KHR, rotate180); + v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_270_BIT_KHR, rotate270); + v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR, horizontal_mirror); + v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR, + horizontal_mirror_rotate90); + v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR, + horizontal_mirror_rotate180); + v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR, + horizontal_mirror_rotate270); + v!(val, result, vk::SURFACE_TRANSFORM_INHERIT_BIT_KHR, inherit); + result +} + +impl SupportedSurfaceTransforms { + /// Builds a `SupportedSurfaceTransforms` with all fields set to false. + #[inline] + pub fn none() -> SupportedSurfaceTransforms { + SupportedSurfaceTransforms { + identity: false, + rotate90: false, + rotate180: false, + rotate270: false, + horizontal_mirror: false, + horizontal_mirror_rotate90: false, + horizontal_mirror_rotate180: false, + horizontal_mirror_rotate270: false, + inherit: false, + } + } + + /// Returns true if the given `SurfaceTransform` is in this list. + #[inline] + pub fn supports(&self, value: SurfaceTransform) -> bool { + match value { + SurfaceTransform::Identity => self.identity, + SurfaceTransform::Rotate90 => self.rotate90, + SurfaceTransform::Rotate180 => self.rotate180, + SurfaceTransform::Rotate270 => self.rotate270, + SurfaceTransform::HorizontalMirror => self.horizontal_mirror, + SurfaceTransform::HorizontalMirrorRotate90 => self.horizontal_mirror_rotate90, + SurfaceTransform::HorizontalMirrorRotate180 => self.horizontal_mirror_rotate180, + SurfaceTransform::HorizontalMirrorRotate270 => self.horizontal_mirror_rotate270, + SurfaceTransform::Inherit => self.inherit, + } + } + + /// Returns an iterator to the list of supported composite alpha. + #[inline] + pub fn iter(&self) -> SupportedSurfaceTransformsIter { + SupportedSurfaceTransformsIter(self.clone()) + } +} + +/// Enumeration of the `SurfaceTransform` that are supported. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct SupportedSurfaceTransformsIter(SupportedSurfaceTransforms); + +impl Iterator for SupportedSurfaceTransformsIter { + type Item = SurfaceTransform; + + #[inline] + fn next(&mut self) -> Option { + if self.0.identity { self.0.identity = false; return Some(SurfaceTransform::Identity); } + if self.0.rotate90 { self.0.rotate90 = false; return Some(SurfaceTransform::Rotate90); } + if self.0.rotate180 { self.0.rotate180 = false; return Some(SurfaceTransform::Rotate180); } + if self.0.rotate270 { self.0.rotate270 = false; return Some(SurfaceTransform::Rotate270); } + if self.0.horizontal_mirror { self.0.horizontal_mirror = false; return Some(SurfaceTransform::HorizontalMirror); } + if self.0.horizontal_mirror_rotate90 { self.0.horizontal_mirror_rotate90 = false; return Some(SurfaceTransform::HorizontalMirrorRotate90); } + if self.0.horizontal_mirror_rotate180 { self.0.horizontal_mirror_rotate180 = false; return Some(SurfaceTransform::HorizontalMirrorRotate180); } + if self.0.horizontal_mirror_rotate270 { self.0.horizontal_mirror_rotate270 = false; return Some(SurfaceTransform::HorizontalMirrorRotate270); } + if self.0.inherit { self.0.inherit = false; return Some(SurfaceTransform::Inherit); } + None + } +} + +impl Default for SurfaceTransform { + #[inline] + fn default() -> SurfaceTransform { + SurfaceTransform::Identity + } +} + +/// How the presentation engine should interpret the data. +/// +/// # A quick lesson about color spaces +/// +/// ## What is a color space? +/// +/// Each pixel of a monitor is made of three components: one red, one green, and one blue. In the +/// past, computers would simply send to the monitor the intensity of each of the three components. +/// +/// This proved to be problematic, because depending on the brand of the monitor the colors would +/// not exactly be the same. For example on some monitors, a value of `[1.0, 0.0, 0.0]` would be a +/// bit more orange than on others. +/// +/// In order to standardize this, there exist what are called *color spaces*: sRGB, AdobeRGB, +/// DCI-P3, scRGB, etc. When you manipulate RGB values in a specific color space, these values have +/// a precise absolute meaning in terms of color, that is the same across all systems and monitors. +/// +/// > **Note**: Color spaces are orthogonal to concept of RGB. *RGB* only indicates what is the +/// > representation of the data, but not how it is interpreted. You can think of this a bit like +/// > text encoding. An *RGB* value is a like a byte, in other words it is the medium by which +/// > values are communicated, and a *color space* is like a text encoding (eg. UTF-8), in other +/// > words it is the way the value should be interpreted. +/// +/// The most commonly used color space today is sRGB. Most monitors today use this color space, +/// and most images files are encoded in this color space. +/// +/// ## Pixel formats and linear vs non-linear +/// +/// In Vulkan all images have a specific format in which the data is stored. The data of an image +/// consists of pixels in RGB but contains no information about the color space (or lack thereof) +/// of these pixels. You are free to store them in whatever color space you want. +/// +/// But one big practical problem with color spaces is that they are sometimes not linear, and in +/// particular the popular sRGB color space is not linear. In a non-linear color space, a value of +/// `[0.6, 0.6, 0.6]` for example is **not** twice as bright as a value of `[0.3, 0.3, 0.3]`. This +/// is problematic, because operations such as taking the average of two colors or calculating the +/// lighting of a texture with a dot product are mathematically incorrect and will produce +/// incorrect colors. +/// +/// > **Note**: If the texture format has an alpha component, it is not affected by the color space +/// > and always behaves linearly. +/// +/// In order to solve this Vulkan also provides image formats with the `Srgb` suffix, which are +/// expected to contain RGB data in the sRGB color space. When you sample an image with such a +/// format from a shader, the implementation will automatically turn the pixel values into a linear +/// color space that is suitable for linear operations (such as additions or multiplications). +/// When you write to a framebuffer attachment with such a format, the implementation will +/// automatically perform the opposite conversion. These conversions are most of the time performed +/// by the hardware and incur no additional cost. +/// +/// ## Color space of the swapchain +/// +/// The color space that you specify when you create a swapchain is how the implementation will +/// interpret the raw data inside of the image. +/// +/// > **Note**: The implementation can choose to send the data in the swapchain image directly to +/// > the monitor, but it can also choose to write it in an intermediary buffer that is then read +/// > by the operating system or windowing system. Therefore the color space that the +/// > implementation supports is not necessarily the same as the one supported by the monitor. +/// +/// It is *your* job to ensure that the data in the swapchain image is in the color space +/// that is specified here, otherwise colors will be incorrect. +/// The implementation will never perform any additional automatic conversion after the colors have +/// been written to the swapchain image. +/// +/// # How do I handle this correctly? +/// +/// The easiest way to handle color spaces in a cross-platform program is: +/// +/// - Always request the `SrgbNonLinear` color space when creating the swapchain. +/// - Make sure that all your image files use the sRGB color space, and load them in images whose +/// format has the `Srgb` suffix. Only use non-sRGB image formats for intermediary computations +/// or to store non-color data. +/// - Swapchain images should have a format with the `Srgb` suffix. +/// +/// > **Note**: It is unclear whether the `SrgbNonLinear` color space is always supported by the +/// > the implementation or not. See https://github.com/KhronosGroup/Vulkan-Docs/issues/442. +/// +/// > **Note**: Lots of developers are confused by color spaces. You can sometimes find articles +/// > talking about gamma correction and suggestion to put your colors to the power 2.2 for +/// > example. These are all hacks and you should use the sRGB pixel formats intead. +/// +/// If you follow these three rules, then everything should render the same way on all platforms. +/// +/// Additionally you can try detect whether the implementation supports any additional color space +/// and perform a manual conversion to that color space from inside your shader. +/// +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum ColorSpace { + SrgbNonLinear = vk::COLOR_SPACE_SRGB_NONLINEAR_KHR, + DisplayP3Linear = vk::COLOR_SPACE_DISPLAY_P3_LINEAR_EXT, + DisplayP3NonLinear = vk::COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT, + ScrgbLinear = vk::COLOR_SPACE_SCRGB_LINEAR_EXT, + ScrgbNonLinear = vk::COLOR_SPACE_SCRGB_NONLINEAR_EXT, + DciP3Linear = vk::COLOR_SPACE_DCI_P3_LINEAR_EXT, + DciP3NonLinear = vk::COLOR_SPACE_DCI_P3_NONLINEAR_EXT, + Bt709Linear = vk::COLOR_SPACE_BT709_LINEAR_EXT, + Bt709NonLinear = vk::COLOR_SPACE_BT709_NONLINEAR_EXT, + Bt2020Linear = vk::COLOR_SPACE_BT2020_LINEAR_EXT, + Bt2020NonLinear = vk::COLOR_SPACE_BT2020_NONLINEAR_EXT, + AdobeRgbLinear = vk::COLOR_SPACE_ADOBERGB_LINEAR_EXT, + AdobeRgbNonLinear = vk::COLOR_SPACE_ADOBERGB_NONLINEAR_EXT, +} + +#[inline] +pub fn color_space_from_num(val: u32) -> ColorSpace { + match val { + vk::COLOR_SPACE_SRGB_NONLINEAR_KHR => ColorSpace::SrgbNonLinear, + vk::COLOR_SPACE_DISPLAY_P3_LINEAR_EXT => ColorSpace::DisplayP3Linear, + vk::COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT => ColorSpace::DisplayP3NonLinear, + vk::COLOR_SPACE_SCRGB_LINEAR_EXT => ColorSpace::ScrgbLinear, + vk::COLOR_SPACE_SCRGB_NONLINEAR_EXT => ColorSpace::ScrgbNonLinear, + vk::COLOR_SPACE_DCI_P3_LINEAR_EXT => ColorSpace::DciP3Linear, + vk::COLOR_SPACE_DCI_P3_NONLINEAR_EXT => ColorSpace::DciP3NonLinear, + vk::COLOR_SPACE_BT709_LINEAR_EXT => ColorSpace::Bt709Linear, + vk::COLOR_SPACE_BT709_NONLINEAR_EXT => ColorSpace::Bt709NonLinear, + vk::COLOR_SPACE_BT2020_LINEAR_EXT => ColorSpace::Bt2020Linear, + vk::COLOR_SPACE_BT2020_NONLINEAR_EXT => ColorSpace::Bt2020NonLinear, + vk::COLOR_SPACE_ADOBERGB_LINEAR_EXT => ColorSpace::AdobeRgbLinear, + vk::COLOR_SPACE_ADOBERGB_NONLINEAR_EXT => ColorSpace::AdobeRgbNonLinear, + _ => panic!("Wrong value for color space enum") + } +} diff --git a/vulkano/src/swapchain/display.rs b/vulkano/src/swapchain/display.rs index 6cc4ea95..45a60dcd 100644 --- a/vulkano/src/swapchain/display.rs +++ b/vulkano/src/swapchain/display.rs @@ -35,6 +35,7 @@ use std::vec::IntoIter; use instance::Instance; use instance::PhysicalDevice; +use swapchain::capabilities; use swapchain::SupportedSurfaceTransforms; use check_errors; @@ -233,7 +234,7 @@ impl Display { /// Returns the transforms supported by this display. #[inline] pub fn supported_transforms(&self) -> SupportedSurfaceTransforms { - SupportedSurfaceTransforms::from_bits(self.properties.supportedTransforms) + capabilities::surface_transforms_from_bits(self.properties.supportedTransforms) } /// Returns true if TODO. diff --git a/vulkano/src/swapchain/mod.rs b/vulkano/src/swapchain/mod.rs index 8004947c..23889bf5 100644 --- a/vulkano/src/swapchain/mod.rs +++ b/vulkano/src/swapchain/mod.rs @@ -72,10 +72,10 @@ //! }; //! //! # fn build_window() -> *const u32 { ptr::null() } -//! let window = build_window(); +//! let window = build_window(); // Third-party function, not provided by vulkano //! let _surface = unsafe { //! let hinstance: *const () = ptr::null(); // Windows-specific object -//! Surface::from_hwnd(&instance, hinstance, window).unwrap() +//! Surface::from_hwnd(instance.clone(), hinstance, window).unwrap() //! }; //! ``` //! @@ -124,7 +124,7 @@ //! Once you created a swapchain and retreived all the images that belong to it (see previous //! section), you can draw on it. This is done in three steps: //! -//! - Call `Swapchain::acquire_next_image`. This function will return the index of the image +//! - Call `swapchain::acquire_next_image`. This function will return the index of the image //! (within the list returned by `Swapchain::new`) that is available to draw, plus a future //! representing the moment when the GPU will gain access to that image. //! - Draw on that image just like you would draw to any other image (see the documentation of @@ -136,9 +136,9 @@ //! //! TODO: add example here //! loop { -//! let index = swapchain.acquire_next_image(Duration::from_millis(500)).unwrap(); +//! let index = swapchain::acquire_next_image(Duration::from_millis(500)).unwrap(); //! draw(images[index]); -//! swapchain.present(queue, index).unwrap(); +//! swapchain::present(queue, index).unwrap(); //! } //! //! ## Recreating a swapchain @@ -156,6 +156,7 @@ //! //! ``` //! # use std::time::Duration; +//! use vulkano::swapchain; //! use vulkano::swapchain::AcquireError; //! use vulkano::sync::GpuFuture; //! @@ -172,7 +173,7 @@ //! //! let (ref swapchain, ref _images) = swapchain; //! -//! let (index, acq_future) = match swapchain.acquire_next_image(Duration::from_millis(500)) { +//! let (index, acq_future) = match swapchain::acquire_next_image(swapchain.clone(), Duration::from_millis(500)) { //! Ok(r) => r, //! Err(AcquireError::OutOfDate) => { recreate_swapchain = true; continue; }, //! Err(err) => panic!("{:?}", err) @@ -191,137 +192,35 @@ //! use std::sync::atomic::AtomicBool; -use vk; -pub use self::surface::Capabilities; +pub use self::capabilities::Capabilities; +pub use self::capabilities::PresentMode; +pub use self::capabilities::SupportedPresentModes; +pub use self::capabilities::SupportedPresentModesIter; +pub use self::capabilities::SurfaceTransform; +pub use self::capabilities::CompositeAlpha; +pub use self::capabilities::SupportedCompositeAlpha; +pub use self::capabilities::SupportedCompositeAlphaIter; +pub use self::capabilities::ColorSpace; +pub use self::capabilities::SupportedSurfaceTransforms; +pub use self::capabilities::SupportedSurfaceTransformsIter; pub use self::surface::Surface; -pub use self::surface::PresentMode; -pub use self::surface::SupportedPresentModes; -pub use self::surface::SupportedPresentModesIter; -pub use self::surface::SurfaceTransform; -pub use self::surface::CompositeAlpha; -pub use self::surface::SupportedCompositeAlpha; -pub use self::surface::SupportedCompositeAlphaIter; -pub use self::surface::ColorSpace; pub use self::surface::SurfaceCreationError; pub use self::swapchain::AcquireError; pub use self::swapchain::PresentFuture; pub use self::swapchain::Swapchain; pub use self::swapchain::SwapchainAcquireFuture; +pub use self::swapchain::acquire_next_image; +pub use self::swapchain::present; +mod capabilities; pub mod display; mod surface; mod swapchain; -/// List of supported composite alpha modes. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct SupportedSurfaceTransforms { - pub identity: bool, - pub rotate90: bool, - pub rotate180: bool, - pub rotate270: bool, - pub horizontal_mirror: bool, - pub horizontal_mirror_rotate90: bool, - pub horizontal_mirror_rotate180: bool, - pub horizontal_mirror_rotate270: bool, - pub inherit: bool, -} - -impl SupportedSurfaceTransforms { - /// Builds a `SupportedSurfaceTransforms` with all fields set to false. - #[inline] - pub fn none() -> SupportedSurfaceTransforms { - SupportedSurfaceTransforms { - identity: false, - rotate90: false, - rotate180: false, - rotate270: false, - horizontal_mirror: false, - horizontal_mirror_rotate90: false, - horizontal_mirror_rotate180: false, - horizontal_mirror_rotate270: false, - inherit: false, - } - } - - #[inline] - fn from_bits(val: vk::SurfaceTransformFlagsKHR) -> SupportedSurfaceTransforms { - macro_rules! v { - ($val:expr, $out:ident, $e:expr, $f:ident) => ( - if ($val & $e) != 0 { $out.$f = true; } - ); - } - - let mut result = SupportedSurfaceTransforms::none(); - v!(val, result, vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR, identity); - v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_90_BIT_KHR, rotate90); - v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_180_BIT_KHR, rotate180); - v!(val, result, vk::SURFACE_TRANSFORM_ROTATE_270_BIT_KHR, rotate270); - v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR, horizontal_mirror); - v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR, - horizontal_mirror_rotate90); - v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR, - horizontal_mirror_rotate180); - v!(val, result, vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR, - horizontal_mirror_rotate270); - v!(val, result, vk::SURFACE_TRANSFORM_INHERIT_BIT_KHR, inherit); - result - } - - /// Returns true if the given `SurfaceTransform` is in this list. - #[inline] - pub fn supports(&self, value: SurfaceTransform) -> bool { - match value { - SurfaceTransform::Identity => self.identity, - SurfaceTransform::Rotate90 => self.rotate90, - SurfaceTransform::Rotate180 => self.rotate180, - SurfaceTransform::Rotate270 => self.rotate270, - SurfaceTransform::HorizontalMirror => self.horizontal_mirror, - SurfaceTransform::HorizontalMirrorRotate90 => self.horizontal_mirror_rotate90, - SurfaceTransform::HorizontalMirrorRotate180 => self.horizontal_mirror_rotate180, - SurfaceTransform::HorizontalMirrorRotate270 => self.horizontal_mirror_rotate270, - SurfaceTransform::Inherit => self.inherit, - } - } - - /// Returns an iterator to the list of supported composite alpha. - #[inline] - pub fn iter(&self) -> SupportedSurfaceTransformsIter { - SupportedSurfaceTransformsIter(self.clone()) - } -} - -/// Enumeration of the `SurfaceTransform` that are supported. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct SupportedSurfaceTransformsIter(SupportedSurfaceTransforms); - -impl Iterator for SupportedSurfaceTransformsIter { - type Item = SurfaceTransform; - - #[inline] - fn next(&mut self) -> Option { - if self.0.identity { self.0.identity = false; return Some(SurfaceTransform::Identity); } - if self.0.rotate90 { self.0.rotate90 = false; return Some(SurfaceTransform::Rotate90); } - if self.0.rotate180 { self.0.rotate180 = false; return Some(SurfaceTransform::Rotate180); } - if self.0.rotate270 { self.0.rotate270 = false; return Some(SurfaceTransform::Rotate270); } - if self.0.horizontal_mirror { self.0.horizontal_mirror = false; return Some(SurfaceTransform::HorizontalMirror); } - if self.0.horizontal_mirror_rotate90 { self.0.horizontal_mirror_rotate90 = false; return Some(SurfaceTransform::HorizontalMirrorRotate90); } - if self.0.horizontal_mirror_rotate180 { self.0.horizontal_mirror_rotate180 = false; return Some(SurfaceTransform::HorizontalMirrorRotate180); } - if self.0.horizontal_mirror_rotate270 { self.0.horizontal_mirror_rotate270 = false; return Some(SurfaceTransform::HorizontalMirrorRotate270); } - if self.0.inherit { self.0.inherit = false; return Some(SurfaceTransform::Inherit); } - None - } -} - -impl Default for SurfaceTransform { - #[inline] - fn default() -> SurfaceTransform { - SurfaceTransform::Identity - } -} - /// Internal trait so that creating/destroying a swapchain can access the surface's "has_swapchain" /// flag. +// TODO: use pub(crate) maybe? unsafe trait SurfaceSwapchainLock { fn flag(&self) -> &AtomicBool; } diff --git a/vulkano/src/swapchain/surface.rs b/vulkano/src/swapchain/surface.rs index 2c13ed64..995b0386 100644 --- a/vulkano/src/swapchain/surface.rs +++ b/vulkano/src/swapchain/surface.rs @@ -16,12 +16,13 @@ use std::sync::Arc; use std::sync::atomic::AtomicBool; use format::Format; -use image::Usage as ImageUsage; +use image::ImageUsage; use instance::Instance; use instance::PhysicalDevice; use instance::QueueFamily; +use swapchain::capabilities; +use swapchain::Capabilities; use swapchain::SurfaceSwapchainLock; -use swapchain::SupportedSurfaceTransforms; use swapchain::display::DisplayMode; use swapchain::display::DisplayPlane; @@ -104,7 +105,7 @@ impl Surface { /// /// The caller must ensure that the `hinstance` and the `hwnd` are both correct and stay /// alive for the entire lifetime of the surface. - pub unsafe fn from_hwnd(instance: &Arc, hinstance: *const T, hwnd: *const U) + pub unsafe fn from_hwnd(instance: Arc, hinstance: *const T, hwnd: *const U) -> Result, SurfaceCreationError> { let vk = instance.pointers(); @@ -143,7 +144,7 @@ impl Surface { /// /// The caller must ensure that the `connection` and the `window` are both correct and stay /// alive for the entire lifetime of the surface. - pub unsafe fn from_xcb(instance: &Arc, connection: *const C, window: u32) + pub unsafe fn from_xcb(instance: Arc, connection: *const C, window: u32) -> Result, SurfaceCreationError> { let vk = instance.pointers(); @@ -182,7 +183,7 @@ impl Surface { /// /// The caller must ensure that the `display` and the `window` are both correct and stay /// alive for the entire lifetime of the surface. - pub unsafe fn from_xlib(instance: &Arc, display: *const D, window: c_ulong) + pub unsafe fn from_xlib(instance: Arc, display: *const D, window: c_ulong) -> Result, SurfaceCreationError> { let vk = instance.pointers(); @@ -221,7 +222,7 @@ impl Surface { /// /// The caller must ensure that the `display` and the `surface` are both correct and stay /// alive for the entire lifetime of the surface. - pub unsafe fn from_wayland(instance: &Arc, display: *const D, surface: *const S) + pub unsafe fn from_wayland(instance: Arc, display: *const D, surface: *const S) -> Result, SurfaceCreationError> { let vk = instance.pointers(); @@ -261,7 +262,7 @@ impl Surface { /// /// The caller must ensure that the `connection` and the `surface` are both correct and stay /// alive for the entire lifetime of the surface. - pub unsafe fn from_mir(instance: &Arc, connection: *const C, surface: *const S) + pub unsafe fn from_mir(instance: Arc, connection: *const C, surface: *const S) -> Result, SurfaceCreationError> { let vk = instance.pointers(); @@ -298,7 +299,7 @@ impl Surface { /// /// The caller must ensure that the `window` is correct and stays alive for the entire /// lifetime of the surface. - pub unsafe fn from_anativewindow(instance: &Arc, window: *const T) + pub unsafe fn from_anativewindow(instance: Arc, window: *const T) -> Result, SurfaceCreationError> { let vk = instance.pointers(); @@ -408,7 +409,7 @@ impl Surface { /// /// The caller must ensure that the `window` is correct and stays alive for the entire /// lifetime of the surface. - pub unsafe fn from_vi_surface(instance: &Arc, window: *const T) + pub unsafe fn from_vi_surface(instance: Arc, window: *const T) -> Result, SurfaceCreationError> { let vk = instance.pointers(); @@ -439,7 +440,8 @@ impl Surface { } /// Returns true if the given queue family can draw on this surface. - pub fn is_supported(&self, queue: &QueueFamily) -> Result { + // FIXME: vulkano doesn't check this for the moment! + pub fn is_supported(&self, queue: QueueFamily) -> Result { unsafe { let vk = self.instance.pointers(); @@ -452,15 +454,22 @@ impl Surface { } } + #[deprecated = "Renamed to `capabilities`"] + #[inline] + pub fn get_capabilities(&self, device: &PhysicalDevice) -> Result { + Ok(self.capabilities(*device).unwrap()) + } + /// Retreives the capabilities of a surface when used by a certain device. /// /// # Panic /// /// - Panics if the device and the surface don't belong to the same instance. /// - pub fn get_capabilities(&self, device: &PhysicalDevice) -> Result { // TODO: wrong error type + pub fn capabilities(&self, device: PhysicalDevice) -> Result { unsafe { - assert_eq!(&*self.instance as *const _, &**device.instance() as *const _); + assert_eq!(&*self.instance as *const _, &**device.instance() as *const _, + "Instance mismatch in Surface::capabilities"); let vk = self.instance.pointers(); @@ -510,7 +519,7 @@ impl Surface { // https://bugs.freedesktop.org/show_bug.cgi?id=97153 // debug_assert!(modes.iter().find(|&&m| m == vk::PRESENT_MODE_FIFO_KHR).is_some()); debug_assert!(modes.iter().count() > 0); - SupportedPresentModes::from_list(modes.into_iter()) + capabilities::supported_present_modes_from_list(modes.into_iter()) }; Ok(Capabilities { @@ -527,16 +536,16 @@ impl Surface { min_image_extent: [caps.minImageExtent.width, caps.minImageExtent.height], max_image_extent: [caps.maxImageExtent.width, caps.maxImageExtent.height], max_image_array_layers: caps.maxImageArrayLayers, - supported_transforms: SupportedSurfaceTransforms::from_bits(caps.supportedTransforms), - current_transform: SupportedSurfaceTransforms::from_bits(caps.supportedTransforms).iter().next().unwrap(), // TODO: - supported_composite_alpha: SupportedCompositeAlpha::from_bits(caps.supportedCompositeAlpha), + supported_transforms: capabilities::surface_transforms_from_bits(caps.supportedTransforms), + current_transform: capabilities::surface_transforms_from_bits(caps.currentTransform).iter().next().unwrap(), // TODO: + supported_composite_alpha: capabilities::supported_composite_alpha_from_bits(caps.supportedCompositeAlpha), supported_usage_flags: { let usage = ImageUsage::from_bits(caps.supportedUsageFlags); debug_assert!(usage.color_attachment); // specs say that this must be true usage }, supported_formats: formats.into_iter().map(|f| { - (Format::from_num(f.format).unwrap(), ColorSpace::from_num(f.colorSpace)) + (Format::from_num(f.format).unwrap(), capabilities::color_space_from_num(f.colorSpace)) }).collect(), present_modes: modes, }) @@ -634,386 +643,57 @@ impl From for SurfaceCreationError { } } -/// The capabilities of a surface when used by a physical device. -/// -/// You have to match these capabilities when you create a swapchain. -#[derive(Clone, Debug)] -pub struct Capabilities { - /// Minimum number of images that must be present in the swapchain. - pub min_image_count: u32, - - /// Maximum number of images that must be present in the swapchain, or `None` if there is no - /// maximum value. Note that "no maximum" doesn't mean that you can set a very high value, as - /// you may still get out of memory errors. - pub max_image_count: Option, - - /// The current dimensions of the surface. `None` means that the surface's dimensions will - /// depend on the dimensions of the swapchain that you are going to create. - pub current_extent: Option<[u32; 2]>, - - /// Minimum width and height of a swapchain that uses this surface. - pub min_image_extent: [u32; 2], - - /// Maximum width and height of a swapchain that uses this surface. - pub max_image_extent: [u32; 2], - - /// Maximum number of image layers if you create an image array. The minimum is 1. - pub max_image_array_layers: u32, - - /// List of transforms supported for the swapchain. - pub supported_transforms: SupportedSurfaceTransforms, - - /// Current transform used by the surface. - pub current_transform: SurfaceTransform, - - /// List of composite alpha modes supports for the swapchain. - pub supported_composite_alpha: SupportedCompositeAlpha, - - /// List of image usages that are supported for images of the swapchain. Only - /// the `color_attachment` usage is guaranteed to be supported. - pub supported_usage_flags: ImageUsage, - - /// List of formats supported for the swapchain. - pub supported_formats: Vec<(Format, ColorSpace)>, // TODO: https://github.com/KhronosGroup/Vulkan-Docs/issues/207 - - /// List of present modes that are supported. `Fifo` is always guaranteed to be supported. - pub present_modes: SupportedPresentModes, -} - -/// The way presenting a swapchain is accomplished. +/// Error that can happen when retreiving a surface's capabilities. #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[repr(u32)] -pub enum PresentMode { - /// Immediately shows the image to the user. May result in visible tearing. - Immediate = vk::PRESENT_MODE_IMMEDIATE_KHR, +pub enum CapabilitiesError { + /// Not enough memory. + OomError(OomError), - /// The action of presenting an image puts it in wait. When the next vertical blanking period - /// happens, the waiting image is effectively shown to the user. If an image is presented while - /// another one is waiting, it is replaced. - Mailbox = vk::PRESENT_MODE_MAILBOX_KHR, - - /// The action of presenting an image adds it to a queue of images. At each vertical blanking - /// period, the queue is poped and an image is presented. - /// - /// Guaranteed to be always supported. - /// - /// This is the equivalent of OpenGL's `SwapInterval` with a value of 1. - Fifo = vk::PRESENT_MODE_FIFO_KHR, - - /// Same as `Fifo`, except that if the queue was empty during the previous vertical blanking - /// period then it is equivalent to `Immediate`. - /// - /// This is the equivalent of OpenGL's `SwapInterval` with a value of -1. - Relaxed = vk::PRESENT_MODE_FIFO_RELAXED_KHR, + /// The surface is no longer accessible and must be recreated. + SurfaceLost, } -/// List of `PresentMode`s that are supported. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct SupportedPresentModes { - pub immediate: bool, - pub mailbox: bool, - pub fifo: bool, - pub relaxed: bool, -} - -impl SupportedPresentModes { - /// Builds a `SupportedPresentModes` with all fields set to false. +impl error::Error for CapabilitiesError { #[inline] - pub fn none() -> SupportedPresentModes { - SupportedPresentModes { - immediate: false, - mailbox: false, - fifo: false, - relaxed: false, + fn description(&self) -> &str { + match *self { + CapabilitiesError::OomError(_) => "not enough memory", + CapabilitiesError::SurfaceLost => "the surface is no longer valid", } } #[inline] - fn from_list(elem: I) -> SupportedPresentModes where I: Iterator { - let mut result = SupportedPresentModes::none(); - for e in elem { - match e { - vk::PRESENT_MODE_IMMEDIATE_KHR => result.immediate = true, - vk::PRESENT_MODE_MAILBOX_KHR => result.mailbox = true, - vk::PRESENT_MODE_FIFO_KHR => result.fifo = true, - vk::PRESENT_MODE_FIFO_RELAXED_KHR => result.relaxed = true, - _ => panic!("Wrong value for vk::PresentModeKHR") - } - } - result - } - - /// Returns true if the given present mode is in this list of supported modes. - #[inline] - pub fn supports(&self, mode: PresentMode) -> bool { - match mode { - PresentMode::Immediate => self.immediate, - PresentMode::Mailbox => self.mailbox, - PresentMode::Fifo => self.fifo, - PresentMode::Relaxed => self.relaxed, + fn cause(&self) -> Option<&error::Error> { + match *self { + CapabilitiesError::OomError(ref err) => Some(err), + _ => None } } +} - /// Returns an iterator to the list of supported present modes. +impl fmt::Display for CapabilitiesError { #[inline] - pub fn iter(&self) -> SupportedPresentModesIter { - SupportedPresentModesIter(self.clone()) + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(fmt, "{}", error::Error::description(self)) } } -/// Enumeration of the `PresentMode`s that are supported. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct SupportedPresentModesIter(SupportedPresentModes); - -impl Iterator for SupportedPresentModesIter { - type Item = PresentMode; - +impl From for CapabilitiesError { #[inline] - fn next(&mut self) -> Option { - if self.0.immediate { self.0.immediate = false; return Some(PresentMode::Immediate); } - if self.0.mailbox { self.0.mailbox = false; return Some(PresentMode::Mailbox); } - if self.0.fifo { self.0.fifo = false; return Some(PresentMode::Fifo); } - if self.0.relaxed { self.0.relaxed = false; return Some(PresentMode::Relaxed); } - None + fn from(err: OomError) -> CapabilitiesError { + CapabilitiesError::OomError(err) } } -/// A transformation to apply to the image before showing it on the screen. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum SurfaceTransform { - /// Don't transform the image. - Identity = vk::SURFACE_TRANSFORM_IDENTITY_BIT_KHR, - /// Rotate 90 degrees. - Rotate90 = vk::SURFACE_TRANSFORM_ROTATE_90_BIT_KHR, - /// Rotate 180 degrees. - Rotate180 = vk::SURFACE_TRANSFORM_ROTATE_180_BIT_KHR, - /// Rotate 270 degrees. - Rotate270 = vk::SURFACE_TRANSFORM_ROTATE_270_BIT_KHR, - /// Mirror the image horizontally. - HorizontalMirror = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR, - /// Mirror the image horizontally and rotate 90 degrees. - HorizontalMirrorRotate90 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR, - /// Mirror the image horizontally and rotate 180 degrees. - HorizontalMirrorRotate180 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR, - /// Mirror the image horizontally and rotate 270 degrees. - HorizontalMirrorRotate270 = vk::SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR, - /// Let the operating system or driver implementation choose. - Inherit = vk::SURFACE_TRANSFORM_INHERIT_BIT_KHR, -} - -/// How the alpha values of the pixels of the window are treated. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum CompositeAlpha { - /// The alpha channel of the image is ignored. All the pixels are considered as if they have a - /// value of 1.0. - Opaque = vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - - /// The alpha channel of the image is respected. The color channels are expected to have - /// already been multiplied by the alpha value. - PreMultiplied = vk::COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, - - /// The alpha channel of the image is respected. The color channels will be multiplied by the - /// alpha value by the compositor before being added to what is behind. - PostMultiplied = vk::COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, - - /// Let the operating system or driver implementation choose. - Inherit = vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR, -} - -/// List of supported composite alpha modes. -/// -/// See the docs of `CompositeAlpha`. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[allow(missing_docs)] -pub struct SupportedCompositeAlpha { - pub opaque: bool, - pub pre_multiplied: bool, - pub post_multiplied: bool, - pub inherit: bool, -} - -impl SupportedCompositeAlpha { - /// Builds a `SupportedCompositeAlpha` with all fields set to false. +impl From for CapabilitiesError { #[inline] - pub fn none() -> SupportedCompositeAlpha { - SupportedCompositeAlpha { - opaque: false, - pre_multiplied: false, - post_multiplied: false, - inherit: false, - } - } - - #[inline] - fn from_bits(val: u32) -> SupportedCompositeAlpha { - let mut result = SupportedCompositeAlpha::none(); - if (val & vk::COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0 { result.opaque = true; } - if (val & vk::COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) != 0 { result.pre_multiplied = true; } - if (val & vk::COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) != 0 { result.post_multiplied = true; } - if (val & vk::COMPOSITE_ALPHA_INHERIT_BIT_KHR) != 0 { result.inherit = true; } - result - } - - /// Returns true if the given `CompositeAlpha` is in this list. - #[inline] - pub fn supports(&self, value: CompositeAlpha) -> bool { - match value { - CompositeAlpha::Opaque => self.opaque, - CompositeAlpha::PreMultiplied => self.pre_multiplied, - CompositeAlpha::PostMultiplied => self.post_multiplied, - CompositeAlpha::Inherit => self.inherit, - } - } - - /// Returns an iterator to the list of supported composite alpha. - #[inline] - pub fn iter(&self) -> SupportedCompositeAlphaIter { - SupportedCompositeAlphaIter(self.clone()) - } -} - -/// Enumeration of the `CompositeAlpha` that are supported. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct SupportedCompositeAlphaIter(SupportedCompositeAlpha); - -impl Iterator for SupportedCompositeAlphaIter { - type Item = CompositeAlpha; - - #[inline] - fn next(&mut self) -> Option { - if self.0.opaque { self.0.opaque = false; return Some(CompositeAlpha::Opaque); } - if self.0.pre_multiplied { self.0.pre_multiplied = false; return Some(CompositeAlpha::PreMultiplied); } - if self.0.post_multiplied { self.0.post_multiplied = false; return Some(CompositeAlpha::PostMultiplied); } - if self.0.inherit { self.0.inherit = false; return Some(CompositeAlpha::Inherit); } - None - } -} - -/// How the presentation engine should interpret the data. -/// -/// # A quick lesson about color spaces -/// -/// ## What is a color space? -/// -/// Each pixel of a monitor is made of three components: one red, one green, and one blue. In the -/// past, computers would simply send to the monitor the intensity of each of the three components. -/// -/// This proved to be problematic, because depending on the brand of the monitor the colors would -/// not exactly be the same. For example on some monitors, a value of `[1.0, 0.0, 0.0]` would be a -/// bit more orange than on others. -/// -/// In order to standardize this, there exist what are called *color spaces*: sRGB, AdobeRGB, -/// DCI-P3, scRGB, etc. When you manipulate RGB values in a specific color space, these values have -/// a precise absolute meaning in terms of color, that is the same across all systems and monitors. -/// -/// > **Note**: Color spaces are orthogonal to concept of RGB. *RGB* only indicates what is the -/// > representation of the data, but not how it is interpreted. You can think of this a bit like -/// > text encoding. An *RGB* value is a like a byte, in other words it is the medium by which -/// > values are communicated, and a *color space* is like a text encoding (eg. UTF-8), in other -/// > words it is the way the value should be interpreted. -/// -/// The most commonly used color space today is sRGB. Most monitors today use this color space, -/// and most images files are encoded in this color space. -/// -/// ## Pixel formats and linear vs non-linear -/// -/// In Vulkan all images have a specific format in which the data is stored. The data of an image -/// consists of pixels in RGB but contains no information about the color space (or lack thereof) -/// of these pixels. You are free to store them in whatever color space you want. -/// -/// But one big practical problem with color spaces is that they are sometimes not linear, and in -/// particular the popular sRGB color space is not linear. In a non-linear color space, a value of -/// `[0.6, 0.6, 0.6]` for example is **not** twice as bright as a value of `[0.3, 0.3, 0.3]`. This -/// is problematic, because operations such as taking the average of two colors or calculating the -/// lighting of a texture with a dot product are mathematically incorrect and will produce -/// incorrect colors. -/// -/// > **Note**: If the texture format has an alpha component, it is not affected by the color space -/// > and always behaves linearly. -/// -/// In order to solve this Vulkan also provides image formats with the `Srgb` suffix, which are -/// expected to contain RGB data in the sRGB color space. When you sample an image with such a -/// format from a shader, the implementation will automatically turn the pixel values into a linear -/// color space that is suitable for linear operations (such as additions or multiplications). -/// When you write to a framebuffer attachment with such a format, the implementation will -/// automatically perform the opposite conversion. These conversions are most of the time performed -/// by the hardware and incur no additional cost. -/// -/// ## Color space of the swapchain -/// -/// The color space that you specify when you create a swapchain is how the implementation will -/// interpret the raw data inside of the image. -/// -/// > **Note**: The implementation can choose to send the data in the swapchain image directly to -/// > the monitor, but it can also choose to write it in an intermediary buffer that is then read -/// > by the operating system or windowing system. Therefore the color space that the -/// > implementation supports is not necessarily the same as the one supported by the monitor. -/// -/// It is *your* job to ensure that the data in the swapchain image is in the color space -/// that is specified here, otherwise colors will be incorrect. -/// The implementation will never perform any additional automatic conversion after the colors have -/// been written to the swapchain image. -/// -/// # How do I handle this correctly? -/// -/// The easiest way to handle color spaces in a cross-platform program is: -/// -/// - Always request the `SrgbNonLinear` color space when creating the swapchain. -/// - Make sure that all your image files use the sRGB color space, and load them in images whose -/// format has the `Srgb` suffix. Only use non-sRGB image formats for intermediary computations -/// or to store non-color data. -/// - Swapchain images should have a format with the `Srgb` suffix. -/// -/// > **Note**: It is unclear whether the `SrgbNonLinear` color space is always supported by the -/// > the implementation or not. See https://github.com/KhronosGroup/Vulkan-Docs/issues/442. -/// -/// > **Note**: Lots of developers are confused by color spaces. You can sometimes find articles -/// > talking about gamma correction and suggestion to put your colors to the power 2.2 for -/// > example. These are all hacks and you should use the sRGB pixel formats intead. -/// -/// If you follow these three rules, then everything should render the same way on all platforms. -/// -/// Additionally you can try detect whether the implementation supports any additional color space -/// and perform a manual conversion to that color space from inside your shader. -/// -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum ColorSpace { - SrgbNonLinear = vk::COLOR_SPACE_SRGB_NONLINEAR_KHR, - DisplayP3Linear = vk::COLOR_SPACE_DISPLAY_P3_LINEAR_EXT, - DisplayP3NonLinear = vk::COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT, - ScrgbLinear = vk::COLOR_SPACE_SCRGB_LINEAR_EXT, - ScrgbNonLinear = vk::COLOR_SPACE_SCRGB_NONLINEAR_EXT, - DciP3Linear = vk::COLOR_SPACE_DCI_P3_LINEAR_EXT, - DciP3NonLinear = vk::COLOR_SPACE_DCI_P3_NONLINEAR_EXT, - Bt709Linear = vk::COLOR_SPACE_BT709_LINEAR_EXT, - Bt709NonLinear = vk::COLOR_SPACE_BT709_NONLINEAR_EXT, - Bt2020Linear = vk::COLOR_SPACE_BT2020_LINEAR_EXT, - Bt2020NonLinear = vk::COLOR_SPACE_BT2020_NONLINEAR_EXT, - AdobeRgbLinear = vk::COLOR_SPACE_ADOBERGB_LINEAR_EXT, - AdobeRgbNonLinear = vk::COLOR_SPACE_ADOBERGB_NONLINEAR_EXT, -} - -impl ColorSpace { - #[inline] - fn from_num(val: u32) -> ColorSpace { - match val { - vk::COLOR_SPACE_SRGB_NONLINEAR_KHR => ColorSpace::SrgbNonLinear, - vk::COLOR_SPACE_DISPLAY_P3_LINEAR_EXT => ColorSpace::DisplayP3Linear, - vk::COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT => ColorSpace::DisplayP3NonLinear, - vk::COLOR_SPACE_SCRGB_LINEAR_EXT => ColorSpace::ScrgbLinear, - vk::COLOR_SPACE_SCRGB_NONLINEAR_EXT => ColorSpace::ScrgbNonLinear, - vk::COLOR_SPACE_DCI_P3_LINEAR_EXT => ColorSpace::DciP3Linear, - vk::COLOR_SPACE_DCI_P3_NONLINEAR_EXT => ColorSpace::DciP3NonLinear, - vk::COLOR_SPACE_BT709_LINEAR_EXT => ColorSpace::Bt709Linear, - vk::COLOR_SPACE_BT709_NONLINEAR_EXT => ColorSpace::Bt709NonLinear, - vk::COLOR_SPACE_BT2020_LINEAR_EXT => ColorSpace::Bt2020Linear, - vk::COLOR_SPACE_BT2020_NONLINEAR_EXT => ColorSpace::Bt2020NonLinear, - vk::COLOR_SPACE_ADOBERGB_LINEAR_EXT => ColorSpace::AdobeRgbLinear, - vk::COLOR_SPACE_ADOBERGB_NONLINEAR_EXT => ColorSpace::AdobeRgbNonLinear, - _ => panic!("Wrong value for color space enum") + fn from(err: Error) -> CapabilitiesError { + match err { + err @ Error::OutOfHostMemory => CapabilitiesError::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => CapabilitiesError::OomError(OomError::from(err)), + Error::SurfaceLost => CapabilitiesError::SurfaceLost, + _ => panic!("unexpected error: {:?}", err) } } } @@ -1027,7 +707,7 @@ mod tests { #[test] fn khr_win32_surface_ext_missing() { let instance = instance!(); - match unsafe { Surface::from_hwnd(&instance, ptr::null::(), ptr::null::()) } { + match unsafe { Surface::from_hwnd(instance, ptr::null::(), ptr::null::()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!() } @@ -1036,7 +716,7 @@ mod tests { #[test] fn khr_xcb_surface_ext_missing() { let instance = instance!(); - match unsafe { Surface::from_xcb(&instance, ptr::null::(), 0) } { + match unsafe { Surface::from_xcb(instance, ptr::null::(), 0) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!() } @@ -1045,7 +725,7 @@ mod tests { #[test] fn khr_xlib_surface_ext_missing() { let instance = instance!(); - match unsafe { Surface::from_xlib(&instance, ptr::null::(), 0) } { + match unsafe { Surface::from_xlib(instance, ptr::null::(), 0) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!() } @@ -1054,7 +734,7 @@ mod tests { #[test] fn khr_wayland_surface_ext_missing() { let instance = instance!(); - match unsafe { Surface::from_wayland(&instance, ptr::null::(), ptr::null::()) } { + match unsafe { Surface::from_wayland(instance, ptr::null::(), ptr::null::()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!() } @@ -1063,7 +743,7 @@ mod tests { #[test] fn khr_mir_surface_ext_missing() { let instance = instance!(); - match unsafe { Surface::from_mir(&instance, ptr::null::(), ptr::null::()) } { + match unsafe { Surface::from_mir(instance, ptr::null::(), ptr::null::()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!() } @@ -1072,7 +752,7 @@ mod tests { #[test] fn khr_android_surface_ext_missing() { let instance = instance!(); - match unsafe { Surface::from_anativewindow(&instance, ptr::null::()) } { + match unsafe { Surface::from_anativewindow(instance, ptr::null::()) } { Err(SurfaceCreationError::MissingExtension { .. }) => (), _ => panic!() } diff --git a/vulkano/src/swapchain/swapchain.rs b/vulkano/src/swapchain/swapchain.rs index 1dc1425d..34fa41fd 100644 --- a/vulkano/src/swapchain/swapchain.rs +++ b/vulkano/src/swapchain/swapchain.rs @@ -13,7 +13,6 @@ use std::mem; use std::ptr; use std::sync::Arc; use std::sync::Mutex; -use std::sync::Weak; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::time::Duration; @@ -29,9 +28,9 @@ use format::Format; use format::FormatDesc; use image::ImageAccess; use image::ImageDimensions; -use image::Layout; +use image::ImageLayout; +use image::ImageUsage; use image::sys::UnsafeImage; -use image::sys::Usage as ImageUsage; use image::swapchain::SwapchainImage; use swapchain::ColorSpace; use swapchain::CompositeAlpha; @@ -56,13 +55,101 @@ use VulkanObject; use VulkanPointers; use vk; +/// Tries to take ownership of an image in order to draw on it. +/// +/// The function returns the index of the image in the array of images that was returned +/// when creating the swapchain, plus a future that represents the moment when the image will +/// become available from the GPU (which may not be *immediately*). +/// +/// If you try to draw on an image without acquiring it first, the execution will block. (TODO +/// behavior may change). +// TODO: has to make sure vkQueuePresent is called, because calling acquire_next_image many +// times in a row is an error +// TODO: change timeout to `Option`. +pub fn acquire_next_image(swapchain: Arc, timeout: Duration) + -> Result<(usize, SwapchainAcquireFuture), AcquireError> +{ + unsafe { + // Check that this is not an old swapchain. From specs: + // > swapchain must not have been replaced by being passed as the + // > VkSwapchainCreateInfoKHR::oldSwapchain value to vkCreateSwapchainKHR + let stale = swapchain.stale.lock().unwrap(); + if *stale { + return Err(AcquireError::OutOfDate); + } + + let vk = swapchain.device.pointers(); + + let semaphore = try!(Semaphore::new(swapchain.device.clone())); + + let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000) + .saturating_add(timeout.subsec_nanos() as u64); + + let mut out = mem::uninitialized(); + let r = try!(check_errors(vk.AcquireNextImageKHR(swapchain.device.internal_object(), + swapchain.swapchain, timeout_ns, + semaphore.internal_object(), 0, + &mut out))); + + let id = match r { + Success::Success => out as usize, + Success::Suboptimal => out as usize, // TODO: give that info to the user + Success::NotReady => return Err(AcquireError::Timeout), + Success::Timeout => return Err(AcquireError::Timeout), + s => panic!("unexpected success value: {:?}", s) + }; + + Ok((id, SwapchainAcquireFuture { + swapchain: swapchain.clone(), // TODO: don't clone + semaphore: semaphore, + image_id: id, + finished: AtomicBool::new(false), + })) + } +} + +/// Presents an image on the screen. +/// +/// The parameter is the same index as what `acquire_next_image` returned. The image must +/// have been acquired first. +/// +/// The actual behavior depends on the present mode that you passed when creating the +/// swapchain. +pub fn present(swapchain: Arc, before: F, queue: Arc, index: usize) + -> PresentFuture + where F: GpuFuture +{ + assert!(index < swapchain.images.len()); + + // TODO: restore this check with a dummy ImageAccess implementation + /*let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead + // Normally if `check_image_access` returns false we're supposed to call the `gpu_access` + // function on the image instead. But since we know that this method on `SwapchainImage` + // always returns false anyway (by design), we don't need to do it. + assert!(before.check_image_access(&swapchain_image, ImageLayout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead*/ + + PresentFuture { + previous: before, + queue: queue, + swapchain: swapchain, + image_id: index, + flushed: AtomicBool::new(false), + finished: AtomicBool::new(false), + } +} + /// Contains the swapping system and the images that can be shown on a surface. -// TODO: #[derive(Debug)] (waiting on https://github.com/aturon/crossbeam/issues/62) pub struct Swapchain { + // The Vulkan device this swapchain was created with. device: Arc, + // The surface, which we need to keep alive. surface: Arc, + // The swapchain object. swapchain: vk::SwapchainKHR, + // The images of this swapchain. + images: Vec, + // If true, that means we have used this swapchain to recreate a new swapchain. The current // swapchain can no longer be used for anything except presenting already-acquired images. // @@ -82,9 +169,12 @@ pub struct Swapchain { alpha: CompositeAlpha, mode: PresentMode, clipped: bool, +} - // TODO: meh for Mutex - images: Mutex, bool)>>, +struct ImageEntry { + image: UnsafeImage, + // If true, then the image is still in the undefined layout and must be transitionned. + undefined_layout: AtomicBool, } impl Swapchain { @@ -110,9 +200,10 @@ impl Swapchain { /// // TODO: remove `old_swapchain` parameter and add another function `with_old_swapchain`. // TODO: add `ColorSpace` parameter + // TODO: isn't it unsafe to take the surface through an Arc when it comes to vulkano-win? #[inline] - pub fn new(device: &Arc, surface: &Arc, num_images: u32, format: F, - dimensions: [u32; 2], layers: u32, usage: &ImageUsage, sharing: S, + pub fn new(device: Arc, surface: Arc, num_images: u32, format: F, + dimensions: [u32; 2], layers: u32, usage: ImageUsage, sharing: S, transform: SurfaceTransform, alpha: CompositeAlpha, mode: PresentMode, clipped: bool, old_swapchain: Option<&Arc>) -> Result<(Arc, Vec>), OomError> @@ -127,16 +218,14 @@ impl Swapchain { pub fn recreate_with_dimension(&self, dimensions: [u32; 2]) -> Result<(Arc, Vec>), OomError> { - Swapchain::new_inner(&self.device, &self.surface, self.num_images, self.format, - self.color_space, dimensions, self.layers, &self.usage, + Swapchain::new_inner(self.device.clone(), self.surface.clone(), self.num_images, + self.format, self.color_space, dimensions, self.layers, self.usage, self.sharing.clone(), self.transform, self.alpha, self.mode, self.clipped, Some(self)) } - // TODO: images layouts should always be set to "PRESENT", since we have no way to switch the - // layout at present time - fn new_inner(device: &Arc, surface: &Arc, num_images: u32, format: Format, - color_space: ColorSpace, dimensions: [u32; 2], layers: u32, usage: &ImageUsage, + fn new_inner(device: Arc, surface: Arc, num_images: u32, format: Format, + color_space: ColorSpace, dimensions: [u32; 2], layers: u32, usage: ImageUsage, sharing: SharingMode, transform: SurfaceTransform, alpha: CompositeAlpha, mode: PresentMode, clipped: bool, old_swapchain: Option<&Swapchain>) -> Result<(Arc, Vec>), OomError> @@ -159,13 +248,13 @@ impl Swapchain { // If we recreate a swapchain, make sure that the surface is the same. if let Some(sc) = old_swapchain { - // TODO: return proper error instead of panicing? - assert_eq!(surface.internal_object(), sc.surface.internal_object()); + assert_eq!(surface.internal_object(), sc.surface.internal_object(), + "Surface mismatch between old and new swapchain"); } // Checking that the surface doesn't already have a swapchain. if old_swapchain.is_none() { - // TODO: return proper error instead of panicing? + // TODO: return proper error instead of panicking let has_already = surface.flag().swap(true, Ordering::AcqRel); if has_already { panic!("The surface already has a swapchain alive"); } } @@ -218,10 +307,42 @@ impl Swapchain { output }; + let image_handles = unsafe { + let mut num = 0; + try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(), + swapchain, &mut num, + ptr::null_mut()))); + + let mut images = Vec::with_capacity(num as usize); + try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(), + swapchain, &mut num, + images.as_mut_ptr()))); + images.set_len(num as usize); + images + }; + + let images = image_handles.into_iter().enumerate().map(|(id, image)| unsafe { + let dims = ImageDimensions::Dim2d { + width: dimensions[0], + height: dimensions[1], + array_layers: layers, + cubemap_compatible: false, + }; + + let img = UnsafeImage::from_raw(device.clone(), image, usage.to_usage_bits(), format, + dims, 1, 1); + + ImageEntry { + image: img, + undefined_layout: AtomicBool::new(true) + } + }).collect::>(); + let swapchain = Arc::new(Swapchain { device: device.clone(), surface: surface.clone(), swapchain: swapchain, + images: images, stale: Mutex::new(false), num_images: num_images, format: format, @@ -234,113 +355,21 @@ impl Swapchain { alpha: alpha, mode: mode, clipped: clipped, - images: Mutex::new(Vec::new()), // Filled below. }); - let images = unsafe { - let mut num = 0; - try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(), - swapchain.swapchain, &mut num, - ptr::null_mut()))); - - let mut images = Vec::with_capacity(num as usize); - try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(), - swapchain.swapchain, &mut num, - images.as_mut_ptr()))); - images.set_len(num as usize); - images - }; - - let images = images.into_iter().enumerate().map(|(id, image)| unsafe { - let unsafe_image = UnsafeImage::from_raw(device, image, usage.to_usage_bits(), format, - ImageDimensions::Dim2d { width: dimensions[0], height: dimensions[1], array_layers: 1, cubemap_compatible: false }, 1, 1); - SwapchainImage::from_raw(unsafe_image, format, &swapchain, id as u32).unwrap() // TODO: propagate error - }).collect::>(); - - *swapchain.images.lock().unwrap() = images.iter().map(|i| (Arc::downgrade(i), true)).collect(); - Ok((swapchain, images)) - } - - /// Tries to take ownership of an image in order to draw on it. - /// - /// The function returns the index of the image in the array of images that was returned - /// when creating the swapchain, plus a future that represents the moment when the image will - /// become available from the GPU (which may not be *immediately*). - /// - /// If you try to draw on an image without acquiring it first, the execution will block. (TODO - /// behavior may change). - // TODO: has to make sure vkQueuePresent is called, because calling acquire_next_image many - // times in a row is an error - // TODO: swapchain must not have been replaced by being passed as the VkSwapchainCreateInfoKHR::oldSwapchain value to vkCreateSwapchainKHR - // TODO: change timeout to `Option`. - pub fn acquire_next_image(&self, timeout: Duration) -> Result<(usize, SwapchainAcquireFuture), AcquireError> { - unsafe { - let stale = self.stale.lock().unwrap(); - if *stale { - return Err(AcquireError::OutOfDate); + let swapchain_images = (0 .. swapchain.images.len()).map(|n| { + unsafe { + SwapchainImage::from_raw(swapchain.clone(), n).unwrap() // TODO: propagate error } + }).collect(); - let vk = self.device.pointers(); - - let semaphore = try!(Semaphore::new(self.device.clone())); - - let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000) - .saturating_add(timeout.subsec_nanos() as u64); - - let mut out = mem::uninitialized(); - let r = try!(check_errors(vk.AcquireNextImageKHR(self.device.internal_object(), - self.swapchain, timeout_ns, - semaphore.internal_object(), 0, - &mut out))); - - let id = match r { - Success::Success => out as usize, - Success::Suboptimal => out as usize, // TODO: give that info to the user - Success::NotReady => return Err(AcquireError::Timeout), - Success::Timeout => return Err(AcquireError::Timeout), - s => panic!("unexpected success value: {:?}", s) - }; - - let mut images = self.images.lock().unwrap(); - let undefined_layout = mem::replace(&mut images.get_mut(id).unwrap().1, false); - - Ok((id, SwapchainAcquireFuture { - semaphore: semaphore, - id: id, - image: images.get(id).unwrap().0.clone(), - finished: AtomicBool::new(false), - undefined_layout: undefined_layout, - })) - } + Ok((swapchain, swapchain_images)) } - /// Presents an image on the screen. - /// - /// The parameter is the same index as what `acquire_next_image` returned. The image must - /// have been acquired first. - /// - /// The actual behavior depends on the present mode that you passed when creating the - /// swapchain. - // TODO: use another API, since taking by Arc is meh - pub fn present(me: Arc, before: F, queue: Arc, index: usize) - -> PresentFuture - where F: GpuFuture - { - assert!(index < me.num_images as usize); - - let swapchain_image = me.images.lock().unwrap().get(index).unwrap().0.upgrade().unwrap(); // TODO: return error instead - // Normally if `check_image_access` returns false we're supposed to call the `gpu_access` - // function on the image instead. But since we know that this method on `SwapchainImage` - // always returns false anyway (by design), we don't need to do it. - assert!(before.check_image_access(&swapchain_image, Layout::PresentSrc, true, &queue).is_ok()); // TODO: return error instead - - PresentFuture { - previous: before, - queue: queue, - swapchain: me, - image_id: index as u32, - finished: AtomicBool::new(false), - } + /// Returns of the images that belong to this swapchain. + #[inline] + pub fn raw_image(&self, offset: usize) -> Option<&UnsafeImage> { + self.images.get(offset).map(|i| &i.image) } /// Returns the number of images of the swapchain. @@ -348,7 +377,7 @@ impl Swapchain { /// See the documentation of `Swapchain::new`. #[inline] pub fn num_images(&self) -> u32 { - self.num_images + self.images.len() as u32 } /// Returns the format of the images of the swapchain. @@ -438,25 +467,23 @@ impl Drop for Swapchain { /// Represents the moment when the GPU will have access to a swapchain image. #[must_use] pub struct SwapchainAcquireFuture { + swapchain: Arc, + image_id: usize, semaphore: Semaphore, - id: usize, - image: Weak, finished: AtomicBool, - // If true, then the acquired image is still in the undefined layout and must be transitionned. - undefined_layout: bool, } impl SwapchainAcquireFuture { /// Returns the index of the image in the list of images returned when creating the swapchain. #[inline] pub fn image_id(&self) -> usize { - self.id + self.image_id } - /// Returns the acquired image. + /// Returns the corresponding swapchain. #[inline] - pub fn image(&self) -> Option> { - self.image.upgrade() + pub fn swapchain(&self) -> &Arc { + &self.swapchain } } @@ -500,34 +527,30 @@ unsafe impl GpuFuture for SwapchainAcquireFuture { } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { - if let Some(sc_img) = self.image.upgrade() { - if sc_img.inner().internal_object() != image.inner().internal_object() { - return Err(AccessCheckError::Unknown); - } - - if self.undefined_layout && layout != Layout::Undefined { - return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized { - requested: layout - })); - } - - if layout != Layout::Undefined && layout != Layout::PresentSrc { - return Err(AccessCheckError::Denied(AccessError::UnexpectedImageLayout { - allowed: Layout::PresentSrc, - requested: layout, - })); - } - - Ok(None) - - } else { - // The swapchain image no longer exists, therefore the `image` parameter received by - // this function cannot possibly be the swapchain image. - Err(AccessCheckError::Unknown) + let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap(); + if swapchain_image.internal_object() != image.inner().internal_object() { + return Err(AccessCheckError::Unknown); } + + if self.swapchain.images[self.image_id].undefined_layout.load(Ordering::Relaxed) && + layout != ImageLayout::Undefined + { + return Err(AccessCheckError::Denied(AccessError::ImageNotInitialized { + requested: layout + })); + } + + if layout != ImageLayout::Undefined && layout != ImageLayout::PresentSrc { + return Err(AccessCheckError::Denied(AccessError::UnexpectedImageLayout { + allowed: ImageLayout::PresentSrc, + requested: layout, + })); + } + + Ok(None) } } @@ -629,10 +652,29 @@ pub struct PresentFuture

where P: GpuFuture { previous: P, queue: Arc, swapchain: Arc, - image_id: u32, + image_id: usize, + // True if `flush()` has been called on the future, which means that the present command has + // been submitted. + flushed: AtomicBool, + // True if `signal_finished()` has been called on the future, which means that the future has + // been submitted and has already been processed by the GPU. finished: AtomicBool, } +impl

PresentFuture

where P: GpuFuture { + /// Returns the index of the image in the list of images returned when creating the swapchain. + #[inline] + pub fn image_id(&self) -> usize { + self.image_id + } + + /// Returns the corresponding swapchain. + #[inline] + pub fn swapchain(&self) -> &Arc { + &self.swapchain + } +} + unsafe impl

GpuFuture for PresentFuture

where P: GpuFuture { #[inline] fn cleanup_finished(&mut self) { @@ -641,6 +683,10 @@ unsafe impl

GpuFuture for PresentFuture

where P: GpuFuture { #[inline] unsafe fn build_submission(&self) -> Result { + if self.flushed.load(Ordering::SeqCst) { + return Ok(SubmitAnyBuilder::Empty); + } + let queue = self.previous.queue().map(|q| q.clone()); // TODO: if the swapchain image layout is not PRESENT, should add a transition command @@ -649,18 +695,18 @@ unsafe impl

GpuFuture for PresentFuture

where P: GpuFuture { Ok(match try!(self.previous.build_submission()) { SubmitAnyBuilder::Empty => { let mut builder = SubmitPresentBuilder::new(); - builder.add_swapchain(&self.swapchain, self.image_id); + builder.add_swapchain(&self.swapchain, self.image_id as u32); SubmitAnyBuilder::QueuePresent(builder) }, SubmitAnyBuilder::SemaphoresWait(sem) => { let mut builder: SubmitPresentBuilder = sem.into(); - builder.add_swapchain(&self.swapchain, self.image_id); + builder.add_swapchain(&self.swapchain, self.image_id as u32); SubmitAnyBuilder::QueuePresent(builder) }, SubmitAnyBuilder::CommandBuffer(cb) => { try!(cb.submit(&queue.unwrap())); // FIXME: wrong because build_submission can be called multiple times let mut builder = SubmitPresentBuilder::new(); - builder.add_swapchain(&self.swapchain, self.image_id); + builder.add_swapchain(&self.swapchain, self.image_id as u32); SubmitAnyBuilder::QueuePresent(builder) }, SubmitAnyBuilder::QueuePresent(present) => { @@ -675,11 +721,25 @@ unsafe impl

GpuFuture for PresentFuture

where P: GpuFuture { #[inline] fn flush(&self) -> Result<(), FlushError> { - unimplemented!() + unsafe { + // If `flushed` already contains `true`, then `build_submission` will return `Empty`. + + match self.build_submission()? { + SubmitAnyBuilder::Empty => {} + SubmitAnyBuilder::QueuePresent(present) => { + present.submit(&self.queue)?; + } + _ => unreachable!() + } + + self.flushed.store(true, Ordering::SeqCst); + Ok(()) + } } #[inline] unsafe fn signal_finished(&self) { + self.flushed.store(true, Ordering::SeqCst); self.finished.store(true, Ordering::SeqCst); self.previous.signal_finished(); } @@ -703,14 +763,23 @@ unsafe impl

GpuFuture for PresentFuture

where P: GpuFuture { fn check_buffer_access(&self, buffer: &BufferAccess, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { - unimplemented!() // TODO: VK specs don't say whether it is legal to do that + self.previous.check_buffer_access(buffer, exclusive, queue) } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { - unimplemented!() // TODO: VK specs don't say whether it is legal to do that + let swapchain_image = self.swapchain.raw_image(self.image_id).unwrap(); + if swapchain_image.internal_object() == image.inner().internal_object() { + // This future presents the swapchain image, which "unlocks" it. Therefore any attempt + // to use this swapchain image afterwards shouldn't get granted automatic access. + // Instead any attempt to access the image afterwards should get an authorization from + // a later swapchain acquire future. Hence why we return `Unknown` here. + Err(AccessCheckError::Unknown) + } else { + self.previous.check_image_access(image, layout, exclusive, queue) + } } } diff --git a/vulkano/src/sync/event.rs b/vulkano/src/sync/event.rs index 86d2bebc..b2dfe53f 100644 --- a/vulkano/src/sync/event.rs +++ b/vulkano/src/sync/event.rs @@ -37,9 +37,7 @@ pub struct Event { impl Event { /// See the docs of new(). #[inline] - pub fn raw(device: &Arc) -> Result { - let vk = device.pointers(); - + pub fn raw(device: Arc) -> Result { let event = unsafe { // since the creation is constant, we use a `static` instead of a struct on the stack static mut INFOS: vk::EventCreateInfo = vk::EventCreateInfo { @@ -49,13 +47,14 @@ impl Event { }; let mut output = mem::uninitialized(); + let vk = device.pointers(); try!(check_errors(vk.CreateEvent(device.internal_object(), &INFOS, ptr::null(), &mut output))); output }; Ok(Event { - device: device.clone(), + device: device, event: event, }) } @@ -67,7 +66,7 @@ impl Event { /// - Panics if the device or host ran out of memory. /// #[inline] - pub fn new(device: &Arc) -> Arc { + pub fn new(device: Arc) -> Arc { Arc::new(Event::raw(device).unwrap()) } @@ -165,14 +164,14 @@ mod tests { #[test] fn event_create() { let (device, _) = gfx_dev_and_queue!(); - let event = Event::new(&device); + let event = Event::new(device); assert!(!event.signaled().unwrap()); } #[test] fn event_set() { let (device, _) = gfx_dev_and_queue!(); - let mut event = Event::new(&device); + let mut event = Event::new(device); assert!(!event.signaled().unwrap()); Arc::get_mut(&mut event).unwrap().set(); @@ -183,7 +182,7 @@ mod tests { fn event_reset() { let (device, _) = gfx_dev_and_queue!(); - let mut event = Event::new(&device); + let mut event = Event::new(device); Arc::get_mut(&mut event).unwrap().set(); assert!(event.signaled().unwrap()); diff --git a/vulkano/src/sync/future/fence_signal.rs b/vulkano/src/sync/future/fence_signal.rs index 3c9bf663..9c41bffe 100644 --- a/vulkano/src/sync/future/fence_signal.rs +++ b/vulkano/src/sync/future/fence_signal.rs @@ -20,7 +20,7 @@ use device::Device; use device::DeviceOwned; use device::Queue; use image::ImageAccess; -use image::Layout; +use image::ImageLayout; use sync::AccessCheckError; use sync::AccessFlagBits; use sync::FlushError; @@ -58,6 +58,35 @@ pub enum FenceSignalFutureBehavior { } /// Represents a fence being signaled after a previous event. +/// +/// Contrary to most other future types, it is possible to block the current thread until the event +/// happens. This is done by calling the `wait()` function. +/// +/// Also note that the `GpuFuture` trait is implemented on `Arc>`. +/// This means that you can put this future in an `Arc` and keep a copy of it somewhere in order +/// to know when the execution reached that point. +/// +/// ``` +/// use std::sync::Arc; +/// use vulkano::sync::GpuFuture; +/// +/// # let future: Box = return; +/// // Assuming you have a chain of operations, like this: +/// // let future = ... +/// // .then_execute(foo) +/// // .then_execute(bar) +/// +/// // You can signal a fence at this point of the chain, and put the future in an `Arc`. +/// let fence_signal = Arc::new(future.then_signal_fence()); +/// +/// // And then continue the chain: +/// // fence_signal.clone() +/// // .then_execute(baz) +/// // .then_execute(qux) +/// +/// // Later you can wait until you reach the point of `fence_signal`: +/// fence_signal.wait(None).unwrap(); +/// ``` #[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"] pub struct FenceSignalFuture where F: GpuFuture { // Current state. See the docs of `FenceSignalFutureState`. @@ -91,6 +120,32 @@ enum FenceSignalFutureState { Poisonned, } +impl FenceSignalFuture where F: GpuFuture { + /// Blocks the current thread until the fence is signaled by the GPU. Performs a flush if + /// necessary. + /// + /// If `timeout` is `None`, then the wait is infinite. Otherwise the thread will unblock after + /// the specified timeout has elapsed and an error will be returned. + /// + /// If the wait is successful, this function also cleans any resource locked by previous + /// submissions. + pub fn wait(&self, timeout: Option) -> Result<(), FlushError> { + let mut state = self.state.lock().unwrap(); + + self.flush_impl(&mut state)?; + + match mem::replace(&mut *state, FenceSignalFutureState::Cleaned) { + FenceSignalFutureState::Flushed(previous, fence) => { + fence.wait(timeout)?; + unsafe { previous.signal_finished(); } + Ok(()) + }, + FenceSignalFutureState::Cleaned => Ok(()), + _ => unreachable!() + } + } +} + impl FenceSignalFuture where F: GpuFuture { // Implementation of `cleanup_finished`, but takes a `&self` instead of a `&mut self`. // This is an external function so that we can also call it from an `Arc`. @@ -99,12 +154,25 @@ impl FenceSignalFuture where F: GpuFuture { let mut state = self.state.lock().unwrap(); match *state { - FenceSignalFutureState::Flushed(_, ref fence) => { + FenceSignalFutureState::Flushed(ref mut prev, ref fence) => { match fence.wait(Some(Duration::from_secs(0))) { - Ok(()) => (), - Err(_) => return, + Ok(()) => unsafe { + prev.signal_finished() + }, + Err(_) => { + prev.cleanup_finished(); + return + }, } }, + FenceSignalFutureState::Pending(ref mut prev, _) => { + prev.cleanup_finished(); + return; + }, + FenceSignalFutureState::PartiallyFlushed(ref mut prev, _) => { + prev.cleanup_finished(); + return; + }, _ => return, }; @@ -305,7 +373,7 @@ unsafe impl GpuFuture for FenceSignalFuture where F: GpuFuture { } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { let state = self.state.lock().unwrap(); if let Some(previous) = state.get_prev() { @@ -393,7 +461,7 @@ unsafe impl GpuFuture for Arc> where F: GpuFuture { } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { (**self).check_image_access(image, layout, exclusive, queue) diff --git a/vulkano/src/sync/future/join.rs b/vulkano/src/sync/future/join.rs index 67f05210..55362c46 100644 --- a/vulkano/src/sync/future/join.rs +++ b/vulkano/src/sync/future/join.rs @@ -15,7 +15,7 @@ use device::Device; use device::DeviceOwned; use device::Queue; use image::ImageAccess; -use image::Layout; +use image::ImageLayout; use sync::AccessCheckError; use sync::AccessFlagBits; use sync::FlushError; @@ -173,7 +173,7 @@ unsafe impl GpuFuture for JoinFuture where A: GpuFuture, B: GpuFutur } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { let first = self.first.check_image_access(image, layout, exclusive, queue); diff --git a/vulkano/src/sync/future/mod.rs b/vulkano/src/sync/future/mod.rs index 09deae62..96533fe4 100644 --- a/vulkano/src/sync/future/mod.rs +++ b/vulkano/src/sync/future/mod.rs @@ -21,7 +21,8 @@ use command_buffer::submit::SubmitCommandBufferError; use device::DeviceOwned; use device::Queue; use image::ImageAccess; -use image::Layout; +use image::ImageLayout; +use swapchain; use swapchain::Swapchain; use swapchain::PresentFuture; use sync::AccessFlagBits; @@ -58,7 +59,9 @@ pub unsafe trait GpuFuture: DeviceOwned { /// /// It is the responsibility of the caller to ensure that the submission is going to be /// submitted only once. However keep in mind that this function can perfectly be called - /// multiple times (as long as the returned object is only submitted once). + /// multiple times (as long as the returned object is only submitted once). + /// Also note that calling `flush()` on the future may change the value returned by + /// `build_submission()`. /// /// It is however the responsibility of the implementation to not return the same submission /// from multiple different future objects. For example if you implement `GpuFuture` on @@ -124,7 +127,7 @@ pub unsafe trait GpuFuture: DeviceOwned { /// /// > **Note**: Keep in mind that changing the layout of an image also requires exclusive /// > access. - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError>; /// Joins this future with another one, representing the moment when both events have happened. @@ -197,9 +200,7 @@ pub unsafe trait GpuFuture: DeviceOwned { /// > function. If so, consider using `then_signal_fence_and_flush`. #[inline] fn then_signal_fence(self) -> FenceSignalFuture where Self: Sized { - fence_signal::then_signal_fence(self, FenceSignalFutureBehavior::Block { - timeout: None - }) + fence_signal::then_signal_fence(self, FenceSignalFutureBehavior::Continue) } /// Signals a fence after this future. Returns another future that represents the signal. @@ -225,7 +226,7 @@ pub unsafe trait GpuFuture: DeviceOwned { image_index: usize) -> PresentFuture where Self: Sized { - Swapchain::present(swapchain, self, queue, image_index) + swapchain::present(swapchain, self, queue, image_index) } } @@ -268,7 +269,7 @@ unsafe impl GpuFuture for Box where F: GpuFuture { } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { (**self).check_image_access(image, layout, exclusive, queue) @@ -281,17 +282,26 @@ pub enum AccessError { /// Exclusive access is denied. ExclusiveDenied, + /// The resource is already in use, and there is no tracking of concurrent usages. + AlreadyInUse, + UnexpectedImageLayout { - allowed: Layout, - requested: Layout, + allowed: ImageLayout, + requested: ImageLayout, }, /// Trying to use an image without transitionning it from the "undefined" or "preinitialized" /// layouts first. ImageNotInitialized { /// The layout that was requested for the image. - requested: Layout, + requested: ImageLayout, }, + + /// Trying to use a buffer that still contains garbage data. + BufferNotInitialized, + + /// Trying to use a swapchain image without depending on a corresponding acquire image future. + SwapchainImageAcquireOnly, } impl error::Error for AccessError { @@ -301,6 +311,9 @@ impl error::Error for AccessError { AccessError::ExclusiveDenied => { "only shared access is allowed for this resource" }, + AccessError::AlreadyInUse => { + "the resource is already in use, and there is no tracking of concurrent usages" + }, AccessError::UnexpectedImageLayout { .. } => { unimplemented!() // TODO: find a description }, @@ -308,6 +321,13 @@ impl error::Error for AccessError { "trying to use an image without transitionning it from the undefined or \ preinitialized layouts first" }, + AccessError::BufferNotInitialized => { + "trying to use a buffer that still contains garbage data" + }, + AccessError::SwapchainImageAcquireOnly => { + "trying to use a swapchain image without depending on a corresponding acquire \ + image future" + }, } } } diff --git a/vulkano/src/sync/future/now.rs b/vulkano/src/sync/future/now.rs index 603115f4..4931b3a7 100644 --- a/vulkano/src/sync/future/now.rs +++ b/vulkano/src/sync/future/now.rs @@ -15,7 +15,7 @@ use device::Device; use device::DeviceOwned; use device::Queue; use image::ImageAccess; -use image::Layout; +use image::ImageLayout; use sync::AccessCheckError; use sync::AccessFlagBits; use sync::FlushError; @@ -72,7 +72,7 @@ unsafe impl GpuFuture for NowFuture { } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { Err(AccessCheckError::Unknown) diff --git a/vulkano/src/sync/future/semaphore_signal.rs b/vulkano/src/sync/future/semaphore_signal.rs index a390594c..57468bae 100644 --- a/vulkano/src/sync/future/semaphore_signal.rs +++ b/vulkano/src/sync/future/semaphore_signal.rs @@ -20,7 +20,7 @@ use device::Device; use device::DeviceOwned; use device::Queue; use image::ImageAccess; -use image::Layout; +use image::ImageLayout; use sync::AccessCheckError; use sync::AccessFlagBits; use sync::FlushError; @@ -136,7 +136,7 @@ unsafe impl GpuFuture for SemaphoreSignalFuture where F: GpuFuture { } #[inline] - fn check_image_access(&self, image: &ImageAccess, layout: Layout, exclusive: bool, queue: &Queue) + fn check_image_access(&self, image: &ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue) -> Result, AccessCheckError> { self.previous.check_image_access(image, layout, exclusive, queue).map(|_| None)