Skip to main content

wgpu_hal/vulkan/
conv.rs

1use alloc::vec::Vec;
2
3use ash::vk;
4
5impl super::PrivateCapabilities {
6    pub fn map_texture_format(&self, format: wgt::TextureFormat) -> vk::Format {
7        use ash::vk::Format as F;
8        use wgt::TextureFormat as Tf;
9        use wgt::{AstcBlock, AstcChannel};
10        match format {
11            Tf::R8Unorm => F::R8_UNORM,
12            Tf::R8Snorm => F::R8_SNORM,
13            Tf::R8Uint => F::R8_UINT,
14            Tf::R8Sint => F::R8_SINT,
15            Tf::R16Uint => F::R16_UINT,
16            Tf::R16Sint => F::R16_SINT,
17            Tf::R16Unorm => F::R16_UNORM,
18            Tf::R16Snorm => F::R16_SNORM,
19            Tf::R16Float => F::R16_SFLOAT,
20            Tf::Rg8Unorm => F::R8G8_UNORM,
21            Tf::Rg8Snorm => F::R8G8_SNORM,
22            Tf::Rg8Uint => F::R8G8_UINT,
23            Tf::Rg8Sint => F::R8G8_SINT,
24            Tf::Rg16Unorm => F::R16G16_UNORM,
25            Tf::Rg16Snorm => F::R16G16_SNORM,
26            Tf::R32Uint => F::R32_UINT,
27            Tf::R32Sint => F::R32_SINT,
28            Tf::R32Float => F::R32_SFLOAT,
29            Tf::Rg16Uint => F::R16G16_UINT,
30            Tf::Rg16Sint => F::R16G16_SINT,
31            Tf::Rg16Float => F::R16G16_SFLOAT,
32            Tf::Rgba8Unorm => F::R8G8B8A8_UNORM,
33            Tf::Rgba8UnormSrgb => F::R8G8B8A8_SRGB,
34            Tf::Bgra8UnormSrgb => F::B8G8R8A8_SRGB,
35            Tf::Rgba8Snorm => F::R8G8B8A8_SNORM,
36            Tf::Bgra8Unorm => F::B8G8R8A8_UNORM,
37            Tf::Rgba8Uint => F::R8G8B8A8_UINT,
38            Tf::Rgba8Sint => F::R8G8B8A8_SINT,
39            Tf::Rgb10a2Uint => F::A2B10G10R10_UINT_PACK32,
40            Tf::Rgb10a2Unorm => F::A2B10G10R10_UNORM_PACK32,
41            Tf::Rg11b10Ufloat => F::B10G11R11_UFLOAT_PACK32,
42            Tf::R64Uint => F::R64_UINT,
43            Tf::Rg32Uint => F::R32G32_UINT,
44            Tf::Rg32Sint => F::R32G32_SINT,
45            Tf::Rg32Float => F::R32G32_SFLOAT,
46            Tf::Rgba16Uint => F::R16G16B16A16_UINT,
47            Tf::Rgba16Sint => F::R16G16B16A16_SINT,
48            Tf::Rgba16Unorm => F::R16G16B16A16_UNORM,
49            Tf::Rgba16Snorm => F::R16G16B16A16_SNORM,
50            Tf::Rgba16Float => F::R16G16B16A16_SFLOAT,
51            Tf::Rgba32Uint => F::R32G32B32A32_UINT,
52            Tf::Rgba32Sint => F::R32G32B32A32_SINT,
53            Tf::Rgba32Float => F::R32G32B32A32_SFLOAT,
54            Tf::Depth32Float => F::D32_SFLOAT,
55            Tf::Depth32FloatStencil8 => F::D32_SFLOAT_S8_UINT,
56            Tf::Depth24Plus => {
57                if self.texture_d24 {
58                    F::X8_D24_UNORM_PACK32
59                } else {
60                    F::D32_SFLOAT
61                }
62            }
63            Tf::Depth24PlusStencil8 => {
64                if self.texture_d24_s8 {
65                    F::D24_UNORM_S8_UINT
66                } else {
67                    F::D32_SFLOAT_S8_UINT
68                }
69            }
70            Tf::Stencil8 => {
71                if self.texture_s8 {
72                    F::S8_UINT
73                } else if self.texture_d24_s8 {
74                    F::D24_UNORM_S8_UINT
75                } else {
76                    F::D32_SFLOAT_S8_UINT
77                }
78            }
79            Tf::Depth16Unorm => F::D16_UNORM,
80            Tf::NV12 => F::G8_B8R8_2PLANE_420_UNORM,
81            Tf::P010 => F::G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
82            Tf::Rgb9e5Ufloat => F::E5B9G9R9_UFLOAT_PACK32,
83            Tf::Bc1RgbaUnorm => F::BC1_RGBA_UNORM_BLOCK,
84            Tf::Bc1RgbaUnormSrgb => F::BC1_RGBA_SRGB_BLOCK,
85            Tf::Bc2RgbaUnorm => F::BC2_UNORM_BLOCK,
86            Tf::Bc2RgbaUnormSrgb => F::BC2_SRGB_BLOCK,
87            Tf::Bc3RgbaUnorm => F::BC3_UNORM_BLOCK,
88            Tf::Bc3RgbaUnormSrgb => F::BC3_SRGB_BLOCK,
89            Tf::Bc4RUnorm => F::BC4_UNORM_BLOCK,
90            Tf::Bc4RSnorm => F::BC4_SNORM_BLOCK,
91            Tf::Bc5RgUnorm => F::BC5_UNORM_BLOCK,
92            Tf::Bc5RgSnorm => F::BC5_SNORM_BLOCK,
93            Tf::Bc6hRgbUfloat => F::BC6H_UFLOAT_BLOCK,
94            Tf::Bc6hRgbFloat => F::BC6H_SFLOAT_BLOCK,
95            Tf::Bc7RgbaUnorm => F::BC7_UNORM_BLOCK,
96            Tf::Bc7RgbaUnormSrgb => F::BC7_SRGB_BLOCK,
97            Tf::Etc2Rgb8Unorm => F::ETC2_R8G8B8_UNORM_BLOCK,
98            Tf::Etc2Rgb8UnormSrgb => F::ETC2_R8G8B8_SRGB_BLOCK,
99            Tf::Etc2Rgb8A1Unorm => F::ETC2_R8G8B8A1_UNORM_BLOCK,
100            Tf::Etc2Rgb8A1UnormSrgb => F::ETC2_R8G8B8A1_SRGB_BLOCK,
101            Tf::Etc2Rgba8Unorm => F::ETC2_R8G8B8A8_UNORM_BLOCK,
102            Tf::Etc2Rgba8UnormSrgb => F::ETC2_R8G8B8A8_SRGB_BLOCK,
103            Tf::EacR11Unorm => F::EAC_R11_UNORM_BLOCK,
104            Tf::EacR11Snorm => F::EAC_R11_SNORM_BLOCK,
105            Tf::EacRg11Unorm => F::EAC_R11G11_UNORM_BLOCK,
106            Tf::EacRg11Snorm => F::EAC_R11G11_SNORM_BLOCK,
107            Tf::Astc { block, channel } => match channel {
108                AstcChannel::Unorm => match block {
109                    AstcBlock::B4x4 => F::ASTC_4X4_UNORM_BLOCK,
110                    AstcBlock::B5x4 => F::ASTC_5X4_UNORM_BLOCK,
111                    AstcBlock::B5x5 => F::ASTC_5X5_UNORM_BLOCK,
112                    AstcBlock::B6x5 => F::ASTC_6X5_UNORM_BLOCK,
113                    AstcBlock::B6x6 => F::ASTC_6X6_UNORM_BLOCK,
114                    AstcBlock::B8x5 => F::ASTC_8X5_UNORM_BLOCK,
115                    AstcBlock::B8x6 => F::ASTC_8X6_UNORM_BLOCK,
116                    AstcBlock::B8x8 => F::ASTC_8X8_UNORM_BLOCK,
117                    AstcBlock::B10x5 => F::ASTC_10X5_UNORM_BLOCK,
118                    AstcBlock::B10x6 => F::ASTC_10X6_UNORM_BLOCK,
119                    AstcBlock::B10x8 => F::ASTC_10X8_UNORM_BLOCK,
120                    AstcBlock::B10x10 => F::ASTC_10X10_UNORM_BLOCK,
121                    AstcBlock::B12x10 => F::ASTC_12X10_UNORM_BLOCK,
122                    AstcBlock::B12x12 => F::ASTC_12X12_UNORM_BLOCK,
123                },
124                AstcChannel::UnormSrgb => match block {
125                    AstcBlock::B4x4 => F::ASTC_4X4_SRGB_BLOCK,
126                    AstcBlock::B5x4 => F::ASTC_5X4_SRGB_BLOCK,
127                    AstcBlock::B5x5 => F::ASTC_5X5_SRGB_BLOCK,
128                    AstcBlock::B6x5 => F::ASTC_6X5_SRGB_BLOCK,
129                    AstcBlock::B6x6 => F::ASTC_6X6_SRGB_BLOCK,
130                    AstcBlock::B8x5 => F::ASTC_8X5_SRGB_BLOCK,
131                    AstcBlock::B8x6 => F::ASTC_8X6_SRGB_BLOCK,
132                    AstcBlock::B8x8 => F::ASTC_8X8_SRGB_BLOCK,
133                    AstcBlock::B10x5 => F::ASTC_10X5_SRGB_BLOCK,
134                    AstcBlock::B10x6 => F::ASTC_10X6_SRGB_BLOCK,
135                    AstcBlock::B10x8 => F::ASTC_10X8_SRGB_BLOCK,
136                    AstcBlock::B10x10 => F::ASTC_10X10_SRGB_BLOCK,
137                    AstcBlock::B12x10 => F::ASTC_12X10_SRGB_BLOCK,
138                    AstcBlock::B12x12 => F::ASTC_12X12_SRGB_BLOCK,
139                },
140                AstcChannel::Hdr => match block {
141                    AstcBlock::B4x4 => F::ASTC_4X4_SFLOAT_BLOCK_EXT,
142                    AstcBlock::B5x4 => F::ASTC_5X4_SFLOAT_BLOCK_EXT,
143                    AstcBlock::B5x5 => F::ASTC_5X5_SFLOAT_BLOCK_EXT,
144                    AstcBlock::B6x5 => F::ASTC_6X5_SFLOAT_BLOCK_EXT,
145                    AstcBlock::B6x6 => F::ASTC_6X6_SFLOAT_BLOCK_EXT,
146                    AstcBlock::B8x5 => F::ASTC_8X5_SFLOAT_BLOCK_EXT,
147                    AstcBlock::B8x6 => F::ASTC_8X6_SFLOAT_BLOCK_EXT,
148                    AstcBlock::B8x8 => F::ASTC_8X8_SFLOAT_BLOCK_EXT,
149                    AstcBlock::B10x5 => F::ASTC_10X5_SFLOAT_BLOCK_EXT,
150                    AstcBlock::B10x6 => F::ASTC_10X6_SFLOAT_BLOCK_EXT,
151                    AstcBlock::B10x8 => F::ASTC_10X8_SFLOAT_BLOCK_EXT,
152                    AstcBlock::B10x10 => F::ASTC_10X10_SFLOAT_BLOCK_EXT,
153                    AstcBlock::B12x10 => F::ASTC_12X10_SFLOAT_BLOCK_EXT,
154                    AstcBlock::B12x12 => F::ASTC_12X12_SFLOAT_BLOCK_EXT,
155                },
156            },
157        }
158    }
159}
160
161pub fn map_vk_surface_formats(
162    sf: vk::SurfaceFormatKHR,
163) -> Option<(wgt::TextureFormat, wgt::SurfaceColorSpace)> {
164    use ash::vk::Format as F;
165    use wgt::TextureFormat as Tf;
166    // Format list we care about pulled from https://vulkan.gpuinfo.org/listsurfaceformats.php.
167    let format = match sf.format {
168        F::B8G8R8A8_UNORM => Tf::Bgra8Unorm,
169        F::B8G8R8A8_SRGB => Tf::Bgra8UnormSrgb,
170        F::R8G8B8A8_SNORM => Tf::Rgba8Snorm,
171        F::R8G8B8A8_UNORM => Tf::Rgba8Unorm,
172        F::R8G8B8A8_SRGB => Tf::Rgba8UnormSrgb,
173        F::R16G16B16A16_SNORM => Tf::Rgba16Snorm,
174        F::R16G16B16A16_UNORM => Tf::Rgba16Unorm,
175        F::R16G16B16A16_SFLOAT => Tf::Rgba16Float,
176        F::A2B10G10R10_UNORM_PACK32 => Tf::Rgb10a2Unorm,
177        _ => return None,
178    };
179    let color_space = map_vk_color_space(sf.color_space)?;
180    Some((format, color_space))
181}
182
183pub fn map_vk_color_space(color_space: vk::ColorSpaceKHR) -> Option<wgt::SurfaceColorSpace> {
184    use wgt::SurfaceColorSpace as Scs;
185    Some(match color_space {
186        vk::ColorSpaceKHR::SRGB_NONLINEAR => Scs::Srgb,
187        vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT => Scs::ExtendedSrgbLinear,
188        vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT => Scs::ExtendedSrgb,
189        vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT => Scs::DisplayP3,
190        vk::ColorSpaceKHR::HDR10_ST2084_EXT => Scs::Bt2100Pq,
191        vk::ColorSpaceKHR::HDR10_HLG_EXT => Scs::Bt2100Hlg,
192        _ => return None,
193    })
194}
195
196pub fn map_surface_color_space(color_space: wgt::SurfaceColorSpace) -> vk::ColorSpaceKHR {
197    use wgt::SurfaceColorSpace as Scs;
198    match color_space {
199        Scs::Auto => unreachable!("wgpu-core resolves `Auto` before configuring the surface"),
200        Scs::Srgb => vk::ColorSpaceKHR::SRGB_NONLINEAR,
201        Scs::ExtendedSrgbLinear => vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT,
202        Scs::ExtendedSrgb => vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT,
203        Scs::DisplayP3 => vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT,
204        Scs::Bt2100Pq => vk::ColorSpaceKHR::HDR10_ST2084_EXT,
205        Scs::Bt2100Hlg => vk::ColorSpaceKHR::HDR10_HLG_EXT,
206        Scs::ExtendedDisplayP3 => {
207            unreachable!("`ExtendedDisplayP3` is never reported in the Vulkan surface capabilities")
208        }
209    }
210}
211
212impl crate::Attachment<'_, super::TextureView> {
213    pub(super) fn make_attachment_key(&self, ops: crate::AttachmentOps) -> super::AttachmentKey {
214        super::AttachmentKey {
215            format: self.view.raw_format,
216            layout: derive_image_layout(self.usage, self.view.format),
217            ops,
218        }
219    }
220}
221
222impl crate::ColorAttachment<'_, super::TextureView> {
223    pub(super) unsafe fn make_vk_clear_color(&self) -> vk::ClearColorValue {
224        let cv = &self.clear_value;
225        match self.target.view.format.sample_type(None, None).unwrap() {
226            wgt::TextureSampleType::Float { .. } => vk::ClearColorValue {
227                float32: [cv.r as f32, cv.g as f32, cv.b as f32, cv.a as f32],
228            },
229            wgt::TextureSampleType::Sint => vk::ClearColorValue {
230                int32: [cv.r as i32, cv.g as i32, cv.b as i32, cv.a as i32],
231            },
232            wgt::TextureSampleType::Uint => vk::ClearColorValue {
233                uint32: [cv.r as u32, cv.g as u32, cv.b as u32, cv.a as u32],
234            },
235            wgt::TextureSampleType::Depth => unreachable!(),
236        }
237    }
238}
239
240pub fn derive_image_layout(usage: wgt::TextureUses, format: wgt::TextureFormat) -> vk::ImageLayout {
241    let is_color = !format.is_depth_stencil_format();
242    match usage {
243        wgt::TextureUses::UNINITIALIZED => vk::ImageLayout::UNDEFINED,
244        wgt::TextureUses::COPY_SRC => vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
245        wgt::TextureUses::COPY_DST => vk::ImageLayout::TRANSFER_DST_OPTIMAL,
246        wgt::TextureUses::RESOURCE if is_color => vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
247        wgt::TextureUses::COLOR_TARGET => vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
248        _ => {
249            if usage == wgt::TextureUses::PRESENT {
250                vk::ImageLayout::PRESENT_SRC_KHR
251            } else if is_color {
252                vk::ImageLayout::GENERAL
253            } else {
254                match (
255                    usage.contains(wgt::TextureUses::DEPTH_WRITE),
256                    usage.contains(wgt::TextureUses::STENCIL_WRITE),
257                ) {
258                    (true, true) => vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
259                    (false, true) => vk::ImageLayout::DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL,
260                    (true, false) => vk::ImageLayout::DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL,
261                    (false, false) => vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL,
262                }
263            }
264        }
265    }
266}
267
268pub fn map_queue_family(family: crate::QueueFamily) -> u32 {
269    match family {
270        crate::QueueFamily::Explicit(index) => index,
271        crate::QueueFamily::External => vk::QUEUE_FAMILY_EXTERNAL,
272        crate::QueueFamily::Foreign => vk::QUEUE_FAMILY_FOREIGN_EXT,
273    }
274}
275
276/// Image usages of which an image must have at least one to be viewable, per
277/// `VUID-VkImageViewCreateInfo-image-04441`.
278pub const VIEWABLE_IMAGE_USAGES: vk::ImageUsageFlags = vk::ImageUsageFlags::from_raw(
279    vk::ImageUsageFlags::SAMPLED.as_raw()
280        | vk::ImageUsageFlags::STORAGE.as_raw()
281        | vk::ImageUsageFlags::COLOR_ATTACHMENT.as_raw()
282        | vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT.as_raw()
283        | vk::ImageUsageFlags::INPUT_ATTACHMENT.as_raw()
284        | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT.as_raw(),
285);
286
287pub fn map_texture_usage(usage: wgt::TextureUses) -> vk::ImageUsageFlags {
288    let mut flags = vk::ImageUsageFlags::empty();
289    if usage.contains(wgt::TextureUses::COPY_SRC) {
290        flags |= vk::ImageUsageFlags::TRANSFER_SRC;
291    }
292    if usage.contains(wgt::TextureUses::COPY_DST) {
293        flags |= vk::ImageUsageFlags::TRANSFER_DST;
294    }
295    if usage.contains(wgt::TextureUses::RESOURCE) {
296        flags |= vk::ImageUsageFlags::SAMPLED;
297    }
298    if usage.contains(wgt::TextureUses::COLOR_TARGET) {
299        flags |= vk::ImageUsageFlags::COLOR_ATTACHMENT;
300    }
301    if usage.intersects(
302        wgt::TextureUses::DEPTH_READ
303            | wgt::TextureUses::DEPTH_WRITE
304            | wgt::TextureUses::STENCIL_READ
305            | wgt::TextureUses::STENCIL_WRITE,
306    ) {
307        flags |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT;
308    }
309    if usage.intersects(
310        wgt::TextureUses::STORAGE_READ_ONLY
311            | wgt::TextureUses::STORAGE_WRITE_ONLY
312            | wgt::TextureUses::STORAGE_READ_WRITE
313            | wgt::TextureUses::STORAGE_ATOMIC,
314    ) {
315        flags |= vk::ImageUsageFlags::STORAGE;
316    }
317    if usage.contains(wgt::TextureUses::TRANSIENT) {
318        flags |= vk::ImageUsageFlags::TRANSIENT_ATTACHMENT;
319    }
320    flags
321}
322
323pub fn map_texture_usage_to_barrier(
324    usage: wgt::TextureUses,
325    queue_flags: vk::QueueFlags,
326    support_store_op_none: bool,
327    features: wgt::Features,
328) -> (vk::PipelineStageFlags, vk::AccessFlags) {
329    let mut stages = vk::PipelineStageFlags::empty();
330    let mut access = vk::AccessFlags::empty();
331    let shader_stages = shader_stages(queue_flags, features);
332
333    if usage.contains(wgt::TextureUses::COPY_SRC) {
334        stages |= vk::PipelineStageFlags::TRANSFER;
335        access |= vk::AccessFlags::TRANSFER_READ;
336    }
337    if usage.contains(wgt::TextureUses::COPY_DST) {
338        stages |= vk::PipelineStageFlags::TRANSFER;
339        access |= vk::AccessFlags::TRANSFER_WRITE;
340    }
341    if usage.contains(wgt::TextureUses::RESOURCE) {
342        stages |= shader_stages;
343        access |= vk::AccessFlags::SHADER_READ;
344    }
345    if usage.contains(wgt::TextureUses::COLOR_TARGET) {
346        stages |= vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT;
347        access |= vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE;
348    }
349    if usage.intersects(wgt::TextureUses::DEPTH_READ | wgt::TextureUses::STENCIL_READ) {
350        stages |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
351            | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
352        access |= if !support_store_op_none {
353            // If `vk::AttachmentStoreOp::NONE` isn't available we use `vk::AttachmentStoreOp::Store`
354            // for readonly depth-stencil attachments, which needs write access to avoid validation error.
355            vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ
356                | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE
357        } else {
358            vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ
359        }
360    }
361    if usage.intersects(wgt::TextureUses::DEPTH_WRITE | wgt::TextureUses::STENCIL_WRITE) {
362        stages |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
363            | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
364        access |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ
365            | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE;
366    }
367    if usage.intersects(
368        wgt::TextureUses::STORAGE_READ_ONLY
369            | wgt::TextureUses::STORAGE_READ_WRITE
370            | wgt::TextureUses::STORAGE_ATOMIC,
371    ) {
372        stages |= shader_stages;
373        access |= vk::AccessFlags::SHADER_READ;
374    }
375    if usage.intersects(
376        wgt::TextureUses::STORAGE_WRITE_ONLY
377            | wgt::TextureUses::STORAGE_READ_WRITE
378            | wgt::TextureUses::STORAGE_ATOMIC,
379    ) {
380        stages |= shader_stages;
381        access |= vk::AccessFlags::SHADER_WRITE;
382    }
383
384    if usage == wgt::TextureUses::UNINITIALIZED || usage == wgt::TextureUses::PRESENT {
385        (
386            vk::PipelineStageFlags::TOP_OF_PIPE,
387            vk::AccessFlags::empty(),
388        )
389    } else {
390        (stages, access)
391    }
392}
393
394pub fn map_vk_image_usage(usage: vk::ImageUsageFlags) -> wgt::TextureUses {
395    let mut bits = wgt::TextureUses::empty();
396    if usage.contains(vk::ImageUsageFlags::TRANSFER_SRC) {
397        bits |= wgt::TextureUses::COPY_SRC;
398    }
399    if usage.contains(vk::ImageUsageFlags::TRANSFER_DST) {
400        bits |= wgt::TextureUses::COPY_DST;
401    }
402    if usage.contains(vk::ImageUsageFlags::SAMPLED) {
403        bits |= wgt::TextureUses::RESOURCE;
404    }
405    if usage.contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) {
406        bits |= wgt::TextureUses::COLOR_TARGET;
407    }
408    if usage.contains(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT) {
409        bits |= wgt::TextureUses::DEPTH_READ
410            | wgt::TextureUses::DEPTH_WRITE
411            | wgt::TextureUses::STENCIL_READ
412            | wgt::TextureUses::STENCIL_WRITE;
413    }
414    if usage.contains(vk::ImageUsageFlags::STORAGE) {
415        bits |= wgt::TextureUses::STORAGE_READ_ONLY
416            | wgt::TextureUses::STORAGE_WRITE_ONLY
417            | wgt::TextureUses::STORAGE_READ_WRITE
418            | wgt::TextureUses::STORAGE_ATOMIC;
419    }
420    if usage.contains(vk::ImageUsageFlags::TRANSIENT_ATTACHMENT) {
421        bits |= wgt::TextureUses::TRANSIENT;
422    }
423    bits
424}
425
426pub fn map_texture_dimension(dim: wgt::TextureDimension) -> vk::ImageType {
427    match dim {
428        wgt::TextureDimension::D1 => vk::ImageType::TYPE_1D,
429        wgt::TextureDimension::D2 => vk::ImageType::TYPE_2D,
430        wgt::TextureDimension::D3 => vk::ImageType::TYPE_3D,
431    }
432}
433
434pub fn map_index_format(index_format: wgt::IndexFormat) -> vk::IndexType {
435    match index_format {
436        wgt::IndexFormat::Uint16 => vk::IndexType::UINT16,
437        wgt::IndexFormat::Uint32 => vk::IndexType::UINT32,
438    }
439}
440
441pub fn map_vertex_format(vertex_format: wgt::VertexFormat) -> vk::Format {
442    use wgt::VertexFormat as Vf;
443    match vertex_format {
444        Vf::Uint8 => vk::Format::R8_UINT,
445        Vf::Uint8x2 => vk::Format::R8G8_UINT,
446        Vf::Uint8x4 => vk::Format::R8G8B8A8_UINT,
447        Vf::Sint8 => vk::Format::R8_SINT,
448        Vf::Sint8x2 => vk::Format::R8G8_SINT,
449        Vf::Sint8x4 => vk::Format::R8G8B8A8_SINT,
450        Vf::Unorm8 => vk::Format::R8_UNORM,
451        Vf::Unorm8x2 => vk::Format::R8G8_UNORM,
452        Vf::Unorm8x4 => vk::Format::R8G8B8A8_UNORM,
453        Vf::Snorm8 => vk::Format::R8_SNORM,
454        Vf::Snorm8x2 => vk::Format::R8G8_SNORM,
455        Vf::Snorm8x4 => vk::Format::R8G8B8A8_SNORM,
456        Vf::Uint16 => vk::Format::R16_UINT,
457        Vf::Uint16x2 => vk::Format::R16G16_UINT,
458        Vf::Uint16x4 => vk::Format::R16G16B16A16_UINT,
459        Vf::Sint16 => vk::Format::R16_SINT,
460        Vf::Sint16x2 => vk::Format::R16G16_SINT,
461        Vf::Sint16x4 => vk::Format::R16G16B16A16_SINT,
462        Vf::Unorm16 => vk::Format::R16_UNORM,
463        Vf::Unorm16x2 => vk::Format::R16G16_UNORM,
464        Vf::Unorm16x4 => vk::Format::R16G16B16A16_UNORM,
465        Vf::Snorm16 => vk::Format::R16_SNORM,
466        Vf::Snorm16x2 => vk::Format::R16G16_SNORM,
467        Vf::Snorm16x4 => vk::Format::R16G16B16A16_SNORM,
468        Vf::Float16 => vk::Format::R16_SFLOAT,
469        Vf::Float16x2 => vk::Format::R16G16_SFLOAT,
470        Vf::Float16x4 => vk::Format::R16G16B16A16_SFLOAT,
471        Vf::Float32 => vk::Format::R32_SFLOAT,
472        Vf::Float32x2 => vk::Format::R32G32_SFLOAT,
473        Vf::Float32x3 => vk::Format::R32G32B32_SFLOAT,
474        Vf::Float32x4 => vk::Format::R32G32B32A32_SFLOAT,
475        Vf::Uint32 => vk::Format::R32_UINT,
476        Vf::Uint32x2 => vk::Format::R32G32_UINT,
477        Vf::Uint32x3 => vk::Format::R32G32B32_UINT,
478        Vf::Uint32x4 => vk::Format::R32G32B32A32_UINT,
479        Vf::Sint32 => vk::Format::R32_SINT,
480        Vf::Sint32x2 => vk::Format::R32G32_SINT,
481        Vf::Sint32x3 => vk::Format::R32G32B32_SINT,
482        Vf::Sint32x4 => vk::Format::R32G32B32A32_SINT,
483        Vf::Float64 => vk::Format::R64_SFLOAT,
484        Vf::Float64x2 => vk::Format::R64G64_SFLOAT,
485        Vf::Float64x3 => vk::Format::R64G64B64_SFLOAT,
486        Vf::Float64x4 => vk::Format::R64G64B64A64_SFLOAT,
487        Vf::Unorm10_10_10_2 => vk::Format::A2B10G10R10_UNORM_PACK32,
488        // Note that, unlike the unorm variant, this format has no mandatory format
489        // features in Vulkan, so vertex buffer support is not guaranteed.
490        Vf::Snorm10_10_10_2 => vk::Format::A2B10G10R10_SNORM_PACK32,
491        Vf::Unorm8x4Bgra => vk::Format::B8G8R8A8_UNORM,
492    }
493}
494
495pub fn map_aspects(aspects: crate::FormatAspects) -> vk::ImageAspectFlags {
496    let mut flags = vk::ImageAspectFlags::empty();
497    if aspects.contains(crate::FormatAspects::COLOR) {
498        flags |= vk::ImageAspectFlags::COLOR;
499    }
500    if aspects.contains(crate::FormatAspects::DEPTH) {
501        flags |= vk::ImageAspectFlags::DEPTH;
502    }
503    if aspects.contains(crate::FormatAspects::STENCIL) {
504        flags |= vk::ImageAspectFlags::STENCIL;
505    }
506    if aspects.contains(crate::FormatAspects::PLANE_0) {
507        flags |= vk::ImageAspectFlags::PLANE_0;
508    }
509    if aspects.contains(crate::FormatAspects::PLANE_1) {
510        flags |= vk::ImageAspectFlags::PLANE_1;
511    }
512    if aspects.contains(crate::FormatAspects::PLANE_2) {
513        flags |= vk::ImageAspectFlags::PLANE_2;
514    }
515    flags
516}
517
518pub fn map_attachment_ops(
519    op: crate::AttachmentOps,
520) -> (vk::AttachmentLoadOp, vk::AttachmentStoreOp) {
521    let load_op = if op.contains(crate::AttachmentOps::LOAD) {
522        vk::AttachmentLoadOp::LOAD
523    } else if op.contains(crate::AttachmentOps::LOAD_DONT_CARE) {
524        vk::AttachmentLoadOp::DONT_CARE
525    } else if op.contains(crate::AttachmentOps::LOAD_CLEAR) {
526        vk::AttachmentLoadOp::CLEAR
527    } else {
528        unreachable!()
529    };
530    let store_op = if op.contains(crate::AttachmentOps::STORE) {
531        vk::AttachmentStoreOp::STORE
532    } else if op.contains(crate::AttachmentOps::STORE_DISCARD) {
533        vk::AttachmentStoreOp::DONT_CARE
534    } else {
535        unreachable!()
536    };
537    (load_op, store_op)
538}
539
540pub fn map_present_mode(mode: wgt::PresentMode) -> vk::PresentModeKHR {
541    match mode {
542        wgt::PresentMode::Immediate => vk::PresentModeKHR::IMMEDIATE,
543        wgt::PresentMode::Mailbox => vk::PresentModeKHR::MAILBOX,
544        wgt::PresentMode::Fifo => vk::PresentModeKHR::FIFO,
545        wgt::PresentMode::FifoRelaxed => vk::PresentModeKHR::FIFO_RELAXED,
546        wgt::PresentMode::AutoNoVsync | wgt::PresentMode::AutoVsync => {
547            unreachable!("Cannot create swapchain with Auto PresentationMode")
548        }
549    }
550}
551
552pub fn map_vk_present_mode(mode: vk::PresentModeKHR) -> Option<wgt::PresentMode> {
553    // Not exposed in Ash yet.
554    const FIFO_LATEST_READY: vk::PresentModeKHR = vk::PresentModeKHR::from_raw(1_000_361_000);
555
556    // See https://registry.khronos.org/vulkan/specs/latest/man/html/VkPresentModeKHR.html
557    match mode {
558        vk::PresentModeKHR::IMMEDIATE => Some(wgt::PresentMode::Immediate),
559        vk::PresentModeKHR::MAILBOX => Some(wgt::PresentMode::Mailbox),
560        vk::PresentModeKHR::FIFO => Some(wgt::PresentMode::Fifo),
561        vk::PresentModeKHR::FIFO_RELAXED => Some(wgt::PresentMode::FifoRelaxed),
562
563        // Modes that aren't exposed yet.
564        vk::PresentModeKHR::SHARED_DEMAND_REFRESH => None,
565        vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH => None,
566        FIFO_LATEST_READY => None,
567
568        _ => {
569            log::debug!("Unrecognized present mode {mode:?}");
570            None
571        }
572    }
573}
574
575pub fn map_composite_alpha_mode(mode: wgt::CompositeAlphaMode) -> vk::CompositeAlphaFlagsKHR {
576    match mode {
577        wgt::CompositeAlphaMode::Opaque => vk::CompositeAlphaFlagsKHR::OPAQUE,
578        wgt::CompositeAlphaMode::PreMultiplied => vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
579        wgt::CompositeAlphaMode::PostMultiplied => vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED,
580        wgt::CompositeAlphaMode::Inherit => vk::CompositeAlphaFlagsKHR::INHERIT,
581        wgt::CompositeAlphaMode::Auto => unreachable!(),
582    }
583}
584
585pub fn map_vk_composite_alpha(flags: vk::CompositeAlphaFlagsKHR) -> Vec<wgt::CompositeAlphaMode> {
586    let mut modes = Vec::new();
587    if flags.contains(vk::CompositeAlphaFlagsKHR::OPAQUE) {
588        modes.push(wgt::CompositeAlphaMode::Opaque);
589    }
590    if flags.contains(vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED) {
591        modes.push(wgt::CompositeAlphaMode::PreMultiplied);
592    }
593    if flags.contains(vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED) {
594        modes.push(wgt::CompositeAlphaMode::PostMultiplied);
595    }
596    if flags.contains(vk::CompositeAlphaFlagsKHR::INHERIT) {
597        modes.push(wgt::CompositeAlphaMode::Inherit);
598    }
599    modes
600}
601
602pub fn map_buffer_usage(usage: wgt::BufferUses) -> vk::BufferUsageFlags {
603    let mut flags = vk::BufferUsageFlags::empty();
604    if usage.contains(wgt::BufferUses::COPY_SRC) {
605        flags |= vk::BufferUsageFlags::TRANSFER_SRC;
606    }
607    if usage.contains(wgt::BufferUses::COPY_DST) {
608        flags |= vk::BufferUsageFlags::TRANSFER_DST;
609    }
610    if usage.contains(wgt::BufferUses::UNIFORM) {
611        flags |= vk::BufferUsageFlags::UNIFORM_BUFFER;
612    }
613    if usage.intersects(wgt::BufferUses::STORAGE_READ_ONLY | wgt::BufferUses::STORAGE_READ_WRITE) {
614        flags |= vk::BufferUsageFlags::STORAGE_BUFFER;
615    }
616    if usage.contains(wgt::BufferUses::INDEX) {
617        flags |= vk::BufferUsageFlags::INDEX_BUFFER;
618    }
619    if usage.contains(wgt::BufferUses::VERTEX) {
620        flags |= vk::BufferUsageFlags::VERTEX_BUFFER;
621    }
622    if usage.contains(wgt::BufferUses::INDIRECT) {
623        flags |= vk::BufferUsageFlags::INDIRECT_BUFFER;
624    }
625    if usage.contains(wgt::BufferUses::ACCELERATION_STRUCTURE_SCRATCH) {
626        flags |= vk::BufferUsageFlags::STORAGE_BUFFER | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS;
627    }
628    if usage.intersects(
629        wgt::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT
630            | wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT,
631    ) {
632        flags |= vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR
633            | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS;
634    }
635    if usage.intersects(wgt::BufferUses::ACCELERATION_STRUCTURE_QUERY) {
636        flags |= vk::BufferUsageFlags::TRANSFER_DST;
637    }
638    if usage.intersects(wgt::BufferUses::RAY_TRACING_PIPELINE_SHADER_DATA) {
639        flags |= vk::BufferUsageFlags::SHADER_BINDING_TABLE_KHR
640            | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS;
641    }
642    flags
643}
644
645pub fn map_buffer_usage_to_barrier(
646    usage: wgt::BufferUses,
647    queue_flags: vk::QueueFlags,
648    features: wgt::Features,
649) -> (vk::PipelineStageFlags, vk::AccessFlags) {
650    let mut stages = vk::PipelineStageFlags::empty();
651    let mut access = vk::AccessFlags::empty();
652    let shader_stages = shader_stages(queue_flags, features);
653
654    if usage.contains(wgt::BufferUses::MAP_READ) {
655        stages |= vk::PipelineStageFlags::HOST;
656        access |= vk::AccessFlags::HOST_READ;
657    }
658    if usage.contains(wgt::BufferUses::MAP_WRITE) {
659        stages |= vk::PipelineStageFlags::HOST;
660        access |= vk::AccessFlags::HOST_WRITE;
661    }
662    if usage.contains(wgt::BufferUses::COPY_SRC) {
663        stages |= vk::PipelineStageFlags::TRANSFER;
664        access |= vk::AccessFlags::TRANSFER_READ;
665    }
666    if usage.contains(wgt::BufferUses::COPY_DST) {
667        stages |= vk::PipelineStageFlags::TRANSFER;
668        access |= vk::AccessFlags::TRANSFER_WRITE;
669    }
670    if usage.contains(wgt::BufferUses::UNIFORM) {
671        stages |= shader_stages;
672        access |= vk::AccessFlags::UNIFORM_READ;
673    }
674    if usage.intersects(wgt::BufferUses::STORAGE_READ_ONLY) {
675        stages |= shader_stages;
676        access |= vk::AccessFlags::SHADER_READ;
677    }
678    if usage.intersects(wgt::BufferUses::STORAGE_READ_WRITE) {
679        stages |= shader_stages;
680        access |= vk::AccessFlags::SHADER_READ | vk::AccessFlags::SHADER_WRITE;
681    }
682    if usage.contains(wgt::BufferUses::INDEX) {
683        stages |= vk::PipelineStageFlags::VERTEX_INPUT;
684        access |= vk::AccessFlags::INDEX_READ;
685    }
686    if usage.contains(wgt::BufferUses::VERTEX) {
687        stages |= vk::PipelineStageFlags::VERTEX_INPUT;
688        access |= vk::AccessFlags::VERTEX_ATTRIBUTE_READ;
689    }
690    if usage.contains(wgt::BufferUses::INDIRECT) {
691        stages |= vk::PipelineStageFlags::DRAW_INDIRECT;
692        access |= vk::AccessFlags::INDIRECT_COMMAND_READ;
693    }
694    if usage.intersects(
695        wgt::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT
696            | wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT
697            | wgt::BufferUses::ACCELERATION_STRUCTURE_SCRATCH,
698    ) {
699        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
700        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR
701            | vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR
702            | vk::AccessFlags::SHADER_READ;
703    }
704    if usage.contains(wgt::BufferUses::ACCELERATION_STRUCTURE_QUERY) {
705        stages |= vk::PipelineStageFlags::TRANSFER;
706        access |= vk::AccessFlags::TRANSFER_WRITE;
707    }
708
709    (stages, access)
710}
711
712fn shader_stages(queue_flags: vk::QueueFlags, features: wgt::Features) -> vk::PipelineStageFlags {
713    let mut stages = vk::PipelineStageFlags::empty();
714
715    if queue_flags.contains(vk::QueueFlags::GRAPHICS) {
716        stages |= vk::PipelineStageFlags::VERTEX_SHADER | vk::PipelineStageFlags::FRAGMENT_SHADER;
717    }
718    if queue_flags.contains(vk::QueueFlags::COMPUTE) {
719        stages |= vk::PipelineStageFlags::COMPUTE_SHADER;
720        // `cmd_trace_rays` counts as compute according to the vulkan spec.
721        // https://docs.vulkan.org/refpages/latest/refpages/source/vkCmdTraceRaysKHR.html#VUID-vkCmdTraceRaysKHR-commandBuffer-cmdpool
722        if features.contains(wgt::Features::EXPERIMENTAL_RAY_TRACING_PIPELINES) {
723            stages |= vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR;
724        }
725    }
726
727    stages
728}
729
730pub fn map_view_dimension(dim: wgt::TextureViewDimension) -> vk::ImageViewType {
731    match dim {
732        wgt::TextureViewDimension::D1 => vk::ImageViewType::TYPE_1D,
733        wgt::TextureViewDimension::D2 => vk::ImageViewType::TYPE_2D,
734        wgt::TextureViewDimension::D2Array => vk::ImageViewType::TYPE_2D_ARRAY,
735        wgt::TextureViewDimension::Cube => vk::ImageViewType::CUBE,
736        wgt::TextureViewDimension::CubeArray => vk::ImageViewType::CUBE_ARRAY,
737        wgt::TextureViewDimension::D3 => vk::ImageViewType::TYPE_3D,
738    }
739}
740
741pub fn map_copy_extent(extent: &crate::CopyExtent) -> vk::Extent3D {
742    vk::Extent3D {
743        width: extent.width,
744        height: extent.height,
745        depth: extent.depth,
746    }
747}
748
749pub fn map_subresource_range(
750    range: &wgt::ImageSubresourceRange,
751    format: wgt::TextureFormat,
752) -> vk::ImageSubresourceRange {
753    vk::ImageSubresourceRange {
754        aspect_mask: map_aspects(crate::FormatAspects::new(format, range.aspect)),
755        base_mip_level: range.base_mip_level,
756        level_count: range.mip_level_count.unwrap_or(vk::REMAINING_MIP_LEVELS),
757        base_array_layer: range.base_array_layer,
758        layer_count: range
759            .array_layer_count
760            .unwrap_or(vk::REMAINING_ARRAY_LAYERS),
761    }
762}
763
764// Special subresource range mapping for dealing with barriers
765// so that we account for the "hidden" depth aspect in emulated Stencil8.
766pub(super) fn map_subresource_range_combined_aspect(
767    range: &wgt::ImageSubresourceRange,
768    format: wgt::TextureFormat,
769    private_caps: &super::PrivateCapabilities,
770) -> vk::ImageSubresourceRange {
771    let mut range = map_subresource_range(range, format);
772    if !private_caps.texture_s8 && format == wgt::TextureFormat::Stencil8 {
773        range.aspect_mask |= vk::ImageAspectFlags::DEPTH;
774    }
775    range
776}
777
778pub fn map_subresource_layers(
779    base: &crate::TextureCopyBase,
780) -> (vk::ImageSubresourceLayers, vk::Offset3D) {
781    let offset = vk::Offset3D {
782        x: base.origin.x as i32,
783        y: base.origin.y as i32,
784        z: base.origin.z as i32,
785    };
786    let subresource = vk::ImageSubresourceLayers {
787        aspect_mask: map_aspects(base.aspect),
788        mip_level: base.mip_level,
789        base_array_layer: base.array_layer,
790        layer_count: 1,
791    };
792    (subresource, offset)
793}
794
795pub fn map_filter_mode(mode: wgt::FilterMode) -> vk::Filter {
796    match mode {
797        wgt::FilterMode::Nearest => vk::Filter::NEAREST,
798        wgt::FilterMode::Linear => vk::Filter::LINEAR,
799    }
800}
801
802pub fn map_mip_filter_mode(mode: wgt::MipmapFilterMode) -> vk::SamplerMipmapMode {
803    match mode {
804        wgt::MipmapFilterMode::Nearest => vk::SamplerMipmapMode::NEAREST,
805        wgt::MipmapFilterMode::Linear => vk::SamplerMipmapMode::LINEAR,
806    }
807}
808
809pub fn map_address_mode(mode: wgt::AddressMode) -> vk::SamplerAddressMode {
810    match mode {
811        wgt::AddressMode::ClampToEdge => vk::SamplerAddressMode::CLAMP_TO_EDGE,
812        wgt::AddressMode::Repeat => vk::SamplerAddressMode::REPEAT,
813        wgt::AddressMode::MirrorRepeat => vk::SamplerAddressMode::MIRRORED_REPEAT,
814        wgt::AddressMode::ClampToBorder => vk::SamplerAddressMode::CLAMP_TO_BORDER,
815        // wgt::AddressMode::MirrorClamp => vk::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE,
816    }
817}
818
819pub fn map_border_color(border_color: wgt::SamplerBorderColor) -> vk::BorderColor {
820    match border_color {
821        wgt::SamplerBorderColor::TransparentBlack | wgt::SamplerBorderColor::Zero => {
822            vk::BorderColor::FLOAT_TRANSPARENT_BLACK
823        }
824        wgt::SamplerBorderColor::OpaqueBlack => vk::BorderColor::FLOAT_OPAQUE_BLACK,
825        wgt::SamplerBorderColor::OpaqueWhite => vk::BorderColor::FLOAT_OPAQUE_WHITE,
826    }
827}
828
829pub fn map_comparison(fun: wgt::CompareFunction) -> vk::CompareOp {
830    use wgt::CompareFunction as Cf;
831    match fun {
832        Cf::Never => vk::CompareOp::NEVER,
833        Cf::Less => vk::CompareOp::LESS,
834        Cf::LessEqual => vk::CompareOp::LESS_OR_EQUAL,
835        Cf::Equal => vk::CompareOp::EQUAL,
836        Cf::GreaterEqual => vk::CompareOp::GREATER_OR_EQUAL,
837        Cf::Greater => vk::CompareOp::GREATER,
838        Cf::NotEqual => vk::CompareOp::NOT_EQUAL,
839        Cf::Always => vk::CompareOp::ALWAYS,
840    }
841}
842
843pub fn map_shader_stage(stage: wgt::ShaderStages) -> vk::ShaderStageFlags {
844    let mut flags = vk::ShaderStageFlags::empty();
845    if stage.contains(wgt::ShaderStages::VERTEX) {
846        flags |= vk::ShaderStageFlags::VERTEX;
847    }
848    if stage.contains(wgt::ShaderStages::FRAGMENT) {
849        flags |= vk::ShaderStageFlags::FRAGMENT;
850    }
851    if stage.contains(wgt::ShaderStages::COMPUTE) {
852        flags |= vk::ShaderStageFlags::COMPUTE;
853    }
854    if stage.contains(wgt::ShaderStages::TASK) {
855        flags |= vk::ShaderStageFlags::TASK_EXT;
856    }
857    if stage.contains(wgt::ShaderStages::MESH) {
858        flags |= vk::ShaderStageFlags::MESH_EXT;
859    }
860    if stage.contains(wgt::ShaderStages::RAY_GENERATION) {
861        flags |= vk::ShaderStageFlags::RAYGEN_KHR;
862    }
863    if stage.contains(wgt::ShaderStages::MISS) {
864        flags |= vk::ShaderStageFlags::MISS_KHR;
865    }
866    if stage.contains(wgt::ShaderStages::ANY_HIT) {
867        flags |= vk::ShaderStageFlags::ANY_HIT_KHR;
868    }
869    if stage.contains(wgt::ShaderStages::CLOSEST_HIT) {
870        flags |= vk::ShaderStageFlags::CLOSEST_HIT_KHR;
871    }
872    flags
873}
874
875pub fn map_binding_type(ty: wgt::BindingType) -> vk::DescriptorType {
876    match ty {
877        wgt::BindingType::Buffer {
878            ty,
879            has_dynamic_offset,
880            ..
881        } => match ty {
882            wgt::BufferBindingType::Storage { .. } => match has_dynamic_offset {
883                true => vk::DescriptorType::STORAGE_BUFFER_DYNAMIC,
884                false => vk::DescriptorType::STORAGE_BUFFER,
885            },
886            wgt::BufferBindingType::Uniform => match has_dynamic_offset {
887                true => vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC,
888                false => vk::DescriptorType::UNIFORM_BUFFER,
889            },
890        },
891        wgt::BindingType::Sampler { .. } => vk::DescriptorType::SAMPLER,
892        wgt::BindingType::Texture { .. } => vk::DescriptorType::SAMPLED_IMAGE,
893        wgt::BindingType::StorageTexture { .. } => vk::DescriptorType::STORAGE_IMAGE,
894        wgt::BindingType::AccelerationStructure { .. } => {
895            vk::DescriptorType::ACCELERATION_STRUCTURE_KHR
896        }
897        wgt::BindingType::ExternalTexture => unimplemented!(),
898    }
899}
900
901pub fn map_topology(topology: wgt::PrimitiveTopology) -> vk::PrimitiveTopology {
902    use wgt::PrimitiveTopology as Pt;
903    match topology {
904        Pt::PointList => vk::PrimitiveTopology::POINT_LIST,
905        Pt::LineList => vk::PrimitiveTopology::LINE_LIST,
906        Pt::LineStrip => vk::PrimitiveTopology::LINE_STRIP,
907        Pt::TriangleList => vk::PrimitiveTopology::TRIANGLE_LIST,
908        Pt::TriangleStrip => vk::PrimitiveTopology::TRIANGLE_STRIP,
909    }
910}
911
912pub fn map_polygon_mode(mode: wgt::PolygonMode) -> vk::PolygonMode {
913    match mode {
914        wgt::PolygonMode::Fill => vk::PolygonMode::FILL,
915        wgt::PolygonMode::Line => vk::PolygonMode::LINE,
916        wgt::PolygonMode::Point => vk::PolygonMode::POINT,
917    }
918}
919
920pub fn map_front_face(front_face: wgt::FrontFace) -> vk::FrontFace {
921    match front_face {
922        wgt::FrontFace::Cw => vk::FrontFace::CLOCKWISE,
923        wgt::FrontFace::Ccw => vk::FrontFace::COUNTER_CLOCKWISE,
924    }
925}
926
927pub fn map_cull_face(face: wgt::Face) -> vk::CullModeFlags {
928    match face {
929        wgt::Face::Front => vk::CullModeFlags::FRONT,
930        wgt::Face::Back => vk::CullModeFlags::BACK,
931    }
932}
933
934pub fn map_stencil_op(op: wgt::StencilOperation) -> vk::StencilOp {
935    use wgt::StencilOperation as So;
936    match op {
937        So::Keep => vk::StencilOp::KEEP,
938        So::Zero => vk::StencilOp::ZERO,
939        So::Replace => vk::StencilOp::REPLACE,
940        So::Invert => vk::StencilOp::INVERT,
941        So::IncrementClamp => vk::StencilOp::INCREMENT_AND_CLAMP,
942        So::IncrementWrap => vk::StencilOp::INCREMENT_AND_WRAP,
943        So::DecrementClamp => vk::StencilOp::DECREMENT_AND_CLAMP,
944        So::DecrementWrap => vk::StencilOp::DECREMENT_AND_WRAP,
945    }
946}
947
948pub fn map_stencil_face(
949    face: &wgt::StencilFaceState,
950    compare_mask: u32,
951    write_mask: u32,
952) -> vk::StencilOpState {
953    vk::StencilOpState {
954        fail_op: map_stencil_op(face.fail_op),
955        pass_op: map_stencil_op(face.pass_op),
956        depth_fail_op: map_stencil_op(face.depth_fail_op),
957        compare_op: map_comparison(face.compare),
958        compare_mask,
959        write_mask,
960        reference: 0,
961    }
962}
963
964fn map_blend_factor(factor: wgt::BlendFactor) -> vk::BlendFactor {
965    use wgt::BlendFactor as Bf;
966    match factor {
967        Bf::Zero => vk::BlendFactor::ZERO,
968        Bf::One => vk::BlendFactor::ONE,
969        Bf::Src => vk::BlendFactor::SRC_COLOR,
970        Bf::OneMinusSrc => vk::BlendFactor::ONE_MINUS_SRC_COLOR,
971        Bf::SrcAlpha => vk::BlendFactor::SRC_ALPHA,
972        Bf::OneMinusSrcAlpha => vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
973        Bf::Dst => vk::BlendFactor::DST_COLOR,
974        Bf::OneMinusDst => vk::BlendFactor::ONE_MINUS_DST_COLOR,
975        Bf::DstAlpha => vk::BlendFactor::DST_ALPHA,
976        Bf::OneMinusDstAlpha => vk::BlendFactor::ONE_MINUS_DST_ALPHA,
977        Bf::SrcAlphaSaturated => vk::BlendFactor::SRC_ALPHA_SATURATE,
978        Bf::Constant => vk::BlendFactor::CONSTANT_COLOR,
979        Bf::OneMinusConstant => vk::BlendFactor::ONE_MINUS_CONSTANT_COLOR,
980        Bf::Src1 => vk::BlendFactor::SRC1_COLOR,
981        Bf::OneMinusSrc1 => vk::BlendFactor::ONE_MINUS_SRC1_COLOR,
982        Bf::Src1Alpha => vk::BlendFactor::SRC1_ALPHA,
983        Bf::OneMinusSrc1Alpha => vk::BlendFactor::ONE_MINUS_SRC1_ALPHA,
984    }
985}
986
987fn map_blend_op(operation: wgt::BlendOperation) -> vk::BlendOp {
988    use wgt::BlendOperation as Bo;
989    match operation {
990        Bo::Add => vk::BlendOp::ADD,
991        Bo::Subtract => vk::BlendOp::SUBTRACT,
992        Bo::ReverseSubtract => vk::BlendOp::REVERSE_SUBTRACT,
993        Bo::Min => vk::BlendOp::MIN,
994        Bo::Max => vk::BlendOp::MAX,
995    }
996}
997
998pub fn map_blend_component(
999    component: &wgt::BlendComponent,
1000) -> (vk::BlendOp, vk::BlendFactor, vk::BlendFactor) {
1001    let op = map_blend_op(component.operation);
1002    let src = map_blend_factor(component.src_factor);
1003    let dst = map_blend_factor(component.dst_factor);
1004    (op, src, dst)
1005}
1006
1007pub fn map_pipeline_statistics(
1008    types: wgt::PipelineStatisticsTypes,
1009) -> vk::QueryPipelineStatisticFlags {
1010    use wgt::PipelineStatisticsTypes as Pst;
1011    let mut flags = vk::QueryPipelineStatisticFlags::empty();
1012    if types.contains(Pst::VERTEX_SHADER_INVOCATIONS) {
1013        flags |= vk::QueryPipelineStatisticFlags::VERTEX_SHADER_INVOCATIONS;
1014    }
1015    if types.contains(Pst::CLIPPER_INVOCATIONS) {
1016        flags |= vk::QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS;
1017    }
1018    if types.contains(Pst::CLIPPER_PRIMITIVES_OUT) {
1019        flags |= vk::QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES;
1020    }
1021    if types.contains(Pst::FRAGMENT_SHADER_INVOCATIONS) {
1022        flags |= vk::QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS;
1023    }
1024    if types.contains(Pst::COMPUTE_SHADER_INVOCATIONS) {
1025        flags |= vk::QueryPipelineStatisticFlags::COMPUTE_SHADER_INVOCATIONS;
1026    }
1027    flags
1028}
1029
1030pub fn map_acceleration_structure_format(
1031    format: crate::AccelerationStructureFormat,
1032) -> vk::AccelerationStructureTypeKHR {
1033    match format {
1034        crate::AccelerationStructureFormat::TopLevel => vk::AccelerationStructureTypeKHR::TOP_LEVEL,
1035        crate::AccelerationStructureFormat::BottomLevel => {
1036            vk::AccelerationStructureTypeKHR::BOTTOM_LEVEL
1037        }
1038    }
1039}
1040
1041pub fn map_acceleration_structure_build_mode(
1042    format: crate::AccelerationStructureBuildMode,
1043) -> vk::BuildAccelerationStructureModeKHR {
1044    match format {
1045        crate::AccelerationStructureBuildMode::Build => {
1046            vk::BuildAccelerationStructureModeKHR::BUILD
1047        }
1048        crate::AccelerationStructureBuildMode::Update => {
1049            vk::BuildAccelerationStructureModeKHR::UPDATE
1050        }
1051    }
1052}
1053
1054pub fn map_acceleration_structure_flags(
1055    flags: crate::AccelerationStructureBuildFlags,
1056) -> vk::BuildAccelerationStructureFlagsKHR {
1057    let mut vk_flags = vk::BuildAccelerationStructureFlagsKHR::empty();
1058
1059    if flags.contains(crate::AccelerationStructureBuildFlags::PREFER_FAST_TRACE) {
1060        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE;
1061    }
1062
1063    if flags.contains(crate::AccelerationStructureBuildFlags::PREFER_FAST_BUILD) {
1064        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_BUILD;
1065    }
1066
1067    if flags.contains(crate::AccelerationStructureBuildFlags::ALLOW_UPDATE) {
1068        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::ALLOW_UPDATE;
1069    }
1070
1071    if flags.contains(crate::AccelerationStructureBuildFlags::LOW_MEMORY) {
1072        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::LOW_MEMORY;
1073    }
1074
1075    if flags.contains(crate::AccelerationStructureBuildFlags::ALLOW_COMPACTION) {
1076        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::ALLOW_COMPACTION
1077    }
1078
1079    if flags.contains(crate::AccelerationStructureBuildFlags::ALLOW_RAY_HIT_VERTEX_RETURN) {
1080        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::ALLOW_DATA_ACCESS
1081    }
1082
1083    vk_flags
1084}
1085
1086pub fn map_acceleration_structure_geometry_flags(
1087    flags: crate::AccelerationStructureGeometryFlags,
1088) -> vk::GeometryFlagsKHR {
1089    let mut vk_flags = vk::GeometryFlagsKHR::empty();
1090
1091    if flags.contains(crate::AccelerationStructureGeometryFlags::OPAQUE) {
1092        vk_flags |= vk::GeometryFlagsKHR::OPAQUE;
1093    }
1094
1095    if flags.contains(crate::AccelerationStructureGeometryFlags::NO_DUPLICATE_ANY_HIT_INVOCATION) {
1096        vk_flags |= vk::GeometryFlagsKHR::NO_DUPLICATE_ANY_HIT_INVOCATION;
1097    }
1098
1099    vk_flags
1100}
1101
1102pub fn map_acceleration_structure_usage_to_barrier(
1103    usage: crate::AccelerationStructureUses,
1104    features: wgt::Features,
1105    queue_flags: vk::QueueFlags,
1106) -> (vk::PipelineStageFlags, vk::AccessFlags) {
1107    let mut stages = vk::PipelineStageFlags::empty();
1108    let mut access = vk::AccessFlags::empty();
1109
1110    if usage.contains(crate::AccelerationStructureUses::BUILD_INPUT) {
1111        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1112        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1113    }
1114    if usage.contains(crate::AccelerationStructureUses::QUERY_INPUT) {
1115        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1116        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1117    }
1118    if usage.contains(crate::AccelerationStructureUses::BUILD_OUTPUT) {
1119        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1120        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR;
1121    }
1122    if usage.contains(crate::AccelerationStructureUses::SHADER_INPUT)
1123        && features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY)
1124    {
1125        stages |= shader_stages(queue_flags, features);
1126        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1127    }
1128    if usage.contains(crate::AccelerationStructureUses::SHADER_INPUT)
1129        && features.contains(wgt::Features::EXPERIMENTAL_RAY_TRACING_PIPELINES)
1130    {
1131        stages |= vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR;
1132        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1133    }
1134    if usage.contains(crate::AccelerationStructureUses::COPY_SRC) {
1135        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1136        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1137    }
1138    if usage.contains(crate::AccelerationStructureUses::COPY_DST) {
1139        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1140        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR;
1141    }
1142
1143    (stages, access)
1144}
1145
1146pub fn map_component_swizzle(swizzle: wgt::ComponentSwizzle) -> vk::ComponentSwizzle {
1147    match swizzle {
1148        wgt::ComponentSwizzle::Zero => vk::ComponentSwizzle::ZERO,
1149        wgt::ComponentSwizzle::One => vk::ComponentSwizzle::ONE,
1150        wgt::ComponentSwizzle::R => vk::ComponentSwizzle::R,
1151        wgt::ComponentSwizzle::G => vk::ComponentSwizzle::G,
1152        wgt::ComponentSwizzle::B => vk::ComponentSwizzle::B,
1153        wgt::ComponentSwizzle::A => vk::ComponentSwizzle::A,
1154    }
1155}
1156
1157pub fn map_texture_component_swizzle(
1158    swizzle: wgt::TextureComponentSwizzle,
1159) -> vk::ComponentMapping {
1160    vk::ComponentMapping {
1161        r: map_component_swizzle(swizzle.r),
1162        g: map_component_swizzle(swizzle.g),
1163        b: map_component_swizzle(swizzle.b),
1164        a: map_component_swizzle(swizzle.a),
1165    }
1166}
1167
1168#[cfg(test)]
1169mod tests {
1170    use super::*;
1171
1172    /// `map_vk_color_space` and `map_surface_color_space` must stay mutually
1173    /// inverse so that a color space reported in the surface capabilities is
1174    /// exactly what the swapchain is created with.
1175    #[test]
1176    fn color_space_round_trip() {
1177        for vk_color_space in [
1178            vk::ColorSpaceKHR::SRGB_NONLINEAR,
1179            vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT,
1180            vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT,
1181            vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT,
1182            vk::ColorSpaceKHR::HDR10_ST2084_EXT,
1183            vk::ColorSpaceKHR::HDR10_HLG_EXT,
1184        ] {
1185            let mapped = map_vk_color_space(vk_color_space).unwrap();
1186            assert_eq!(map_surface_color_space(mapped), vk_color_space);
1187        }
1188    }
1189
1190    #[test]
1191    fn unknown_color_spaces_are_dropped() {
1192        for vk_color_space in [
1193            vk::ColorSpaceKHR::BT2020_LINEAR_EXT,
1194            vk::ColorSpaceKHR::DOLBYVISION_EXT,
1195            vk::ColorSpaceKHR::PASS_THROUGH_EXT,
1196            vk::ColorSpaceKHR::ADOBERGB_NONLINEAR_EXT,
1197        ] {
1198            assert!(map_vk_color_space(vk_color_space).is_none());
1199            assert!(map_vk_surface_formats(vk::SurfaceFormatKHR {
1200                format: vk::Format::B8G8R8A8_UNORM,
1201                color_space: vk_color_space,
1202            })
1203            .is_none());
1204        }
1205    }
1206
1207    #[test]
1208    fn hdr10_surface_format_maps() {
1209        let (format, color_space) = map_vk_surface_formats(vk::SurfaceFormatKHR {
1210            format: vk::Format::A2B10G10R10_UNORM_PACK32,
1211            color_space: vk::ColorSpaceKHR::HDR10_ST2084_EXT,
1212        })
1213        .unwrap();
1214        assert_eq!(format, wgt::TextureFormat::Rgb10a2Unorm);
1215        assert_eq!(color_space, wgt::SurfaceColorSpace::Bt2100Pq);
1216    }
1217
1218    #[test]
1219    fn buffer_shader_stages_follow_queue_flags() {
1220        let usage = wgt::BufferUses::UNIFORM;
1221
1222        let (stages, access) =
1223            map_buffer_usage_to_barrier(usage, vk::QueueFlags::GRAPHICS, wgt::Features::empty());
1224        assert_eq!(
1225            stages,
1226            vk::PipelineStageFlags::VERTEX_SHADER | vk::PipelineStageFlags::FRAGMENT_SHADER
1227        );
1228        assert_eq!(access, vk::AccessFlags::UNIFORM_READ);
1229
1230        let (stages, access) =
1231            map_buffer_usage_to_barrier(usage, vk::QueueFlags::COMPUTE, wgt::Features::empty());
1232        assert_eq!(stages, vk::PipelineStageFlags::COMPUTE_SHADER);
1233        assert_eq!(access, vk::AccessFlags::UNIFORM_READ);
1234
1235        let (stages, access) = map_buffer_usage_to_barrier(
1236            usage,
1237            vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE,
1238            wgt::Features::empty(),
1239        );
1240        assert_eq!(
1241            stages,
1242            vk::PipelineStageFlags::VERTEX_SHADER
1243                | vk::PipelineStageFlags::FRAGMENT_SHADER
1244                | vk::PipelineStageFlags::COMPUTE_SHADER
1245        );
1246        assert_eq!(access, vk::AccessFlags::UNIFORM_READ);
1247    }
1248
1249    #[test]
1250    fn non_shader_buffer_usage_is_unchanged() {
1251        let (stages, access) = map_buffer_usage_to_barrier(
1252            wgt::BufferUses::COPY_SRC | wgt::BufferUses::VERTEX,
1253            vk::QueueFlags::empty(),
1254            wgt::Features::empty(),
1255        );
1256
1257        assert_eq!(
1258            stages,
1259            vk::PipelineStageFlags::TRANSFER | vk::PipelineStageFlags::VERTEX_INPUT
1260        );
1261        assert_eq!(
1262            access,
1263            vk::AccessFlags::TRANSFER_READ | vk::AccessFlags::VERTEX_ATTRIBUTE_READ
1264        );
1265    }
1266
1267    #[test]
1268    fn texture_shader_stages_follow_queue_flags() {
1269        let usage = wgt::TextureUses::RESOURCE;
1270
1271        let (stages, access) = map_texture_usage_to_barrier(
1272            usage,
1273            vk::QueueFlags::GRAPHICS,
1274            false,
1275            wgt::Features::empty(),
1276        );
1277        assert_eq!(
1278            stages,
1279            vk::PipelineStageFlags::VERTEX_SHADER | vk::PipelineStageFlags::FRAGMENT_SHADER
1280        );
1281        assert_eq!(access, vk::AccessFlags::SHADER_READ);
1282
1283        let (stages, access) = map_texture_usage_to_barrier(
1284            usage,
1285            vk::QueueFlags::COMPUTE,
1286            false,
1287            wgt::Features::empty(),
1288        );
1289        assert_eq!(stages, vk::PipelineStageFlags::COMPUTE_SHADER);
1290        assert_eq!(access, vk::AccessFlags::SHADER_READ);
1291
1292        let (stages, access) = map_texture_usage_to_barrier(
1293            usage,
1294            vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE,
1295            false,
1296            wgt::Features::empty(),
1297        );
1298        assert_eq!(
1299            stages,
1300            vk::PipelineStageFlags::VERTEX_SHADER
1301                | vk::PipelineStageFlags::FRAGMENT_SHADER
1302                | vk::PipelineStageFlags::COMPUTE_SHADER
1303        );
1304        assert_eq!(access, vk::AccessFlags::SHADER_READ);
1305    }
1306
1307    #[test]
1308    fn non_shader_texture_usage_is_unchanged() {
1309        let (stages, access) = map_texture_usage_to_barrier(
1310            wgt::TextureUses::COPY_SRC | wgt::TextureUses::COLOR_TARGET,
1311            vk::QueueFlags::empty(),
1312            false,
1313            wgt::Features::empty(),
1314        );
1315
1316        assert_eq!(
1317            stages,
1318            vk::PipelineStageFlags::TRANSFER | vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
1319        );
1320        assert_eq!(
1321            access,
1322            vk::AccessFlags::TRANSFER_READ
1323                | vk::AccessFlags::COLOR_ATTACHMENT_READ
1324                | vk::AccessFlags::COLOR_ATTACHMENT_WRITE
1325        );
1326    }
1327
1328    #[test]
1329    fn acceleration_structure_shader_stages_follow_queue_flags() {
1330        let usage = crate::AccelerationStructureUses::SHADER_INPUT;
1331        let features = wgt::Features::EXPERIMENTAL_RAY_QUERY;
1332
1333        let (stages, access) =
1334            map_acceleration_structure_usage_to_barrier(usage, features, vk::QueueFlags::GRAPHICS);
1335        assert_eq!(
1336            stages,
1337            vk::PipelineStageFlags::VERTEX_SHADER | vk::PipelineStageFlags::FRAGMENT_SHADER
1338        );
1339        assert_eq!(access, vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR);
1340
1341        let (stages, access) =
1342            map_acceleration_structure_usage_to_barrier(usage, features, vk::QueueFlags::COMPUTE);
1343        assert_eq!(stages, vk::PipelineStageFlags::COMPUTE_SHADER);
1344        assert_eq!(access, vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR);
1345
1346        let (stages, access) = map_acceleration_structure_usage_to_barrier(
1347            usage,
1348            features | wgt::Features::EXPERIMENTAL_RAY_TRACING_PIPELINES,
1349            vk::QueueFlags::COMPUTE,
1350        );
1351        assert_eq!(
1352            stages,
1353            vk::PipelineStageFlags::COMPUTE_SHADER | vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR
1354        );
1355        assert_eq!(access, vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR);
1356
1357        let (stages, access) = map_acceleration_structure_usage_to_barrier(
1358            usage,
1359            features,
1360            vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE,
1361        );
1362        assert_eq!(
1363            stages,
1364            vk::PipelineStageFlags::VERTEX_SHADER
1365                | vk::PipelineStageFlags::FRAGMENT_SHADER
1366                | vk::PipelineStageFlags::COMPUTE_SHADER
1367        );
1368        assert_eq!(access, vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR);
1369    }
1370
1371    #[test]
1372    fn non_shader_acceleration_structure_usage_is_unchanged() {
1373        let (stages, access) = map_acceleration_structure_usage_to_barrier(
1374            crate::AccelerationStructureUses::BUILD_INPUT
1375                | crate::AccelerationStructureUses::BUILD_OUTPUT,
1376            wgt::Features::empty(),
1377            vk::QueueFlags::empty(),
1378        );
1379
1380        assert_eq!(
1381            stages,
1382            vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR
1383        );
1384        assert_eq!(
1385            access,
1386            vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR
1387                | vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR
1388        );
1389    }
1390}