mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-22 14:56:42 +00:00
Document data layout in shaders (#2133)
* Document data layout in shaders * Marc fix * Some rewriting, separate SPIR-V/GLSL layout from alignment rules * Small change * Explicitly mention the SPIR-V decorations * Marc fix 2 * More small wording changes
This commit is contained in:
parent
1594b24495
commit
758151b087
@ -90,18 +90,13 @@
|
|||||||
//! Using uniform/storage texel buffers requires creating a *buffer view*. See [the `view` module]
|
//! Using uniform/storage texel buffers requires creating a *buffer view*. See [the `view` module]
|
||||||
//! for how to create a buffer view.
|
//! for how to create a buffer view.
|
||||||
//!
|
//!
|
||||||
//! # A note on endianness
|
//! See also [the `shader` module documentation] for information about how buffer contents need to
|
||||||
//!
|
//! be laid out in accordance with the shader interface.
|
||||||
//! The Vulkan specification requires that a Vulkan implementation has runtime support for the
|
|
||||||
//! types [`u8`], [`u16`], [`u32`], [`u64`] as well as their signed versions, as well as [`f32`]
|
|
||||||
//! and [`f64`] on the host, and that the representation and endianness of these types matches
|
|
||||||
//! those on the device. This means that if you have for example a `Subbuffer<[u32]>`, you can be
|
|
||||||
//! sure that it is represented the same way on the host as it is on the device, and you don't need
|
|
||||||
//! to worry about converting the endianness.
|
|
||||||
//!
|
//!
|
||||||
//! [`RawBuffer`]: self::sys::RawBuffer
|
//! [`RawBuffer`]: self::sys::RawBuffer
|
||||||
//! [`SubbufferAllocator`]: self::allocator::SubbufferAllocator
|
//! [`SubbufferAllocator`]: self::allocator::SubbufferAllocator
|
||||||
//! [the `view` module]: self::view
|
//! [the `view` module]: self::view
|
||||||
|
//! [the `shader` module documentation]: crate::shader
|
||||||
|
|
||||||
pub use self::{subbuffer::Subbuffer, usage::BufferUsage};
|
pub use self::{subbuffer::Subbuffer, usage::BufferUsage};
|
||||||
use self::{
|
use self::{
|
||||||
|
@ -228,7 +228,7 @@ mod tests {
|
|||||||
|
|
||||||
let cb_allocator =
|
let cb_allocator =
|
||||||
StandardCommandBufferAllocator::new(device.clone(), Default::default());
|
StandardCommandBufferAllocator::new(device.clone(), Default::default());
|
||||||
let mut cbb = AutoCommandBufferBuilder::primary(
|
let cbb = AutoCommandBufferBuilder::primary(
|
||||||
&cb_allocator,
|
&cb_allocator,
|
||||||
queue.queue_family_index(),
|
queue.queue_family_index(),
|
||||||
CommandBufferUsage::OneTimeSubmit,
|
CommandBufferUsage::OneTimeSubmit,
|
||||||
|
@ -16,6 +16,120 @@
|
|||||||
//! The vulkano library can parse and introspect SPIR-V code, but it does not fully validate the
|
//! The vulkano library can parse and introspect SPIR-V code, but it does not fully validate the
|
||||||
//! code. You are encouraged to use the `vulkano-shaders` crate that will generate Rust code that
|
//! code. You are encouraged to use the `vulkano-shaders` crate that will generate Rust code that
|
||||||
//! wraps around vulkano's shaders API.
|
//! wraps around vulkano's shaders API.
|
||||||
|
//!
|
||||||
|
//! # Shader interface
|
||||||
|
//!
|
||||||
|
//! Vulkan has specific rules for interfacing shaders with each other, and with other parts
|
||||||
|
//! of a program.
|
||||||
|
//!
|
||||||
|
//! ## Endianness
|
||||||
|
//!
|
||||||
|
//! The Vulkan specification requires that a Vulkan implementation has runtime support for the
|
||||||
|
//! types [`u8`], [`u16`], [`u32`], [`u64`] as well as their signed versions, as well as [`f32`]
|
||||||
|
//! and [`f64`] on the host, and that the representation and endianness of these types matches
|
||||||
|
//! those on the device. This means that if you have for example a `Subbuffer<u32>`, you can be
|
||||||
|
//! sure that it is represented the same way on the host as it is on the device, and you don't need
|
||||||
|
//! to worry about converting the endianness.
|
||||||
|
//!
|
||||||
|
//! ## Layout of data
|
||||||
|
//!
|
||||||
|
//! When buffers, push constants or other user-provided data are accessed in shaders,
|
||||||
|
//! the shader expects the values inside to be laid out in a specific way. For every uniform buffer,
|
||||||
|
//! storage buffer or push constant block, the SPIR-V specification requires the SPIR-V code to
|
||||||
|
//! provide the `Offset` decoration for every member of a struct, indicating where it is placed
|
||||||
|
//! relative to the start of the struct. If there are arrays or matrices among the variables, the
|
||||||
|
//! SPIR-V code must also provide an `ArrayStride` or `MatrixStride` decoration for them,
|
||||||
|
//! indicating the number of bytes between the start of each element in the array or column in the
|
||||||
|
//! matrix. When providing data to shaders, you must make sure that your data is placed at the
|
||||||
|
//! locations indicated within the SPIR-V code, or the shader will read the wrong data and produce
|
||||||
|
//! nonsense.
|
||||||
|
//!
|
||||||
|
//! GLSL does not require you to give explicit offsets and/or strides to your variables (although
|
||||||
|
//! it has the option to provide them if you wish). Instead, the shader compiler automatically
|
||||||
|
//! assigns every variable an offset, increasing in the order you declare them in.
|
||||||
|
//! To know the exact offsets that will be used, so that you can lay out your data appropriately,
|
||||||
|
//! you must know the alignment rules that the shader compiler uses. The shader compiler will
|
||||||
|
//! always give a variable the smallest offset that fits the alignment rules and doesn't overlap
|
||||||
|
//! with the previous variable. The shader compiler uses default alignment rules depending on the
|
||||||
|
//! type of block, but you can specify another layout by using the `layout` qualifier.
|
||||||
|
//!
|
||||||
|
//! ## Alignment rules
|
||||||
|
//!
|
||||||
|
//! The offset of each variable from the start of a block, matrix or array must be a
|
||||||
|
//! multiple of a certain number, which is called its *alignment*. The stride of an array or matrix
|
||||||
|
//! must likewise be a multiple of this number. An alignment is always a power-of-two value.
|
||||||
|
//! Regardless of whether the offset/stride is provided manually in the compiled SPIR-V code,
|
||||||
|
//! or assigned automatically by the shader compiler, all variable offsets/strides in a shader must
|
||||||
|
//! follow these alignment rules.
|
||||||
|
//!
|
||||||
|
//! Three sets of [alignment rules] are supported by Vulkan. Each one has a GLSL qualifier that
|
||||||
|
//! you can place in front of a block, to make the shader compiler use that layout for the block.
|
||||||
|
//! If you don't provide this qualifier, it will use a default alignment.
|
||||||
|
//!
|
||||||
|
//! - **Scalar alignment** (GLSL qualifier: `layout(scalar)`, requires the
|
||||||
|
//! [`GL_EXT_scalar_block_layout`] GLSL extension). This is the same as the C alignment,
|
||||||
|
//! expressed in Rust with the
|
||||||
|
//! [`#[repr(C)]`](https://doc.rust-lang.org/nomicon/other-reprs.html#reprc) attribute.
|
||||||
|
//! The shader compiler does not use this alignment by default, so you must use the GLSL
|
||||||
|
//! qualifier. You must also enable the [`scalar_block_layout`] feature in Vulkan.
|
||||||
|
//! - **Base alignment**, also known as **std430** (GLSL qualifier: `layout(std430)`).
|
||||||
|
//! The shader compiler uses this alignment by default for all shader data except uniform buffers.
|
||||||
|
//! If you use the base alignment for a uniform buffer, you must also enable the
|
||||||
|
//! [`uniform_buffer_standard_layout`] feature in Vulkan.
|
||||||
|
//! - **Extended alignment**, also known as **std140** (GLSL qualifier: `layout(std140)`).
|
||||||
|
//! The shader compiler uses this alignment by default for uniform buffers.
|
||||||
|
//!
|
||||||
|
//! Each alignment type is a subset of the ones above it, so if something adheres to the extended
|
||||||
|
//! alignment rules, it also follows the rules for the base and scalar alignments.
|
||||||
|
//!
|
||||||
|
//! In all three of these alignment rules, a primitive/scalar value with a size of N bytes has an
|
||||||
|
//! alignment of N, meaning that it must have an offset that is a multiple of its size,
|
||||||
|
//! like in C or Rust. For example, a `float` (like a Rust `f32`) has a size of 4 bytes,
|
||||||
|
//! and an alignment of 4.
|
||||||
|
//!
|
||||||
|
//! The differences between the alignment rules are in how compound types (vectors, matrices,
|
||||||
|
//! arrays and structs) are expected to be laid out. For a compound type with an element whose
|
||||||
|
//! alignment is N, the scalar alignment considers the alignment of the compound type to be also N.
|
||||||
|
//! However, the base and extended alignments are stricter:
|
||||||
|
//!
|
||||||
|
//! | GLSL type | Scalar | Base | Extended |
|
||||||
|
//! |-----------|-----------------|-----------------|--------------------------|
|
||||||
|
//! | primitive | N | N | N |
|
||||||
|
//! | `vec2` | N | N * 2 | N * 2 |
|
||||||
|
//! | `vec3` | N | N * 4 | N * 4 |
|
||||||
|
//! | `vec4` | N | N * 4 | N * 4 |
|
||||||
|
//! | array | N | N | max(N, 16) |
|
||||||
|
//! | `struct` | N<sub>max</sub> | N<sub>max</sub> | max(N<sub>max</sub>, 16) |
|
||||||
|
//!
|
||||||
|
//! In the base and extended alignment, the alignment of a vector is the size of the whole vector,
|
||||||
|
//! rather than the size of its individual elements as is the case in the scalar alignment.
|
||||||
|
//! But note that, because alignment must be a power of two, the alignment of `vec3` cannot be
|
||||||
|
//! N * 3; it must be N * 4, the same alignment as `vec4`. This means that it is not possible to
|
||||||
|
//! tightly pack multiple `vec3` values (e.g. in an array); there will always be empty padding
|
||||||
|
//! between them.
|
||||||
|
//!
|
||||||
|
//! In both the scalar and base alignment, the alignment of arrays and their elements is equal to
|
||||||
|
//! the alignment of the contained type. In the extended alignment, however, the alignment is
|
||||||
|
//! always at least 16 (the size of a `vec4`). Therefore, the minimum stride of the array can be
|
||||||
|
//! much greater than the element size. For example, in an array of `float`, the stride must be at
|
||||||
|
//! least 16, even though a `float` itself is only 4 bytes in size. Every `float` element will be
|
||||||
|
//! followed by at least 12 bytes of unused space.
|
||||||
|
//!
|
||||||
|
//! A matrix `matCxR` is considered equivalent to an array of column vectors `vecR[C]`.
|
||||||
|
//! In the base and extended alignments, that means that if the matrix has 3 rows, there will be
|
||||||
|
//! one element's worth of padding between the column vectors. In the extended alignment,
|
||||||
|
//! the alignment is also at least 16, further increasing the amount of padding between the
|
||||||
|
//! column vectors.
|
||||||
|
//!
|
||||||
|
//! The rules for `struct`s are similar to those of arrays. When the members of the struct have
|
||||||
|
//! different alignment requirements, the alignment of the struct as a whole is the maximum
|
||||||
|
//! of the alignments of its members. As with arrays, in the extended alignment, the alignment
|
||||||
|
//! of a struct is at least 16.
|
||||||
|
//!
|
||||||
|
//! [alignment rules]: <https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html#interfaces-resources-layout>
|
||||||
|
//! [`GL_EXT_scalar_block_layout`]: <https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GL_EXT_scalar_block_layout.txt>
|
||||||
|
//! [`scalar_block_layout`]: crate::device::Features::scalar_block_layout
|
||||||
|
//! [`uniform_buffer_standard_layout`]: crate::device::Features::uniform_buffer_standard_layout
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
descriptor_set::layout::DescriptorType,
|
descriptor_set::layout::DescriptorType,
|
||||||
|
Loading…
Reference in New Issue
Block a user