screen_13/lib.rs
1/*!
2
3Provides a high performance [Vulkan 1.2](https://www.vulkan.org/) driver using smart pointers.
4Supports windowed and headless rendering with a fully-featured render graph and resource pooling
5types.
6
7This crate allows graphics programmers to focus on acceleration structure, buffer, and image
8resources and the shader pipelines they are accessed from. There are no restrictions or opinions
9placed on the types of graphics algorithms you might create. Some implementations of common graphics
10patterns are provided in the `contrib` directory.
11
12# Getting Sarted
13
14Typical usage involves creating an operating system window event loop, and rendering frames
15using the provided [`FrameContext`] closure. The [`EventLoop`] builder handles creating an instance
16of the [`Device`] driver, however you may construct one manually for headless rendering.
17
18```no_run
19use screen_13_window::{Window, WindowError};
20
21fn main() -> Result<(), WindowError> {
22 let window = Window::new()?;
23
24 // Use the device to create resources and pipelines before running
25 let device = &window.device;
26
27 window.run(|frame| {
28 // You may also create resources and pipelines while running
29 let device = &frame.device;
30 })
31}
32```
33
34# Resources and Pipelines
35
36All resources and pipelines, as well as the driver itself, use shared reference tracking to keep
37pointers alive. _Screen 13_ uses `std::sync::Arc` to track references.
38
39## Information
40
41All [`driver`] types have associated information structures which describe their properties.
42Each object provides a `create` function which uses the information to return an instance.
43
44| Resource | Create Using |
45|-------------------------------|-----------------------------------------------------|
46| [`AccelerationStructureInfo`] | [`AccelerationStructure::create`] |
47| [`BufferInfo`] | [`Buffer::create`] or [`Buffer::create_from_slice`] |
48| [`ImageInfo`] | [`Image::create`] |
49
50For example, a typical host-mappable buffer:
51
52```no_run
53# use std::sync::Arc;
54# use ash::vk;
55# use screen_13::driver::DriverError;
56# use screen_13::driver::device::{Device, DeviceInfo};
57# use screen_13::driver::buffer::{Buffer, BufferInfo};
58# fn main() -> Result<(), DriverError> {
59# let device = Arc::new(Device::create_headless(DeviceInfo::default())?);
60let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER);
61let my_buf = Buffer::create(&device, info)?;
62# Ok(()) }
63```
64
65| Pipeline | Create Using |
66|-------------------------------|-----------------------------------------------------|
67| [`ComputePipelineInfo`] | [`ComputePipeline::create`] |
68| [`GraphicPipelineInfo`] | [`GraphicPipeline::create`] |
69| [`RayTracePipelineInfo`] | [`RayTracePipeline::create`] |
70
71For example, a graphics pipeline:
72
73```no_run
74# use std::sync::Arc;
75# use ash::vk;
76# use screen_13::driver::DriverError;
77# use screen_13::driver::device::{Device, DeviceInfo};
78# use screen_13::driver::graphic::{GraphicPipeline, GraphicPipelineInfo};
79# use screen_13::driver::shader::Shader;
80# fn main() -> Result<(), DriverError> {
81# let device = Arc::new(Device::create_headless(DeviceInfo::default())?);
82# let my_frag_code = [0u8; 1];
83# let my_vert_code = [0u8; 1];
84// shader code is raw SPIR-V code as bytes
85let vert = Shader::new_vertex(my_vert_code.as_slice());
86let frag = Shader::new_fragment(my_frag_code.as_slice());
87let info = GraphicPipelineInfo::default();
88let my_pipeline = GraphicPipeline::create(&device, info, [vert, frag])?;
89# Ok(()) }
90```
91
92## Pooling
93
94Multiple [`pool`] types are available to reduce the impact of frequently creating and dropping
95resources. Leased resources behave identically to owned resources and can be used in a render graph.
96
97Resource aliasing is also availble as an optional way to reduce the number of concurrent resources
98that may be required.
99
100For example, leasing an image:
101
102```no_run
103# use std::sync::Arc;
104# use ash::vk;
105# use screen_13::driver::DriverError;
106# use screen_13::driver::device::{Device, DeviceInfo};
107# use screen_13::driver::image::{ImageInfo};
108# use screen_13::pool::{Pool};
109# use screen_13::pool::lazy::{LazyPool};
110# fn main() -> Result<(), DriverError> {
111# let device = Arc::new(Device::create_headless(DeviceInfo::default())?);
112let mut pool = LazyPool::new(&device);
113
114let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE);
115let my_image = pool.lease(info)?;
116# Ok(()) }
117```
118
119# Render Graph Operations
120
121All rendering in _Screen 13_ is performed using a [`RenderGraph`] composed of user-specified passes,
122which may include pipelines and read/write access to resources. Recorded passes are automatically
123optimized before submission to the graphics hardware.
124
125Some notes about the awesome render pass optimization which was _totally stolen_ from [Granite]:
126
127- Scheduling: passes are submitted to the Vulkan API using batches designed for low-latency
128- Re-ordering: passes are shuffled using a heuristic which gives the GPU more time to complete work
129- Merging: compatible passes are merged into dynamic subpasses when it is more efficient (_on-tile
130 rendering_)
131- Aliasing: resources and pipelines are optimized to emit minimal barriers per unit of work (_max
132 one, typically zero_)
133
134## Nodes
135
136Resources may be directly bound to a render graph. During the time a resource is bound we refer to
137it as a node. Bound nodes may only be used with the graphs they were bound to. Nodes implement
138`Copy` to make using them easier.
139
140```no_run
141# use std::sync::Arc;
142# use ash::vk;
143# use screen_13::driver::DriverError;
144# use screen_13::driver::device::{Device, DeviceInfo};
145# use screen_13::driver::buffer::{Buffer, BufferInfo};
146# use screen_13::driver::image::{Image, ImageInfo};
147# use screen_13::graph::RenderGraph;
148# use screen_13::pool::{Pool};
149# use screen_13::pool::lazy::{LazyPool};
150# fn main() -> Result<(), DriverError> {
151# let device = Arc::new(Device::create_headless(DeviceInfo::default())?);
152# let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER);
153# let buffer = Buffer::create(&device, info)?;
154# let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE);
155# let image = Image::create(&device, info)?;
156# let mut graph = RenderGraph::new();
157println!("{:?}", buffer); // Buffer
158println!("{:?}", image); // Image
159
160// Bind our resources into opaque "usize" nodes
161let buffer = graph.bind_node(buffer);
162let image = graph.bind_node(image);
163
164// The results have unique types!
165println!("{:?}", buffer); // BufferNode
166println!("{:?}", image); // ImageNode
167
168// Unbind nodes back into resources (Optional!)
169let buffer = graph.unbind_node(buffer);
170let image = graph.unbind_node(image);
171
172// Magically, they return to the correct types! (the graph wrapped them in Arc for us)
173println!("{:?}", buffer); // Arc<Buffer>
174println!("{:?}", image); // Arc<Image>
175# Ok(()) }
176```
177
178_Note:_ See [this code](https://github.com/attackgoat/screen-13/blob/master/src/graph/edge.rs#L34)
179for all the things that can be bound or unbound from a graph.
180
181_Note:_ Once unbound, the node is invalid and should be dropped.
182
183## Access and synchronization
184
185Render graphs and their passes contain a set of functions used to handle Vulkan synchronization with
186prefixes of `access`, `read`, or `write`. For each resource used in a computing, graphics subpass,
187ray tracing, or general command buffer you must call an access function. Generally choose a `read`
188or `write` function unless you want to be most efficient.
189
190Example:
191
192```no_run
193# use std::sync::Arc;
194# use ash::vk;
195# use screen_13::driver::DriverError;
196# use screen_13::driver::device::{Device, DeviceInfo};
197# use screen_13::driver::buffer::{Buffer, BufferInfo};
198# use screen_13::driver::image::{Image, ImageInfo};
199# use screen_13::graph::RenderGraph;
200# use screen_13::pool::{Pool};
201# use screen_13::pool::lazy::{LazyPool};
202# fn main() -> Result<(), DriverError> {
203# let device = Arc::new(Device::create_headless(DeviceInfo::default())?);
204# let info = BufferInfo::host_mem(1024, vk::BufferUsageFlags::STORAGE_BUFFER);
205# let buffer = Buffer::create(&device, info)?;
206# let info = ImageInfo::image_2d(8, 8, vk::Format::R8G8B8A8_UNORM, vk::ImageUsageFlags::STORAGE);
207# let image = Image::create(&device, info)?;
208let mut graph = RenderGraph::new();
209let buffer_node = graph.bind_node(buffer);
210let image_node = graph.bind_node(image);
211graph
212 .begin_pass("Do some raw Vulkan or interop with another Vulkan library")
213 .record_cmd_buf(move |device, cmd_buf, bindings| unsafe {
214 // I always run first!
215 })
216 .read_node(buffer_node) // <-- These two functions, read_node/write_node, completely
217 .write_node(image_node) // handle vulkan synchronization.
218 .record_cmd_buf(move |device, cmd_buf, bindings| unsafe {
219 // device is &ash::Device
220 // cmd_buf is vk::CommandBuffer
221 // bindings is a magical object you can retrieve the Vulkan resource from
222 let vk_buffer: vk::Buffer = *bindings[buffer_node];
223 let vk_image: vk::Image = *bindings[image_node];
224
225 // You are free to READ vk_buffer and WRITE vk_image!
226 });
227# Ok(()) }
228```
229
230## Shader pipelines
231
232Pipeline instances may be bound to a [`PassRef`] in order to execute the associated shader code:
233
234```no_run
235# use std::sync::Arc;
236# use ash::vk;
237# use screen_13::driver::DriverError;
238# use screen_13::driver::device::{Device, DeviceInfo};
239# use screen_13::driver::compute::{ComputePipeline, ComputePipelineInfo};
240# use screen_13::driver::shader::{Shader};
241# use screen_13::graph::RenderGraph;
242# fn main() -> Result<(), DriverError> {
243# let device = Arc::new(Device::create_headless(DeviceInfo::default())?);
244# let my_shader_code = [0u8; 1];
245# let info = ComputePipelineInfo::default();
246# let shader = Shader::new_compute(my_shader_code.as_slice());
247# let my_compute_pipeline = Arc::new(ComputePipeline::create(&device, info, shader)?);
248# let mut graph = RenderGraph::new();
249graph
250 .begin_pass("My compute pass")
251 .bind_pipeline(&my_compute_pipeline)
252 .record_compute(|compute, _| {
253 compute.push_constants(&42u32.to_ne_bytes())
254 .dispatch(128, 1, 1);
255 });
256# Ok(()) }
257```
258
259## Image samplers
260
261By default, _Screen 13_ will use "linear repeat-mode" samplers unless a special suffix appears as
262part of the name within GLSL or HLSL shader code. The `_sampler_123` suffix should be used where
263`1`, `2`, and `3` are replaced with:
264
2651. `l` for `LINEAR` texel filtering (default) or `n` for `NEAREST`
2661. `l` (default) or `n`, as above, but for mipmap filtering
2671. Addressing mode where:
268 - `b` is `CLAMP_TO_BORDER`
269 - `e` is `CLAMP_TO_EDGE`
270 - `m` is `MIRRORED_REPEAT`
271 - `r` is `REPEAT`
272
273For example, the following sampler named `pages_sampler_nnr` specifies nearest texel/mipmap modes and repeat addressing:
274
275```glsl
276layout(set = 0, binding = 0) uniform sampler2D pages_sampler_nnr[NUM_PAGES];
277```
278
279For more complex image sampling, use [`ShaderBuilder::image_sampler`] to specify the exact image
280sampling mode.
281
282## Vertex input
283
284Optional name suffixes are used in the same way with vertex input as with image samplers. The
285additional attribution of your shader code is optional but may help in a few scenarios:
286
287- Per-instance vertex rate data
288- Multiple vertex buffer binding indexes
289
290The data for vertex input is assumed to be per-vertex and bound to vertex buffer binding index zero.
291Add `_ibindX` for per-instance data, or the matching `_vbindX` for per-vertex data where `X` is
292replaced with the vertex buffer binding index in each case.
293
294For more complex vertex layouts, use the [`ShaderBuilder::vertex_input`] to specify the exact
295layout.
296
297[`AccelerationStructureInfo`]: driver::accel_struct::AccelerationStructureInfo
298[`AccelerationStructure::create`]: driver::accel_struct::AccelerationStructure::create
299[`Buffer::create`]: driver::buffer::Buffer::create
300[`Buffer::create_from_slice`]: driver::buffer::Buffer::create_from_slice
301[`BufferInfo`]: driver::buffer::BufferInfo
302[`ComputePipeline::create`]: driver::compute::ComputePipeline::create
303[`ComputePipelineInfo`]: driver::compute::ComputePipelineInfo
304[`Device`]: driver::device::Device
305[`EventLoop`]: EventLoop
306[`FrameContext`]: FrameContext
307[Granite]: https://github.com/Themaister/Granite
308[`GraphicPipeline::create`]: driver::graphic::GraphicPipeline::create
309[`GraphicPipelineInfo`]: driver::graphic::GraphicPipelineInfo
310[`Image::create`]: driver::image::Image::create
311[`ImageInfo`]: driver::image::ImageInfo
312[`PassRef`]: graph::pass_ref::PassRef
313[`RayTracePipeline::create`]: driver::ray_trace::RayTracePipeline::create
314[`RayTracePipelineInfo`]: driver::ray_trace::RayTracePipelineInfo
315[`RenderGraph`]: graph::RenderGraph
316[`ShaderBuilder::image_sampler`]: driver::shader::ShaderBuilder::image_sampler
317[`ShaderBuilder::vertex_input`]: driver::shader::ShaderBuilder::vertex_input
318
319*/
320
321#![warn(missing_docs)]
322
323pub mod driver;
324pub mod graph;
325pub mod pool;
326
327mod display;
328
329/// Things which are used in almost every single _Screen 13_ program.
330pub mod prelude {
331 pub use super::{
332 display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool},
333 driver::{
334 AccessType, CommandBuffer, DriverError, Instance,
335 accel_struct::{
336 AccelerationStructure, AccelerationStructureGeometry,
337 AccelerationStructureGeometryData, AccelerationStructureGeometryInfo,
338 AccelerationStructureInfo, AccelerationStructureInfoBuilder,
339 AccelerationStructureSize, DeviceOrHostAddress,
340 },
341 ash::vk,
342 buffer::{Buffer, BufferInfo, BufferInfoBuilder, BufferSubresourceRange},
343 compute::{ComputePipeline, ComputePipelineInfo, ComputePipelineInfoBuilder},
344 device::{Device, DeviceInfo, DeviceInfoBuilder},
345 graphic::{
346 BlendMode, BlendModeBuilder, DepthStencilMode, DepthStencilModeBuilder,
347 GraphicPipeline, GraphicPipelineInfo, GraphicPipelineInfoBuilder, StencilMode,
348 },
349 image::{
350 Image, ImageInfo, ImageInfoBuilder, ImageType, ImageViewInfo, ImageViewInfoBuilder,
351 SampleCount,
352 },
353 physical_device::{
354 AccelerationStructureProperties, PhysicalDevice, RayQueryFeatures,
355 RayTraceFeatures, RayTraceProperties, Vulkan10Features, Vulkan10Limits,
356 Vulkan10Properties, Vulkan11Features, Vulkan11Properties, Vulkan12Features,
357 Vulkan12Properties,
358 },
359 ray_trace::{
360 RayTracePipeline, RayTracePipelineInfo, RayTracePipelineInfoBuilder,
361 RayTraceShaderGroup, RayTraceShaderGroupType,
362 },
363 render_pass::ResolveMode,
364 shader::{
365 SamplerInfo, SamplerInfoBuilder, Shader, ShaderBuilder, ShaderCode,
366 SpecializationInfo,
367 },
368 surface::Surface,
369 swapchain::{
370 Swapchain, SwapchainError, SwapchainImage, SwapchainInfo, SwapchainInfoBuilder,
371 },
372 },
373 graph::{
374 Bind, ClearColorValue, RenderGraph, Unbind,
375 node::{
376 AccelerationStructureLeaseNode, AccelerationStructureNode,
377 AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode,
378 BufferNode, ImageLeaseNode, ImageNode, SwapchainImageNode,
379 },
380 pass_ref::{PassRef, PipelinePassRef},
381 },
382 pool::{
383 Lease, Pool, PoolInfo, PoolInfoBuilder,
384 alias::{Alias, AliasPool},
385 fifo::FifoPool,
386 hash::HashPool,
387 lazy::LazyPool,
388 },
389 };
390}
391
392pub use self::display::{Display, DisplayError, DisplayInfo, DisplayInfoBuilder, ResolverPool};