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    // Note: depth textures are always sampled with RODS layout
242    let is_color = !format.is_depth_stencil_format();
243    match usage {
244        wgt::TextureUses::UNINITIALIZED => vk::ImageLayout::UNDEFINED,
245        wgt::TextureUses::COPY_SRC => vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
246        wgt::TextureUses::COPY_DST => vk::ImageLayout::TRANSFER_DST_OPTIMAL,
247        wgt::TextureUses::RESOURCE if is_color => vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
248        wgt::TextureUses::COLOR_TARGET => vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
249        wgt::TextureUses::DEPTH_STENCIL_WRITE => vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
250        _ => {
251            if usage == wgt::TextureUses::PRESENT {
252                vk::ImageLayout::PRESENT_SRC_KHR
253            } else if is_color {
254                vk::ImageLayout::GENERAL
255            } else {
256                vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL
257            }
258        }
259    }
260}
261
262pub fn map_texture_usage(usage: wgt::TextureUses) -> vk::ImageUsageFlags {
263    let mut flags = vk::ImageUsageFlags::empty();
264    if usage.contains(wgt::TextureUses::COPY_SRC) {
265        flags |= vk::ImageUsageFlags::TRANSFER_SRC;
266    }
267    if usage.contains(wgt::TextureUses::COPY_DST) {
268        flags |= vk::ImageUsageFlags::TRANSFER_DST;
269    }
270    if usage.contains(wgt::TextureUses::RESOURCE) {
271        flags |= vk::ImageUsageFlags::SAMPLED;
272    }
273    if usage.contains(wgt::TextureUses::COLOR_TARGET) {
274        flags |= vk::ImageUsageFlags::COLOR_ATTACHMENT;
275    }
276    if usage
277        .intersects(wgt::TextureUses::DEPTH_STENCIL_READ | wgt::TextureUses::DEPTH_STENCIL_WRITE)
278    {
279        flags |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT;
280    }
281    if usage.intersects(
282        wgt::TextureUses::STORAGE_READ_ONLY
283            | wgt::TextureUses::STORAGE_WRITE_ONLY
284            | wgt::TextureUses::STORAGE_READ_WRITE
285            | wgt::TextureUses::STORAGE_ATOMIC,
286    ) {
287        flags |= vk::ImageUsageFlags::STORAGE;
288    }
289    if usage.contains(wgt::TextureUses::TRANSIENT) {
290        flags |= vk::ImageUsageFlags::TRANSIENT_ATTACHMENT;
291    }
292    flags
293}
294
295pub fn map_texture_usage_to_barrier(
296    usage: wgt::TextureUses,
297    queue_flags: vk::QueueFlags,
298) -> (vk::PipelineStageFlags, vk::AccessFlags) {
299    let mut stages = vk::PipelineStageFlags::empty();
300    let mut access = vk::AccessFlags::empty();
301    let shader_stages = shader_stages(queue_flags);
302
303    if usage.contains(wgt::TextureUses::COPY_SRC) {
304        stages |= vk::PipelineStageFlags::TRANSFER;
305        access |= vk::AccessFlags::TRANSFER_READ;
306    }
307    if usage.contains(wgt::TextureUses::COPY_DST) {
308        stages |= vk::PipelineStageFlags::TRANSFER;
309        access |= vk::AccessFlags::TRANSFER_WRITE;
310    }
311    if usage.contains(wgt::TextureUses::RESOURCE) {
312        stages |= shader_stages;
313        access |= vk::AccessFlags::SHADER_READ;
314    }
315    if usage.contains(wgt::TextureUses::COLOR_TARGET) {
316        stages |= vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT;
317        access |= vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE;
318    }
319    if usage.intersects(wgt::TextureUses::DEPTH_STENCIL_READ) {
320        stages |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
321            | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
322        access |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ;
323    }
324    if usage.intersects(wgt::TextureUses::DEPTH_STENCIL_WRITE) {
325        stages |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
326            | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
327        access |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ
328            | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE;
329    }
330    if usage.intersects(
331        wgt::TextureUses::STORAGE_READ_ONLY
332            | wgt::TextureUses::STORAGE_READ_WRITE
333            | wgt::TextureUses::STORAGE_ATOMIC,
334    ) {
335        stages |= shader_stages;
336        access |= vk::AccessFlags::SHADER_READ;
337    }
338    if usage.intersects(
339        wgt::TextureUses::STORAGE_WRITE_ONLY
340            | wgt::TextureUses::STORAGE_READ_WRITE
341            | wgt::TextureUses::STORAGE_ATOMIC,
342    ) {
343        stages |= shader_stages;
344        access |= vk::AccessFlags::SHADER_WRITE;
345    }
346
347    if usage == wgt::TextureUses::UNINITIALIZED || usage == wgt::TextureUses::PRESENT {
348        (
349            vk::PipelineStageFlags::TOP_OF_PIPE,
350            vk::AccessFlags::empty(),
351        )
352    } else {
353        (stages, access)
354    }
355}
356
357pub fn map_vk_image_usage(usage: vk::ImageUsageFlags) -> wgt::TextureUses {
358    let mut bits = wgt::TextureUses::empty();
359    if usage.contains(vk::ImageUsageFlags::TRANSFER_SRC) {
360        bits |= wgt::TextureUses::COPY_SRC;
361    }
362    if usage.contains(vk::ImageUsageFlags::TRANSFER_DST) {
363        bits |= wgt::TextureUses::COPY_DST;
364    }
365    if usage.contains(vk::ImageUsageFlags::SAMPLED) {
366        bits |= wgt::TextureUses::RESOURCE;
367    }
368    if usage.contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) {
369        bits |= wgt::TextureUses::COLOR_TARGET;
370    }
371    if usage.contains(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT) {
372        bits |= wgt::TextureUses::DEPTH_STENCIL_READ | wgt::TextureUses::DEPTH_STENCIL_WRITE;
373    }
374    if usage.contains(vk::ImageUsageFlags::STORAGE) {
375        bits |= wgt::TextureUses::STORAGE_READ_ONLY
376            | wgt::TextureUses::STORAGE_WRITE_ONLY
377            | wgt::TextureUses::STORAGE_READ_WRITE
378            | wgt::TextureUses::STORAGE_ATOMIC;
379    }
380    if usage.contains(vk::ImageUsageFlags::TRANSIENT_ATTACHMENT) {
381        bits |= wgt::TextureUses::TRANSIENT;
382    }
383    bits
384}
385
386pub fn map_texture_dimension(dim: wgt::TextureDimension) -> vk::ImageType {
387    match dim {
388        wgt::TextureDimension::D1 => vk::ImageType::TYPE_1D,
389        wgt::TextureDimension::D2 => vk::ImageType::TYPE_2D,
390        wgt::TextureDimension::D3 => vk::ImageType::TYPE_3D,
391    }
392}
393
394pub fn map_index_format(index_format: wgt::IndexFormat) -> vk::IndexType {
395    match index_format {
396        wgt::IndexFormat::Uint16 => vk::IndexType::UINT16,
397        wgt::IndexFormat::Uint32 => vk::IndexType::UINT32,
398    }
399}
400
401pub fn map_vertex_format(vertex_format: wgt::VertexFormat) -> vk::Format {
402    use wgt::VertexFormat as Vf;
403    match vertex_format {
404        Vf::Uint8 => vk::Format::R8_UINT,
405        Vf::Uint8x2 => vk::Format::R8G8_UINT,
406        Vf::Uint8x4 => vk::Format::R8G8B8A8_UINT,
407        Vf::Sint8 => vk::Format::R8_SINT,
408        Vf::Sint8x2 => vk::Format::R8G8_SINT,
409        Vf::Sint8x4 => vk::Format::R8G8B8A8_SINT,
410        Vf::Unorm8 => vk::Format::R8_UNORM,
411        Vf::Unorm8x2 => vk::Format::R8G8_UNORM,
412        Vf::Unorm8x4 => vk::Format::R8G8B8A8_UNORM,
413        Vf::Snorm8 => vk::Format::R8_SNORM,
414        Vf::Snorm8x2 => vk::Format::R8G8_SNORM,
415        Vf::Snorm8x4 => vk::Format::R8G8B8A8_SNORM,
416        Vf::Uint16 => vk::Format::R16_UINT,
417        Vf::Uint16x2 => vk::Format::R16G16_UINT,
418        Vf::Uint16x4 => vk::Format::R16G16B16A16_UINT,
419        Vf::Sint16 => vk::Format::R16_SINT,
420        Vf::Sint16x2 => vk::Format::R16G16_SINT,
421        Vf::Sint16x4 => vk::Format::R16G16B16A16_SINT,
422        Vf::Unorm16 => vk::Format::R16_UNORM,
423        Vf::Unorm16x2 => vk::Format::R16G16_UNORM,
424        Vf::Unorm16x4 => vk::Format::R16G16B16A16_UNORM,
425        Vf::Snorm16 => vk::Format::R16_SNORM,
426        Vf::Snorm16x2 => vk::Format::R16G16_SNORM,
427        Vf::Snorm16x4 => vk::Format::R16G16B16A16_SNORM,
428        Vf::Float16 => vk::Format::R16_SFLOAT,
429        Vf::Float16x2 => vk::Format::R16G16_SFLOAT,
430        Vf::Float16x4 => vk::Format::R16G16B16A16_SFLOAT,
431        Vf::Float32 => vk::Format::R32_SFLOAT,
432        Vf::Float32x2 => vk::Format::R32G32_SFLOAT,
433        Vf::Float32x3 => vk::Format::R32G32B32_SFLOAT,
434        Vf::Float32x4 => vk::Format::R32G32B32A32_SFLOAT,
435        Vf::Uint32 => vk::Format::R32_UINT,
436        Vf::Uint32x2 => vk::Format::R32G32_UINT,
437        Vf::Uint32x3 => vk::Format::R32G32B32_UINT,
438        Vf::Uint32x4 => vk::Format::R32G32B32A32_UINT,
439        Vf::Sint32 => vk::Format::R32_SINT,
440        Vf::Sint32x2 => vk::Format::R32G32_SINT,
441        Vf::Sint32x3 => vk::Format::R32G32B32_SINT,
442        Vf::Sint32x4 => vk::Format::R32G32B32A32_SINT,
443        Vf::Float64 => vk::Format::R64_SFLOAT,
444        Vf::Float64x2 => vk::Format::R64G64_SFLOAT,
445        Vf::Float64x3 => vk::Format::R64G64B64_SFLOAT,
446        Vf::Float64x4 => vk::Format::R64G64B64A64_SFLOAT,
447        Vf::Unorm10_10_10_2 => vk::Format::A2B10G10R10_UNORM_PACK32,
448        Vf::Unorm8x4Bgra => vk::Format::B8G8R8A8_UNORM,
449    }
450}
451
452pub fn map_aspects(aspects: crate::FormatAspects) -> vk::ImageAspectFlags {
453    let mut flags = vk::ImageAspectFlags::empty();
454    if aspects.contains(crate::FormatAspects::COLOR) {
455        flags |= vk::ImageAspectFlags::COLOR;
456    }
457    if aspects.contains(crate::FormatAspects::DEPTH) {
458        flags |= vk::ImageAspectFlags::DEPTH;
459    }
460    if aspects.contains(crate::FormatAspects::STENCIL) {
461        flags |= vk::ImageAspectFlags::STENCIL;
462    }
463    if aspects.contains(crate::FormatAspects::PLANE_0) {
464        flags |= vk::ImageAspectFlags::PLANE_0;
465    }
466    if aspects.contains(crate::FormatAspects::PLANE_1) {
467        flags |= vk::ImageAspectFlags::PLANE_1;
468    }
469    if aspects.contains(crate::FormatAspects::PLANE_2) {
470        flags |= vk::ImageAspectFlags::PLANE_2;
471    }
472    flags
473}
474
475pub fn map_attachment_ops(
476    op: crate::AttachmentOps,
477) -> (vk::AttachmentLoadOp, vk::AttachmentStoreOp) {
478    let load_op = if op.contains(crate::AttachmentOps::LOAD) {
479        vk::AttachmentLoadOp::LOAD
480    } else if op.contains(crate::AttachmentOps::LOAD_DONT_CARE) {
481        vk::AttachmentLoadOp::DONT_CARE
482    } else if op.contains(crate::AttachmentOps::LOAD_CLEAR) {
483        vk::AttachmentLoadOp::CLEAR
484    } else {
485        unreachable!()
486    };
487    let store_op = if op.contains(crate::AttachmentOps::STORE) {
488        vk::AttachmentStoreOp::STORE
489    } else if op.contains(crate::AttachmentOps::STORE_DISCARD) {
490        vk::AttachmentStoreOp::DONT_CARE
491    } else {
492        unreachable!()
493    };
494    (load_op, store_op)
495}
496
497pub fn map_present_mode(mode: wgt::PresentMode) -> vk::PresentModeKHR {
498    match mode {
499        wgt::PresentMode::Immediate => vk::PresentModeKHR::IMMEDIATE,
500        wgt::PresentMode::Mailbox => vk::PresentModeKHR::MAILBOX,
501        wgt::PresentMode::Fifo => vk::PresentModeKHR::FIFO,
502        wgt::PresentMode::FifoRelaxed => vk::PresentModeKHR::FIFO_RELAXED,
503        wgt::PresentMode::AutoNoVsync | wgt::PresentMode::AutoVsync => {
504            unreachable!("Cannot create swapchain with Auto PresentationMode")
505        }
506    }
507}
508
509pub fn map_vk_present_mode(mode: vk::PresentModeKHR) -> Option<wgt::PresentMode> {
510    // Not exposed in Ash yet.
511    const FIFO_LATEST_READY: vk::PresentModeKHR = vk::PresentModeKHR::from_raw(1_000_361_000);
512
513    // See https://registry.khronos.org/vulkan/specs/latest/man/html/VkPresentModeKHR.html
514    match mode {
515        vk::PresentModeKHR::IMMEDIATE => Some(wgt::PresentMode::Immediate),
516        vk::PresentModeKHR::MAILBOX => Some(wgt::PresentMode::Mailbox),
517        vk::PresentModeKHR::FIFO => Some(wgt::PresentMode::Fifo),
518        vk::PresentModeKHR::FIFO_RELAXED => Some(wgt::PresentMode::FifoRelaxed),
519
520        // Modes that aren't exposed yet.
521        vk::PresentModeKHR::SHARED_DEMAND_REFRESH => None,
522        vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH => None,
523        FIFO_LATEST_READY => None,
524
525        _ => {
526            log::debug!("Unrecognized present mode {mode:?}");
527            None
528        }
529    }
530}
531
532pub fn map_composite_alpha_mode(mode: wgt::CompositeAlphaMode) -> vk::CompositeAlphaFlagsKHR {
533    match mode {
534        wgt::CompositeAlphaMode::Opaque => vk::CompositeAlphaFlagsKHR::OPAQUE,
535        wgt::CompositeAlphaMode::PreMultiplied => vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED,
536        wgt::CompositeAlphaMode::PostMultiplied => vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED,
537        wgt::CompositeAlphaMode::Inherit => vk::CompositeAlphaFlagsKHR::INHERIT,
538        wgt::CompositeAlphaMode::Auto => unreachable!(),
539    }
540}
541
542pub fn map_vk_composite_alpha(flags: vk::CompositeAlphaFlagsKHR) -> Vec<wgt::CompositeAlphaMode> {
543    let mut modes = Vec::new();
544    if flags.contains(vk::CompositeAlphaFlagsKHR::OPAQUE) {
545        modes.push(wgt::CompositeAlphaMode::Opaque);
546    }
547    if flags.contains(vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED) {
548        modes.push(wgt::CompositeAlphaMode::PreMultiplied);
549    }
550    if flags.contains(vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED) {
551        modes.push(wgt::CompositeAlphaMode::PostMultiplied);
552    }
553    if flags.contains(vk::CompositeAlphaFlagsKHR::INHERIT) {
554        modes.push(wgt::CompositeAlphaMode::Inherit);
555    }
556    modes
557}
558
559pub fn map_buffer_usage(usage: wgt::BufferUses) -> vk::BufferUsageFlags {
560    let mut flags = vk::BufferUsageFlags::empty();
561    if usage.contains(wgt::BufferUses::COPY_SRC) {
562        flags |= vk::BufferUsageFlags::TRANSFER_SRC;
563    }
564    if usage.contains(wgt::BufferUses::COPY_DST) {
565        flags |= vk::BufferUsageFlags::TRANSFER_DST;
566    }
567    if usage.contains(wgt::BufferUses::UNIFORM) {
568        flags |= vk::BufferUsageFlags::UNIFORM_BUFFER;
569    }
570    if usage.intersects(wgt::BufferUses::STORAGE_READ_ONLY | wgt::BufferUses::STORAGE_READ_WRITE) {
571        flags |= vk::BufferUsageFlags::STORAGE_BUFFER;
572    }
573    if usage.contains(wgt::BufferUses::INDEX) {
574        flags |= vk::BufferUsageFlags::INDEX_BUFFER;
575    }
576    if usage.contains(wgt::BufferUses::VERTEX) {
577        flags |= vk::BufferUsageFlags::VERTEX_BUFFER;
578    }
579    if usage.contains(wgt::BufferUses::INDIRECT) {
580        flags |= vk::BufferUsageFlags::INDIRECT_BUFFER;
581    }
582    if usage.contains(wgt::BufferUses::ACCELERATION_STRUCTURE_SCRATCH) {
583        flags |= vk::BufferUsageFlags::STORAGE_BUFFER | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS;
584    }
585    if usage.intersects(
586        wgt::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT
587            | wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT,
588    ) {
589        flags |= vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR
590            | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS;
591    }
592    if usage.intersects(wgt::BufferUses::ACCELERATION_STRUCTURE_QUERY) {
593        flags |= vk::BufferUsageFlags::TRANSFER_DST;
594    }
595    if usage.intersects(wgt::BufferUses::RAY_TRACING_PIPELINE_SHADER_DATA) {
596        flags |= vk::BufferUsageFlags::SHADER_BINDING_TABLE_KHR
597            | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS;
598    }
599    flags
600}
601
602pub fn map_buffer_usage_to_barrier(
603    usage: wgt::BufferUses,
604    queue_flags: vk::QueueFlags,
605) -> (vk::PipelineStageFlags, vk::AccessFlags) {
606    let mut stages = vk::PipelineStageFlags::empty();
607    let mut access = vk::AccessFlags::empty();
608    let shader_stages = shader_stages(queue_flags);
609
610    if usage.contains(wgt::BufferUses::MAP_READ) {
611        stages |= vk::PipelineStageFlags::HOST;
612        access |= vk::AccessFlags::HOST_READ;
613    }
614    if usage.contains(wgt::BufferUses::MAP_WRITE) {
615        stages |= vk::PipelineStageFlags::HOST;
616        access |= vk::AccessFlags::HOST_WRITE;
617    }
618    if usage.contains(wgt::BufferUses::COPY_SRC) {
619        stages |= vk::PipelineStageFlags::TRANSFER;
620        access |= vk::AccessFlags::TRANSFER_READ;
621    }
622    if usage.contains(wgt::BufferUses::COPY_DST) {
623        stages |= vk::PipelineStageFlags::TRANSFER;
624        access |= vk::AccessFlags::TRANSFER_WRITE;
625    }
626    if usage.contains(wgt::BufferUses::UNIFORM) {
627        stages |= shader_stages;
628        access |= vk::AccessFlags::UNIFORM_READ;
629    }
630    if usage.intersects(wgt::BufferUses::STORAGE_READ_ONLY) {
631        stages |= shader_stages;
632        access |= vk::AccessFlags::SHADER_READ;
633    }
634    if usage.intersects(wgt::BufferUses::STORAGE_READ_WRITE) {
635        stages |= shader_stages;
636        access |= vk::AccessFlags::SHADER_READ | vk::AccessFlags::SHADER_WRITE;
637    }
638    if usage.contains(wgt::BufferUses::INDEX) {
639        stages |= vk::PipelineStageFlags::VERTEX_INPUT;
640        access |= vk::AccessFlags::INDEX_READ;
641    }
642    if usage.contains(wgt::BufferUses::VERTEX) {
643        stages |= vk::PipelineStageFlags::VERTEX_INPUT;
644        access |= vk::AccessFlags::VERTEX_ATTRIBUTE_READ;
645    }
646    if usage.contains(wgt::BufferUses::INDIRECT) {
647        stages |= vk::PipelineStageFlags::DRAW_INDIRECT;
648        access |= vk::AccessFlags::INDIRECT_COMMAND_READ;
649    }
650    if usage.intersects(
651        wgt::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT
652            | wgt::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT
653            | wgt::BufferUses::ACCELERATION_STRUCTURE_SCRATCH,
654    ) {
655        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
656        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR
657            | vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR
658            | vk::AccessFlags::SHADER_READ;
659    }
660    if usage.contains(wgt::BufferUses::ACCELERATION_STRUCTURE_QUERY) {
661        stages |= vk::PipelineStageFlags::TRANSFER;
662        access |= vk::AccessFlags::TRANSFER_WRITE;
663    }
664
665    (stages, access)
666}
667
668fn shader_stages(queue_flags: vk::QueueFlags) -> vk::PipelineStageFlags {
669    let mut stages = vk::PipelineStageFlags::empty();
670
671    if queue_flags.contains(vk::QueueFlags::GRAPHICS) {
672        stages |= vk::PipelineStageFlags::VERTEX_SHADER | vk::PipelineStageFlags::FRAGMENT_SHADER;
673    }
674    if queue_flags.contains(vk::QueueFlags::COMPUTE) {
675        stages |= vk::PipelineStageFlags::COMPUTE_SHADER;
676    }
677
678    stages
679}
680
681pub fn map_view_dimension(dim: wgt::TextureViewDimension) -> vk::ImageViewType {
682    match dim {
683        wgt::TextureViewDimension::D1 => vk::ImageViewType::TYPE_1D,
684        wgt::TextureViewDimension::D2 => vk::ImageViewType::TYPE_2D,
685        wgt::TextureViewDimension::D2Array => vk::ImageViewType::TYPE_2D_ARRAY,
686        wgt::TextureViewDimension::Cube => vk::ImageViewType::CUBE,
687        wgt::TextureViewDimension::CubeArray => vk::ImageViewType::CUBE_ARRAY,
688        wgt::TextureViewDimension::D3 => vk::ImageViewType::TYPE_3D,
689    }
690}
691
692pub fn map_copy_extent(extent: &crate::CopyExtent) -> vk::Extent3D {
693    vk::Extent3D {
694        width: extent.width,
695        height: extent.height,
696        depth: extent.depth,
697    }
698}
699
700pub fn map_subresource_range(
701    range: &wgt::ImageSubresourceRange,
702    format: wgt::TextureFormat,
703) -> vk::ImageSubresourceRange {
704    vk::ImageSubresourceRange {
705        aspect_mask: map_aspects(crate::FormatAspects::new(format, range.aspect)),
706        base_mip_level: range.base_mip_level,
707        level_count: range.mip_level_count.unwrap_or(vk::REMAINING_MIP_LEVELS),
708        base_array_layer: range.base_array_layer,
709        layer_count: range
710            .array_layer_count
711            .unwrap_or(vk::REMAINING_ARRAY_LAYERS),
712    }
713}
714
715// Special subresource range mapping for dealing with barriers
716// so that we account for the "hidden" depth aspect in emulated Stencil8.
717pub(super) fn map_subresource_range_combined_aspect(
718    range: &wgt::ImageSubresourceRange,
719    format: wgt::TextureFormat,
720    private_caps: &super::PrivateCapabilities,
721) -> vk::ImageSubresourceRange {
722    let mut range = map_subresource_range(range, format);
723    if !private_caps.texture_s8 && format == wgt::TextureFormat::Stencil8 {
724        range.aspect_mask |= vk::ImageAspectFlags::DEPTH;
725    }
726    range
727}
728
729pub fn map_subresource_layers(
730    base: &crate::TextureCopyBase,
731) -> (vk::ImageSubresourceLayers, vk::Offset3D) {
732    let offset = vk::Offset3D {
733        x: base.origin.x as i32,
734        y: base.origin.y as i32,
735        z: base.origin.z as i32,
736    };
737    let subresource = vk::ImageSubresourceLayers {
738        aspect_mask: map_aspects(base.aspect),
739        mip_level: base.mip_level,
740        base_array_layer: base.array_layer,
741        layer_count: 1,
742    };
743    (subresource, offset)
744}
745
746pub fn map_filter_mode(mode: wgt::FilterMode) -> vk::Filter {
747    match mode {
748        wgt::FilterMode::Nearest => vk::Filter::NEAREST,
749        wgt::FilterMode::Linear => vk::Filter::LINEAR,
750    }
751}
752
753pub fn map_mip_filter_mode(mode: wgt::MipmapFilterMode) -> vk::SamplerMipmapMode {
754    match mode {
755        wgt::MipmapFilterMode::Nearest => vk::SamplerMipmapMode::NEAREST,
756        wgt::MipmapFilterMode::Linear => vk::SamplerMipmapMode::LINEAR,
757    }
758}
759
760pub fn map_address_mode(mode: wgt::AddressMode) -> vk::SamplerAddressMode {
761    match mode {
762        wgt::AddressMode::ClampToEdge => vk::SamplerAddressMode::CLAMP_TO_EDGE,
763        wgt::AddressMode::Repeat => vk::SamplerAddressMode::REPEAT,
764        wgt::AddressMode::MirrorRepeat => vk::SamplerAddressMode::MIRRORED_REPEAT,
765        wgt::AddressMode::ClampToBorder => vk::SamplerAddressMode::CLAMP_TO_BORDER,
766        // wgt::AddressMode::MirrorClamp => vk::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE,
767    }
768}
769
770pub fn map_border_color(border_color: wgt::SamplerBorderColor) -> vk::BorderColor {
771    match border_color {
772        wgt::SamplerBorderColor::TransparentBlack | wgt::SamplerBorderColor::Zero => {
773            vk::BorderColor::FLOAT_TRANSPARENT_BLACK
774        }
775        wgt::SamplerBorderColor::OpaqueBlack => vk::BorderColor::FLOAT_OPAQUE_BLACK,
776        wgt::SamplerBorderColor::OpaqueWhite => vk::BorderColor::FLOAT_OPAQUE_WHITE,
777    }
778}
779
780pub fn map_comparison(fun: wgt::CompareFunction) -> vk::CompareOp {
781    use wgt::CompareFunction as Cf;
782    match fun {
783        Cf::Never => vk::CompareOp::NEVER,
784        Cf::Less => vk::CompareOp::LESS,
785        Cf::LessEqual => vk::CompareOp::LESS_OR_EQUAL,
786        Cf::Equal => vk::CompareOp::EQUAL,
787        Cf::GreaterEqual => vk::CompareOp::GREATER_OR_EQUAL,
788        Cf::Greater => vk::CompareOp::GREATER,
789        Cf::NotEqual => vk::CompareOp::NOT_EQUAL,
790        Cf::Always => vk::CompareOp::ALWAYS,
791    }
792}
793
794pub fn map_shader_stage(stage: wgt::ShaderStages) -> vk::ShaderStageFlags {
795    let mut flags = vk::ShaderStageFlags::empty();
796    if stage.contains(wgt::ShaderStages::VERTEX) {
797        flags |= vk::ShaderStageFlags::VERTEX;
798    }
799    if stage.contains(wgt::ShaderStages::FRAGMENT) {
800        flags |= vk::ShaderStageFlags::FRAGMENT;
801    }
802    if stage.contains(wgt::ShaderStages::COMPUTE) {
803        flags |= vk::ShaderStageFlags::COMPUTE;
804    }
805    if stage.contains(wgt::ShaderStages::TASK) {
806        flags |= vk::ShaderStageFlags::TASK_EXT;
807    }
808    if stage.contains(wgt::ShaderStages::MESH) {
809        flags |= vk::ShaderStageFlags::MESH_EXT;
810    }
811    if stage.contains(wgt::ShaderStages::RAY_GENERATION) {
812        flags |= vk::ShaderStageFlags::RAYGEN_KHR;
813    }
814    if stage.contains(wgt::ShaderStages::MISS) {
815        flags |= vk::ShaderStageFlags::MISS_KHR;
816    }
817    if stage.contains(wgt::ShaderStages::ANY_HIT) {
818        flags |= vk::ShaderStageFlags::ANY_HIT_KHR;
819    }
820    if stage.contains(wgt::ShaderStages::CLOSEST_HIT) {
821        flags |= vk::ShaderStageFlags::CLOSEST_HIT_KHR;
822    }
823    flags
824}
825
826pub fn map_binding_type(ty: wgt::BindingType) -> vk::DescriptorType {
827    match ty {
828        wgt::BindingType::Buffer {
829            ty,
830            has_dynamic_offset,
831            ..
832        } => match ty {
833            wgt::BufferBindingType::Storage { .. } => match has_dynamic_offset {
834                true => vk::DescriptorType::STORAGE_BUFFER_DYNAMIC,
835                false => vk::DescriptorType::STORAGE_BUFFER,
836            },
837            wgt::BufferBindingType::Uniform => match has_dynamic_offset {
838                true => vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC,
839                false => vk::DescriptorType::UNIFORM_BUFFER,
840            },
841        },
842        wgt::BindingType::Sampler { .. } => vk::DescriptorType::SAMPLER,
843        wgt::BindingType::Texture { .. } => vk::DescriptorType::SAMPLED_IMAGE,
844        wgt::BindingType::StorageTexture { .. } => vk::DescriptorType::STORAGE_IMAGE,
845        wgt::BindingType::AccelerationStructure { .. } => {
846            vk::DescriptorType::ACCELERATION_STRUCTURE_KHR
847        }
848        wgt::BindingType::ExternalTexture => unimplemented!(),
849    }
850}
851
852pub fn map_topology(topology: wgt::PrimitiveTopology) -> vk::PrimitiveTopology {
853    use wgt::PrimitiveTopology as Pt;
854    match topology {
855        Pt::PointList => vk::PrimitiveTopology::POINT_LIST,
856        Pt::LineList => vk::PrimitiveTopology::LINE_LIST,
857        Pt::LineStrip => vk::PrimitiveTopology::LINE_STRIP,
858        Pt::TriangleList => vk::PrimitiveTopology::TRIANGLE_LIST,
859        Pt::TriangleStrip => vk::PrimitiveTopology::TRIANGLE_STRIP,
860    }
861}
862
863pub fn map_polygon_mode(mode: wgt::PolygonMode) -> vk::PolygonMode {
864    match mode {
865        wgt::PolygonMode::Fill => vk::PolygonMode::FILL,
866        wgt::PolygonMode::Line => vk::PolygonMode::LINE,
867        wgt::PolygonMode::Point => vk::PolygonMode::POINT,
868    }
869}
870
871pub fn map_front_face(front_face: wgt::FrontFace) -> vk::FrontFace {
872    match front_face {
873        wgt::FrontFace::Cw => vk::FrontFace::CLOCKWISE,
874        wgt::FrontFace::Ccw => vk::FrontFace::COUNTER_CLOCKWISE,
875    }
876}
877
878pub fn map_cull_face(face: wgt::Face) -> vk::CullModeFlags {
879    match face {
880        wgt::Face::Front => vk::CullModeFlags::FRONT,
881        wgt::Face::Back => vk::CullModeFlags::BACK,
882    }
883}
884
885pub fn map_stencil_op(op: wgt::StencilOperation) -> vk::StencilOp {
886    use wgt::StencilOperation as So;
887    match op {
888        So::Keep => vk::StencilOp::KEEP,
889        So::Zero => vk::StencilOp::ZERO,
890        So::Replace => vk::StencilOp::REPLACE,
891        So::Invert => vk::StencilOp::INVERT,
892        So::IncrementClamp => vk::StencilOp::INCREMENT_AND_CLAMP,
893        So::IncrementWrap => vk::StencilOp::INCREMENT_AND_WRAP,
894        So::DecrementClamp => vk::StencilOp::DECREMENT_AND_CLAMP,
895        So::DecrementWrap => vk::StencilOp::DECREMENT_AND_WRAP,
896    }
897}
898
899pub fn map_stencil_face(
900    face: &wgt::StencilFaceState,
901    compare_mask: u32,
902    write_mask: u32,
903) -> vk::StencilOpState {
904    vk::StencilOpState {
905        fail_op: map_stencil_op(face.fail_op),
906        pass_op: map_stencil_op(face.pass_op),
907        depth_fail_op: map_stencil_op(face.depth_fail_op),
908        compare_op: map_comparison(face.compare),
909        compare_mask,
910        write_mask,
911        reference: 0,
912    }
913}
914
915fn map_blend_factor(factor: wgt::BlendFactor) -> vk::BlendFactor {
916    use wgt::BlendFactor as Bf;
917    match factor {
918        Bf::Zero => vk::BlendFactor::ZERO,
919        Bf::One => vk::BlendFactor::ONE,
920        Bf::Src => vk::BlendFactor::SRC_COLOR,
921        Bf::OneMinusSrc => vk::BlendFactor::ONE_MINUS_SRC_COLOR,
922        Bf::SrcAlpha => vk::BlendFactor::SRC_ALPHA,
923        Bf::OneMinusSrcAlpha => vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
924        Bf::Dst => vk::BlendFactor::DST_COLOR,
925        Bf::OneMinusDst => vk::BlendFactor::ONE_MINUS_DST_COLOR,
926        Bf::DstAlpha => vk::BlendFactor::DST_ALPHA,
927        Bf::OneMinusDstAlpha => vk::BlendFactor::ONE_MINUS_DST_ALPHA,
928        Bf::SrcAlphaSaturated => vk::BlendFactor::SRC_ALPHA_SATURATE,
929        Bf::Constant => vk::BlendFactor::CONSTANT_COLOR,
930        Bf::OneMinusConstant => vk::BlendFactor::ONE_MINUS_CONSTANT_COLOR,
931        Bf::Src1 => vk::BlendFactor::SRC1_COLOR,
932        Bf::OneMinusSrc1 => vk::BlendFactor::ONE_MINUS_SRC1_COLOR,
933        Bf::Src1Alpha => vk::BlendFactor::SRC1_ALPHA,
934        Bf::OneMinusSrc1Alpha => vk::BlendFactor::ONE_MINUS_SRC1_ALPHA,
935    }
936}
937
938fn map_blend_op(operation: wgt::BlendOperation) -> vk::BlendOp {
939    use wgt::BlendOperation as Bo;
940    match operation {
941        Bo::Add => vk::BlendOp::ADD,
942        Bo::Subtract => vk::BlendOp::SUBTRACT,
943        Bo::ReverseSubtract => vk::BlendOp::REVERSE_SUBTRACT,
944        Bo::Min => vk::BlendOp::MIN,
945        Bo::Max => vk::BlendOp::MAX,
946    }
947}
948
949pub fn map_blend_component(
950    component: &wgt::BlendComponent,
951) -> (vk::BlendOp, vk::BlendFactor, vk::BlendFactor) {
952    let op = map_blend_op(component.operation);
953    let src = map_blend_factor(component.src_factor);
954    let dst = map_blend_factor(component.dst_factor);
955    (op, src, dst)
956}
957
958pub fn map_pipeline_statistics(
959    types: wgt::PipelineStatisticsTypes,
960) -> vk::QueryPipelineStatisticFlags {
961    use wgt::PipelineStatisticsTypes as Pst;
962    let mut flags = vk::QueryPipelineStatisticFlags::empty();
963    if types.contains(Pst::VERTEX_SHADER_INVOCATIONS) {
964        flags |= vk::QueryPipelineStatisticFlags::VERTEX_SHADER_INVOCATIONS;
965    }
966    if types.contains(Pst::CLIPPER_INVOCATIONS) {
967        flags |= vk::QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS;
968    }
969    if types.contains(Pst::CLIPPER_PRIMITIVES_OUT) {
970        flags |= vk::QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES;
971    }
972    if types.contains(Pst::FRAGMENT_SHADER_INVOCATIONS) {
973        flags |= vk::QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS;
974    }
975    if types.contains(Pst::COMPUTE_SHADER_INVOCATIONS) {
976        flags |= vk::QueryPipelineStatisticFlags::COMPUTE_SHADER_INVOCATIONS;
977    }
978    flags
979}
980
981pub fn map_acceleration_structure_format(
982    format: crate::AccelerationStructureFormat,
983) -> vk::AccelerationStructureTypeKHR {
984    match format {
985        crate::AccelerationStructureFormat::TopLevel => vk::AccelerationStructureTypeKHR::TOP_LEVEL,
986        crate::AccelerationStructureFormat::BottomLevel => {
987            vk::AccelerationStructureTypeKHR::BOTTOM_LEVEL
988        }
989    }
990}
991
992pub fn map_acceleration_structure_build_mode(
993    format: crate::AccelerationStructureBuildMode,
994) -> vk::BuildAccelerationStructureModeKHR {
995    match format {
996        crate::AccelerationStructureBuildMode::Build => {
997            vk::BuildAccelerationStructureModeKHR::BUILD
998        }
999        crate::AccelerationStructureBuildMode::Update => {
1000            vk::BuildAccelerationStructureModeKHR::UPDATE
1001        }
1002    }
1003}
1004
1005pub fn map_acceleration_structure_flags(
1006    flags: crate::AccelerationStructureBuildFlags,
1007) -> vk::BuildAccelerationStructureFlagsKHR {
1008    let mut vk_flags = vk::BuildAccelerationStructureFlagsKHR::empty();
1009
1010    if flags.contains(crate::AccelerationStructureBuildFlags::PREFER_FAST_TRACE) {
1011        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE;
1012    }
1013
1014    if flags.contains(crate::AccelerationStructureBuildFlags::PREFER_FAST_BUILD) {
1015        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_BUILD;
1016    }
1017
1018    if flags.contains(crate::AccelerationStructureBuildFlags::ALLOW_UPDATE) {
1019        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::ALLOW_UPDATE;
1020    }
1021
1022    if flags.contains(crate::AccelerationStructureBuildFlags::LOW_MEMORY) {
1023        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::LOW_MEMORY;
1024    }
1025
1026    if flags.contains(crate::AccelerationStructureBuildFlags::ALLOW_COMPACTION) {
1027        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::ALLOW_COMPACTION
1028    }
1029
1030    if flags.contains(crate::AccelerationStructureBuildFlags::ALLOW_RAY_HIT_VERTEX_RETURN) {
1031        vk_flags |= vk::BuildAccelerationStructureFlagsKHR::ALLOW_DATA_ACCESS
1032    }
1033
1034    vk_flags
1035}
1036
1037pub fn map_acceleration_structure_geometry_flags(
1038    flags: crate::AccelerationStructureGeometryFlags,
1039) -> vk::GeometryFlagsKHR {
1040    let mut vk_flags = vk::GeometryFlagsKHR::empty();
1041
1042    if flags.contains(crate::AccelerationStructureGeometryFlags::OPAQUE) {
1043        vk_flags |= vk::GeometryFlagsKHR::OPAQUE;
1044    }
1045
1046    if flags.contains(crate::AccelerationStructureGeometryFlags::NO_DUPLICATE_ANY_HIT_INVOCATION) {
1047        vk_flags |= vk::GeometryFlagsKHR::NO_DUPLICATE_ANY_HIT_INVOCATION;
1048    }
1049
1050    vk_flags
1051}
1052
1053pub fn map_acceleration_structure_usage_to_barrier(
1054    usage: crate::AccelerationStructureUses,
1055    features: wgt::Features,
1056    queue_flags: vk::QueueFlags,
1057) -> (vk::PipelineStageFlags, vk::AccessFlags) {
1058    let mut stages = vk::PipelineStageFlags::empty();
1059    let mut access = vk::AccessFlags::empty();
1060
1061    if usage.contains(crate::AccelerationStructureUses::BUILD_INPUT) {
1062        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1063        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1064    }
1065    if usage.contains(crate::AccelerationStructureUses::QUERY_INPUT) {
1066        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1067        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1068    }
1069    if usage.contains(crate::AccelerationStructureUses::BUILD_OUTPUT) {
1070        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1071        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR;
1072    }
1073    if usage.contains(crate::AccelerationStructureUses::SHADER_INPUT)
1074        && features.contains(wgt::Features::EXPERIMENTAL_RAY_QUERY)
1075    {
1076        stages |= shader_stages(queue_flags);
1077        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1078    }
1079    if usage.contains(crate::AccelerationStructureUses::SHADER_INPUT)
1080        && features.contains(wgt::Features::EXPERIMENTAL_RAY_TRACING_PIPELINES)
1081    {
1082        stages |= vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR;
1083        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1084    }
1085    if usage.contains(crate::AccelerationStructureUses::COPY_SRC) {
1086        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1087        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR;
1088    }
1089    if usage.contains(crate::AccelerationStructureUses::COPY_DST) {
1090        stages |= vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR;
1091        access |= vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR;
1092    }
1093
1094    (stages, access)
1095}
1096
1097#[cfg(test)]
1098mod tests {
1099    use super::*;
1100
1101    /// `map_vk_color_space` and `map_surface_color_space` must stay mutually
1102    /// inverse so that a color space reported in the surface capabilities is
1103    /// exactly what the swapchain is created with.
1104    #[test]
1105    fn color_space_round_trip() {
1106        for vk_color_space in [
1107            vk::ColorSpaceKHR::SRGB_NONLINEAR,
1108            vk::ColorSpaceKHR::EXTENDED_SRGB_LINEAR_EXT,
1109            vk::ColorSpaceKHR::EXTENDED_SRGB_NONLINEAR_EXT,
1110            vk::ColorSpaceKHR::DISPLAY_P3_NONLINEAR_EXT,
1111            vk::ColorSpaceKHR::HDR10_ST2084_EXT,
1112            vk::ColorSpaceKHR::HDR10_HLG_EXT,
1113        ] {
1114            let mapped = map_vk_color_space(vk_color_space).unwrap();
1115            assert_eq!(map_surface_color_space(mapped), vk_color_space);
1116        }
1117    }
1118
1119    #[test]
1120    fn unknown_color_spaces_are_dropped() {
1121        for vk_color_space in [
1122            vk::ColorSpaceKHR::BT2020_LINEAR_EXT,
1123            vk::ColorSpaceKHR::DOLBYVISION_EXT,
1124            vk::ColorSpaceKHR::PASS_THROUGH_EXT,
1125            vk::ColorSpaceKHR::ADOBERGB_NONLINEAR_EXT,
1126        ] {
1127            assert!(map_vk_color_space(vk_color_space).is_none());
1128            assert!(map_vk_surface_formats(vk::SurfaceFormatKHR {
1129                format: vk::Format::B8G8R8A8_UNORM,
1130                color_space: vk_color_space,
1131            })
1132            .is_none());
1133        }
1134    }
1135
1136    #[test]
1137    fn hdr10_surface_format_maps() {
1138        let (format, color_space) = map_vk_surface_formats(vk::SurfaceFormatKHR {
1139            format: vk::Format::A2B10G10R10_UNORM_PACK32,
1140            color_space: vk::ColorSpaceKHR::HDR10_ST2084_EXT,
1141        })
1142        .unwrap();
1143        assert_eq!(format, wgt::TextureFormat::Rgb10a2Unorm);
1144        assert_eq!(color_space, wgt::SurfaceColorSpace::Bt2100Pq);
1145    }
1146
1147    #[test]
1148    fn buffer_shader_stages_follow_queue_flags() {
1149        let usage = wgt::BufferUses::UNIFORM;
1150
1151        let (stages, access) = map_buffer_usage_to_barrier(usage, vk::QueueFlags::GRAPHICS);
1152        assert_eq!(
1153            stages,
1154            vk::PipelineStageFlags::VERTEX_SHADER | vk::PipelineStageFlags::FRAGMENT_SHADER
1155        );
1156        assert_eq!(access, vk::AccessFlags::UNIFORM_READ);
1157
1158        let (stages, access) = map_buffer_usage_to_barrier(usage, vk::QueueFlags::COMPUTE);
1159        assert_eq!(stages, vk::PipelineStageFlags::COMPUTE_SHADER);
1160        assert_eq!(access, vk::AccessFlags::UNIFORM_READ);
1161
1162        let (stages, access) =
1163            map_buffer_usage_to_barrier(usage, vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE);
1164        assert_eq!(
1165            stages,
1166            vk::PipelineStageFlags::VERTEX_SHADER
1167                | vk::PipelineStageFlags::FRAGMENT_SHADER
1168                | vk::PipelineStageFlags::COMPUTE_SHADER
1169        );
1170        assert_eq!(access, vk::AccessFlags::UNIFORM_READ);
1171    }
1172
1173    #[test]
1174    fn non_shader_buffer_usage_is_unchanged() {
1175        let (stages, access) = map_buffer_usage_to_barrier(
1176            wgt::BufferUses::COPY_SRC | wgt::BufferUses::VERTEX,
1177            vk::QueueFlags::empty(),
1178        );
1179
1180        assert_eq!(
1181            stages,
1182            vk::PipelineStageFlags::TRANSFER | vk::PipelineStageFlags::VERTEX_INPUT
1183        );
1184        assert_eq!(
1185            access,
1186            vk::AccessFlags::TRANSFER_READ | vk::AccessFlags::VERTEX_ATTRIBUTE_READ
1187        );
1188    }
1189
1190    #[test]
1191    fn texture_shader_stages_follow_queue_flags() {
1192        let usage = wgt::TextureUses::RESOURCE;
1193
1194        let (stages, access) = map_texture_usage_to_barrier(usage, vk::QueueFlags::GRAPHICS);
1195        assert_eq!(
1196            stages,
1197            vk::PipelineStageFlags::VERTEX_SHADER | vk::PipelineStageFlags::FRAGMENT_SHADER
1198        );
1199        assert_eq!(access, vk::AccessFlags::SHADER_READ);
1200
1201        let (stages, access) = map_texture_usage_to_barrier(usage, vk::QueueFlags::COMPUTE);
1202        assert_eq!(stages, vk::PipelineStageFlags::COMPUTE_SHADER);
1203        assert_eq!(access, vk::AccessFlags::SHADER_READ);
1204
1205        let (stages, access) =
1206            map_texture_usage_to_barrier(usage, vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE);
1207        assert_eq!(
1208            stages,
1209            vk::PipelineStageFlags::VERTEX_SHADER
1210                | vk::PipelineStageFlags::FRAGMENT_SHADER
1211                | vk::PipelineStageFlags::COMPUTE_SHADER
1212        );
1213        assert_eq!(access, vk::AccessFlags::SHADER_READ);
1214    }
1215
1216    #[test]
1217    fn non_shader_texture_usage_is_unchanged() {
1218        let (stages, access) = map_texture_usage_to_barrier(
1219            wgt::TextureUses::COPY_SRC | wgt::TextureUses::COLOR_TARGET,
1220            vk::QueueFlags::empty(),
1221        );
1222
1223        assert_eq!(
1224            stages,
1225            vk::PipelineStageFlags::TRANSFER | vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
1226        );
1227        assert_eq!(
1228            access,
1229            vk::AccessFlags::TRANSFER_READ
1230                | vk::AccessFlags::COLOR_ATTACHMENT_READ
1231                | vk::AccessFlags::COLOR_ATTACHMENT_WRITE
1232        );
1233    }
1234
1235    #[test]
1236    fn acceleration_structure_shader_stages_follow_queue_flags() {
1237        let usage = crate::AccelerationStructureUses::SHADER_INPUT;
1238        let features = wgt::Features::EXPERIMENTAL_RAY_QUERY;
1239
1240        let (stages, access) =
1241            map_acceleration_structure_usage_to_barrier(usage, features, vk::QueueFlags::GRAPHICS);
1242        assert_eq!(
1243            stages,
1244            vk::PipelineStageFlags::VERTEX_SHADER | vk::PipelineStageFlags::FRAGMENT_SHADER
1245        );
1246        assert_eq!(access, vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR);
1247
1248        let (stages, access) =
1249            map_acceleration_structure_usage_to_barrier(usage, features, vk::QueueFlags::COMPUTE);
1250        assert_eq!(stages, vk::PipelineStageFlags::COMPUTE_SHADER);
1251        assert_eq!(access, vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR);
1252
1253        let (stages, access) = map_acceleration_structure_usage_to_barrier(
1254            usage,
1255            features,
1256            vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE,
1257        );
1258        assert_eq!(
1259            stages,
1260            vk::PipelineStageFlags::VERTEX_SHADER
1261                | vk::PipelineStageFlags::FRAGMENT_SHADER
1262                | vk::PipelineStageFlags::COMPUTE_SHADER
1263        );
1264        assert_eq!(access, vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR);
1265    }
1266
1267    #[test]
1268    fn non_shader_acceleration_structure_usage_is_unchanged() {
1269        let (stages, access) = map_acceleration_structure_usage_to_barrier(
1270            crate::AccelerationStructureUses::BUILD_INPUT
1271                | crate::AccelerationStructureUses::BUILD_OUTPUT,
1272            wgt::Features::empty(),
1273            vk::QueueFlags::empty(),
1274        );
1275
1276        assert_eq!(
1277            stages,
1278            vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR
1279        );
1280        assert_eq!(
1281            access,
1282            vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR
1283                | vk::AccessFlags::ACCELERATION_STRUCTURE_WRITE_KHR
1284        );
1285    }
1286}