1use alloc::sync::Arc;
35use alloc::vec;
36use alloc::vec::Vec;
37use core::ffi::c_void;
38
39use rt::sync::SpinLock;
40
41use crate::vk_dev::{Device, Garbage, Shared};
42use crate::vk_ffi as vk;
43use crate::vk_mem::Alloc;
44use crate::*;
45
46pub(crate) fn vk_format(f: Format) -> u32 {
49 use Format::*;
50 match f {
51 R8Unorm => vk::FORMAT_R8_UNORM,
52 Rg8Unorm => vk::FORMAT_R8G8_UNORM,
53 Rgba8Unorm => vk::FORMAT_R8G8B8A8_UNORM,
54 Rgba8Srgb => vk::FORMAT_R8G8B8A8_SRGB,
55 Bgra8Unorm => vk::FORMAT_B8G8R8A8_UNORM,
56 Bgra8Srgb => vk::FORMAT_B8G8R8A8_SRGB,
57 Rgb10a2Unorm => vk::FORMAT_A2B10G10R10_UNORM_PACK32,
58 Rg11b10Float => vk::FORMAT_B10G11R11_UFLOAT_PACK32,
59 R16Float => vk::FORMAT_R16_SFLOAT,
60 Rg16Float => vk::FORMAT_R16G16_SFLOAT,
61 Rgba16Float => vk::FORMAT_R16G16B16A16_SFLOAT,
62 R32Uint => vk::FORMAT_R32_UINT,
63 R32Sint => vk::FORMAT_R32_SINT,
64 R32Float => vk::FORMAT_R32_SFLOAT,
65 Rg32Uint => vk::FORMAT_R32G32_UINT,
66 Rg32Float => vk::FORMAT_R32G32_SFLOAT,
67 Rgba32Uint => vk::FORMAT_R32G32B32A32_UINT,
68 Rgba32Float => vk::FORMAT_R32G32B32A32_SFLOAT,
69 Depth16 => vk::FORMAT_D16_UNORM,
70 Depth32Float => vk::FORMAT_D32_SFLOAT,
71 Depth24Stencil8 => vk::FORMAT_D24_UNORM_S8_UINT,
72 Depth32FloatStencil8 => vk::FORMAT_D32_SFLOAT_S8_UINT,
73 Bc1Unorm => vk::FORMAT_BC1_RGBA_UNORM_BLOCK,
74 Bc1Srgb => vk::FORMAT_BC1_RGBA_SRGB_BLOCK,
75 Bc3Unorm => vk::FORMAT_BC3_UNORM_BLOCK,
76 Bc3Srgb => vk::FORMAT_BC3_SRGB_BLOCK,
77 Bc4Unorm => vk::FORMAT_BC4_UNORM_BLOCK,
78 Bc5Unorm => vk::FORMAT_BC5_UNORM_BLOCK,
79 Bc7Unorm => vk::FORMAT_BC7_UNORM_BLOCK,
80 Bc7Srgb => vk::FORMAT_BC7_SRGB_BLOCK,
81 }
82}
83
84fn aspects(f: Format) -> (u32, u32) {
86 if f.has_stencil() {
87 (vk::IMAGE_ASPECT_DEPTH_BIT | vk::IMAGE_ASPECT_STENCIL_BIT, vk::IMAGE_ASPECT_DEPTH_BIT)
88 } else if f.is_depth() {
89 (vk::IMAGE_ASPECT_DEPTH_BIT, vk::IMAGE_ASPECT_DEPTH_BIT)
90 } else {
91 (vk::IMAGE_ASPECT_COLOR_BIT, vk::IMAGE_ASPECT_COLOR_BIT)
92 }
93}
94
95pub(crate) fn compare_op(c: Compare) -> u32 {
96 match c {
97 Compare::Never => vk::COMPARE_OP_NEVER,
98 Compare::Less => vk::COMPARE_OP_LESS,
99 Compare::Equal => vk::COMPARE_OP_EQUAL,
100 Compare::LessEqual => vk::COMPARE_OP_LESS_OR_EQUAL,
101 Compare::Greater => vk::COMPARE_OP_GREATER,
102 Compare::NotEqual => vk::COMPARE_OP_NOT_EQUAL,
103 Compare::GreaterEqual => vk::COMPARE_OP_GREATER_OR_EQUAL,
104 Compare::Always => vk::COMPARE_OP_ALWAYS,
105 }
106}
107
108fn stage_bits(s: Stages) -> u32 {
109 let mut v = 0;
110 if s.intersects(Stages::VERTEX) {
111 v |= vk::SHADER_STAGE_VERTEX_BIT;
112 }
113 if s.intersects(Stages::FRAGMENT) {
114 v |= vk::SHADER_STAGE_FRAGMENT_BIT;
115 }
116 if s.intersects(Stages::COMPUTE) {
117 v |= vk::SHADER_STAGE_COMPUTE_BIT;
118 }
119 v
120}
121
122fn descriptor_type(k: BindingKind) -> u32 {
123 match k {
124 BindingKind::UniformBuffer => vk::DESCRIPTOR_TYPE_UNIFORM_BUFFER,
125 BindingKind::StorageBuffer | BindingKind::StorageBufferRead => vk::DESCRIPTOR_TYPE_STORAGE_BUFFER,
126 BindingKind::Texture => vk::DESCRIPTOR_TYPE_SAMPLED_IMAGE,
127 BindingKind::StorageTexture => vk::DESCRIPTOR_TYPE_STORAGE_IMAGE,
128 BindingKind::Sampler => vk::DESCRIPTOR_TYPE_SAMPLER,
129 }
130}
131
132fn nul_name(s: &str) -> Vec<u8> {
133 let mut v: Vec<u8> = s.bytes().filter(|&b| b != 0).collect();
134 v.push(0);
135 v
136}
137
138pub(crate) struct BufInner {
141 sh: Arc<Shared>,
142 h: vk::Handle,
143 a: Alloc,
144 size: u64,
145 usage: BufferUsage,
146 mem: MemoryKind,
147}
148
149impl Drop for BufInner {
150 fn drop(&mut self) {
151 self.sh.defer(Garbage::Buffer(self.h, self.a));
152 }
153}
154
155#[derive(Clone)]
157pub struct Buffer(Arc<BufInner>);
158
159impl Buffer {
160 pub fn size(&self) -> u64 {
161 self.0.size
162 }
163 pub fn usage(&self) -> BufferUsage {
164 self.0.usage
165 }
166 pub fn memory(&self) -> MemoryKind {
167 self.0.mem
168 }
169 pub(crate) fn handle(&self) -> vk::Handle {
170 self.0.h
171 }
172
173 fn mapped_range(&self, offset: u64, n: u64) -> Result<usize> {
174 let b = &self.0;
175 if b.mem == MemoryKind::Device || b.a.ptr == 0 {
178 return Err(GpuError::Invalid("buffer not host-visible (MemoryKind::Device)"));
179 }
180 if offset.checked_add(n).is_none_or(|e| e > b.size) {
181 return Err(GpuError::Invalid("range outside the buffer"));
182 }
183 Ok(b.a.ptr + offset as usize)
184 }
185
186 fn atom_range(&self, offset: u64, n: u64) -> vk::MappedMemoryRange {
188 let b = &self.0;
189 let atom = b.sh.raw.non_coherent_atom_size.max(1);
190 let start = (b.a.offset + offset) / atom * atom;
191 let end = (b.a.offset + offset + n).div_ceil(atom) * atom;
192 let block_end = b.a.offset + b.a.size; vk::MappedMemoryRange {
194 s_type: vk::STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
195 memory: b.a.mem,
196 offset: start,
197 size: if end > block_end { vk::WHOLE_SIZE } else { end - start },
198 ..vk::Zero::zero()
199 }
200 }
201
202 pub fn write<T: Pod>(&self, offset: u64, data: &[T]) -> Result<()> {
205 let bytes = bytes_of(data);
206 let p = self.mapped_range(offset, bytes.len() as u64)?;
207 unsafe { core::ptr::copy_nonoverlapping(bytes.as_ptr(), p as *mut u8, bytes.len()) };
209 if !self.0.a.coherent && !bytes.is_empty() {
210 let r = self.atom_range(offset, bytes.len() as u64);
211 self.0.sh.vk(unsafe { (self.0.sh.f.flush_mapped_memory_ranges)(self.0.sh.dev, 1, &r) })?;
213 }
214 Ok(())
215 }
216
217 pub fn read<T: Pod>(&self, offset: u64, out: &mut [T]) -> Result<()> {
220 let n = core::mem::size_of_val(out);
221 let p = self.mapped_range(offset, n as u64)?;
222 if !self.0.a.coherent && n > 0 {
223 let r = self.atom_range(offset, n as u64);
224 self.0.sh.vk(unsafe { (self.0.sh.f.invalidate_mapped_memory_ranges)(self.0.sh.dev, 1, &r) })?;
226 }
227 unsafe { core::ptr::copy_nonoverlapping(p as *const u8, out.as_mut_ptr() as *mut u8, n) };
229 Ok(())
230 }
231}
232
233impl Device {
234 pub fn create_buffer(&self, desc: &BufferDesc) -> Result<Buffer> {
235 let sh = &self.sh;
236 sh.check()?;
237 if desc.size == 0 {
238 return Err(GpuError::Invalid("buffer size 0"));
239 }
240 let u = desc.usage;
241 let mut usage = vk::BUFFER_USAGE_TRANSFER_SRC_BIT | vk::BUFFER_USAGE_TRANSFER_DST_BIT;
242 for (b, v) in [
243 (BufferUsage::UNIFORM, vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT),
244 (BufferUsage::STORAGE, vk::BUFFER_USAGE_STORAGE_BUFFER_BIT),
245 (BufferUsage::INDEX, vk::BUFFER_USAGE_INDEX_BUFFER_BIT),
246 (BufferUsage::VERTEX, vk::BUFFER_USAGE_VERTEX_BUFFER_BIT),
247 (BufferUsage::INDIRECT, vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT),
248 ] {
249 if u.contains(b) {
250 usage |= v;
251 }
252 }
253 let mut st = sh.st.lock();
254 let (h, a) = sh.raw_buffer(&mut st, desc.size, usage, desc.memory)?;
255 drop(st);
256 Ok(Buffer(Arc::new(BufInner { sh: sh.clone(), h, a, size: desc.size, usage: u, mem: desc.memory })))
257 }
258}
259
260pub(crate) struct TexInner {
263 sh: Arc<Shared>,
264 pub image: vk::Handle,
265 a: Option<Alloc>,
266 owned: bool,
267 pub desc: TextureDesc,
268 pub aspect_all: u32,
269 pub aspect_copy: u32,
270}
271
272impl Drop for TexInner {
273 fn drop(&mut self) {
274 if self.owned {
275 self.sh.defer(Garbage::Image(self.image, self.a));
276 }
277 }
278}
279
280pub(crate) struct ViewInner {
281 sh: Arc<Shared>,
282 pub h: vk::Handle,
283 pub tex: Arc<TexInner>,
284 pub base_mip: u32,
285 pub mips: u32,
286 pub base_layer: u32,
287 pub layers: u32,
288}
289
290impl Drop for ViewInner {
291 fn drop(&mut self) {
292 self.sh.defer(Garbage::View(self.h));
293 }
294}
295
296#[derive(Clone)]
298pub struct TextureView(pub(crate) Arc<ViewInner>);
299
300impl TextureView {
301 pub fn format(&self) -> Format {
302 self.0.tex.desc.format
303 }
304 pub fn size(&self) -> (u32, u32) {
306 let d = &self.0.tex.desc;
307 ((d.width >> self.0.base_mip).max(1), (d.height >> self.0.base_mip).max(1))
308 }
309 pub(crate) fn usage(&self) -> TextureUsage {
310 self.0.tex.desc.usage
311 }
312}
313
314#[derive(Clone)]
316pub struct Texture {
317 pub(crate) t: Arc<TexInner>,
318 view: TextureView,
319}
320
321impl Texture {
322 pub fn view(&self) -> &TextureView {
324 &self.view
325 }
326 pub fn width(&self) -> u32 {
327 self.t.desc.width
328 }
329 pub fn height(&self) -> u32 {
330 self.t.desc.height
331 }
332 pub fn depth(&self) -> u32 {
333 self.t.desc.depth
334 }
335 pub fn layers(&self) -> u32 {
336 self.t.desc.layers
337 }
338 pub fn mips(&self) -> u32 {
339 self.t.desc.mips
340 }
341 pub fn format(&self) -> Format {
342 self.t.desc.format
343 }
344 pub fn usage(&self) -> TextureUsage {
345 self.t.desc.usage
346 }
347 pub(crate) fn image(&self) -> vk::Handle {
348 self.t.image
349 }
350
351 pub fn create_view(&self, desc: &ViewDesc) -> Result<TextureView> {
353 let d = &self.t.desc;
354 let mips = if desc.mips == 0 { d.mips.saturating_sub(desc.base_mip) } else { desc.mips };
355 let layers = if desc.layers == 0 { d.layers.saturating_sub(desc.base_layer) } else { desc.layers };
356 if mips == 0 || desc.base_mip + mips > d.mips || layers == 0 || desc.base_layer + layers > d.layers {
357 return Err(GpuError::Invalid("view range outside the texture"));
358 }
359 let ok = match desc.dim {
360 ViewDim::D2 => d.depth == 1 && layers == 1,
361 ViewDim::D2Array => d.depth == 1,
362 ViewDim::Cube => d.cube && layers == 6,
363 ViewDim::CubeArray => d.cube && layers % 6 == 0,
364 ViewDim::D3 => d.depth > 1,
365 };
366 if !ok {
367 return Err(GpuError::Invalid("view dimension does not fit the texture"));
368 }
369 make_view(&self.t, desc.dim, desc.base_mip, mips, desc.base_layer, layers)
370 }
371}
372
373fn make_view(t: &Arc<TexInner>, dim: ViewDim, base_mip: u32, mips: u32, base_layer: u32, layers: u32) -> Result<TextureView> {
374 let sh = &t.sh;
375 let vi = vk::ImageViewCreateInfo {
376 s_type: vk::STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
377 image: t.image,
378 view_type: match dim {
379 ViewDim::D2 => vk::IMAGE_VIEW_TYPE_2D,
380 ViewDim::D2Array => vk::IMAGE_VIEW_TYPE_2D_ARRAY,
381 ViewDim::Cube => vk::IMAGE_VIEW_TYPE_CUBE,
382 ViewDim::CubeArray => vk::IMAGE_VIEW_TYPE_CUBE_ARRAY,
383 ViewDim::D3 => vk::IMAGE_VIEW_TYPE_3D,
384 },
385 format: vk_format(t.desc.format),
386 subresource_range: vk::ImageSubresourceRange {
387 aspect_mask: t.aspect_copy,
388 base_mip_level: base_mip,
389 level_count: mips,
390 base_array_layer: base_layer,
391 layer_count: layers,
392 },
393 ..vk::Zero::zero()
394 };
395 let mut h = 0;
396 sh.vk(unsafe { (sh.f.create_image_view)(sh.dev, &vi, core::ptr::null(), &mut h) })?;
398 Ok(TextureView(Arc::new(ViewInner { sh: sh.clone(), h, tex: t.clone(), base_mip, mips, base_layer, layers })))
399}
400
401fn default_dim(d: &TextureDesc) -> ViewDim {
403 if d.depth > 1 {
404 ViewDim::D3
405 } else if d.cube {
406 if d.layers == 6 { ViewDim::Cube } else { ViewDim::CubeArray }
407 } else if d.layers > 1 {
408 ViewDim::D2Array
409 } else {
410 ViewDim::D2
411 }
412}
413
414pub(crate) fn wrap_image(sh: &Arc<Shared>, image: vk::Handle, desc: TextureDesc) -> Result<Texture> {
416 let (aspect_all, aspect_copy) = aspects(desc.format);
417 let t = Arc::new(TexInner { sh: sh.clone(), image, a: None, owned: false, desc, aspect_all, aspect_copy });
418 let view = make_view(&t, ViewDim::D2, 0, 1, 0, 1)?;
419 Ok(Texture { t, view })
420}
421
422impl Device {
423 pub fn create_texture(&self, desc: &TextureDesc) -> Result<Texture> {
424 let sh = &self.sh;
425 sh.check()?;
426 let d = *desc;
427 let lim = &sh.limits;
428 let max_dim = if d.depth > 1 { lim.max_texture_3d } else { lim.max_texture_2d };
429 if d.width == 0 || d.height == 0 || d.depth == 0 || d.layers == 0 || d.mips == 0 {
430 return Err(GpuError::Invalid("texture size, layers or mips 0"));
431 }
432 if d.width > max_dim || d.height > max_dim || d.depth > max_dim || d.layers > lim.max_texture_layers {
433 return Err(GpuError::Invalid("texture larger than the device's limits"));
434 }
435 let longest = d.width.max(d.height).max(d.depth);
436 if d.mips > 32 - longest.leading_zeros() {
437 return Err(GpuError::Invalid("more mips than the size has"));
438 }
439 if d.depth > 1 && (d.layers > 1 || d.cube || d.format.is_depth()) {
440 return Err(GpuError::Invalid("3D textures have one layer and a colour format"));
441 }
442 if d.cube && (d.layers % 6 != 0 || d.width != d.height) {
443 return Err(GpuError::Invalid("cube textures are square with 6 * n layers"));
444 }
445 if d.usage == TextureUsage::NONE {
446 return Err(GpuError::Invalid("texture without usage"));
447 }
448 if d.format.is_compressed() && !sh.features.texture_bc {
449 return Err(GpuError::Unsupported("BC compressed textures"));
450 }
451 let fmt = vk_format(d.format);
452 let mut fp: vk::FormatProperties = vk::Zero::zero();
453 unsafe { (sh.inst.f.get_physical_device_format_properties)(sh.pd, fmt, &mut fp) };
455 let of = fp.optimal_tiling_features;
456 let mut usage = 0;
457 for (u, v, need) in [
458 (TextureUsage::COPY_SRC, vk::IMAGE_USAGE_TRANSFER_SRC_BIT, 0),
459 (TextureUsage::COPY_DST, vk::IMAGE_USAGE_TRANSFER_DST_BIT, 0),
460 (TextureUsage::SAMPLED, vk::IMAGE_USAGE_SAMPLED_BIT, vk::FORMAT_FEATURE_SAMPLED_IMAGE_BIT),
461 (TextureUsage::STORAGE, vk::IMAGE_USAGE_STORAGE_BIT, vk::FORMAT_FEATURE_STORAGE_IMAGE_BIT),
462 (TextureUsage::COLOR_TARGET, vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT, vk::FORMAT_FEATURE_COLOR_ATTACHMENT_BIT),
463 (TextureUsage::DEPTH_TARGET, vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
464 vk::FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT),
465 ] {
466 if d.usage.contains(u) {
467 if of & need != need {
468 return Err(GpuError::Unsupported("format with this usage"));
469 }
470 usage |= v;
471 }
472 }
473 if d.usage.contains(TextureUsage::COLOR_TARGET) && d.format.is_depth()
474 || d.usage.contains(TextureUsage::DEPTH_TARGET) && !d.format.is_depth()
475 {
476 return Err(GpuError::Invalid("colour targets need colour formats, depth targets depth formats"));
477 }
478 let ii = vk::ImageCreateInfo {
479 s_type: vk::STRUCTURE_TYPE_IMAGE_CREATE_INFO,
480 flags: if d.cube { vk::IMAGE_CREATE_CUBE_COMPATIBLE_BIT } else { 0 },
481 image_type: if d.depth > 1 { vk::IMAGE_TYPE_3D } else { vk::IMAGE_TYPE_2D },
482 format: fmt,
483 extent: vk::Extent3D { width: d.width, height: d.height, depth: d.depth },
484 mip_levels: d.mips,
485 array_layers: d.layers,
486 samples: vk::SAMPLE_COUNT_1_BIT,
487 tiling: vk::IMAGE_TILING_OPTIMAL,
488 usage,
489 sharing_mode: vk::SHARING_MODE_EXCLUSIVE,
490 initial_layout: vk::IMAGE_LAYOUT_UNDEFINED,
491 ..vk::Zero::zero()
492 };
493 let mut image = 0;
494 sh.vk(unsafe { (sh.f.create_image)(sh.dev, &ii, core::ptr::null(), &mut image) })?;
496 let mut req: vk::MemoryRequirements = vk::Zero::zero();
497 unsafe { (sh.f.get_image_memory_requirements)(sh.dev, image, &mut req) };
499 let mut st = sh.st.lock();
500 let a = match st.mem.alloc(&sh.f, sh.dev, &req, MemoryKind::Device, false) {
501 Ok(a) => a,
502 Err(e) => {
503 unsafe { (sh.f.destroy_image)(sh.dev, image, core::ptr::null()) };
505 return Err(e);
506 }
507 };
508 drop(st);
509 if let Err(e) = sh.vk(unsafe { (sh.f.bind_image_memory)(sh.dev, image, a.mem, a.offset) }) {
511 sh.defer(Garbage::Image(image, Some(a)));
512 return Err(e);
513 }
514 let (aspect_all, aspect_copy) = aspects(d.format);
515 let t = Arc::new(TexInner { sh: sh.clone(), image, a: Some(a), owned: true, desc: d, aspect_all, aspect_copy });
516 let view = make_view(&t, default_dim(&d), 0, d.mips, 0, d.layers)?;
517 Ok(Texture { t, view })
518 }
519}
520
521pub(crate) fn subresources(t: &Texture, base_mip: u32, mips: u32, base_layer: u32, layers: u32) -> vk::ImageSubresourceRange {
523 vk::ImageSubresourceRange {
524 aspect_mask: t.t.aspect_all,
525 base_mip_level: base_mip,
526 level_count: mips,
527 base_array_layer: base_layer,
528 layer_count: layers,
529 }
530}
531
532pub(crate) fn copy_region(r: &TextureRegion, l: &DataLayout, data_len: u64) -> Result<(vk::BufferImageCopy, u64)> {
536 let t = r.texture;
537 let d = &t.t.desc;
538 if r.mip >= d.mips || r.layers == 0 || r.base_layer + r.layers > d.layers {
539 return Err(GpuError::Invalid("copy: mip or layers outside the texture"));
540 }
541 let (mw, mh, md) = ((d.width >> r.mip).max(1), (d.height >> r.mip).max(1), (d.depth >> r.mip).max(1));
542 let [x, y, z] = r.origin;
543 let [w, h, dd] = r.size;
544 if w == 0 || h == 0 || dd == 0 || x + w > mw || y + h > mh || z + dd > md {
545 return Err(GpuError::Invalid("copy: region outside the mip"));
546 }
547 let f = d.format;
548 let (bw, bb) = (f.block_dim(), f.block_bytes());
549 if bw > 1 && (x % bw != 0 || y % bw != 0 || (w % bw != 0 && x + w != mw) || (h % bw != 0 && y + h != mh)) {
550 return Err(GpuError::Invalid("copy: compressed regions are whole blocks"));
551 }
552 if f.has_stencil() {
553 return Err(GpuError::Unsupported("copies of depth-stencil formats"));
554 }
555 let bx = w.div_ceil(bw) as u64;
556 let by = h.div_ceil(bw) as u64;
557 let row = bx * bb as u64;
558 let bpr = if l.bytes_per_row == 0 { row } else { l.bytes_per_row as u64 };
559 let rows = if l.rows_per_image == 0 { by } else { l.rows_per_image as u64 };
560 if bpr < row || bpr % bb as u64 != 0 || rows < by {
561 return Err(GpuError::Invalid("copy: bytes_per_row / rows_per_image too small or not whole texels"));
562 }
563 let align = if f.is_depth() { 4 } else { bb as u64 };
564 if l.offset % align != 0 {
565 return Err(GpuError::Invalid("copy: data offset not a multiple of the texel size"));
566 }
567 let slices = (dd * r.layers) as u64;
568 let span = bpr * rows * (slices - 1) + bpr * (by - 1) + row;
569 if l.offset.checked_add(span).is_none_or(|e| e > data_len) {
570 return Err(GpuError::Invalid("copy: data smaller than the region"));
571 }
572 let copy = vk::BufferImageCopy {
573 buffer_offset: l.offset,
574 buffer_row_length: (bpr / bb as u64 * bw as u64) as u32,
575 buffer_image_height: (rows * bw as u64) as u32,
576 image_subresource: vk::ImageSubresourceLayers {
577 aspect_mask: t.t.aspect_copy,
578 mip_level: r.mip,
579 base_array_layer: r.base_layer,
580 layer_count: r.layers,
581 },
582 image_offset: vk::Offset3D { x: x as i32, y: y as i32, z: z as i32 },
583 image_extent: vk::Extent3D { width: w, height: h, depth: dd },
584 };
585 Ok((copy, span))
586}
587
588pub(crate) struct SamplerInner {
591 sh: Arc<Shared>,
592 pub h: vk::Handle,
593}
594
595impl Drop for SamplerInner {
596 fn drop(&mut self) {
597 self.sh.defer(Garbage::Sampler(self.h));
598 }
599}
600
601#[derive(Clone)]
603pub struct Sampler(pub(crate) Arc<SamplerInner>);
604
605impl Device {
606 pub fn create_sampler(&self, desc: &SamplerDesc) -> Result<Sampler> {
607 let sh = &self.sh;
608 sh.check()?;
609 let filter = |f: Filter| if f == Filter::Linear { vk::FILTER_LINEAR } else { vk::FILTER_NEAREST };
610 let addr = |a: AddressMode| match a {
611 AddressMode::Repeat => vk::SAMPLER_ADDRESS_MODE_REPEAT,
612 AddressMode::MirrorRepeat => vk::SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
613 AddressMode::ClampToEdge => vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
614 };
615 let aniso = desc.anisotropy > 1;
616 if aniso && !sh.features.anisotropy {
617 return Err(GpuError::Unsupported("anisotropic filtering"));
618 }
619 if desc.lod_min < 0.0 || desc.lod_max < desc.lod_min {
620 return Err(GpuError::Invalid("sampler lod range"));
621 }
622 let si = vk::SamplerCreateInfo {
623 s_type: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
624 mag_filter: filter(desc.mag),
625 min_filter: filter(desc.min),
626 mipmap_mode: if desc.mip == Filter::Linear { vk::SAMPLER_MIPMAP_MODE_LINEAR } else { vk::SAMPLER_MIPMAP_MODE_NEAREST },
627 address_mode_u: addr(desc.address[0]),
628 address_mode_v: addr(desc.address[1]),
629 address_mode_w: addr(desc.address[2]),
630 anisotropy_enable: aniso as u32,
631 max_anisotropy: (desc.anisotropy as f32).min(sh.limits.max_anisotropy).max(1.0),
632 compare_enable: desc.compare.is_some() as u32,
633 compare_op: desc.compare.map_or(vk::COMPARE_OP_NEVER, compare_op),
634 min_lod: desc.lod_min,
635 max_lod: desc.lod_max,
636 border_color: vk::BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
637 ..vk::Zero::zero()
638 };
639 let mut h = 0;
640 sh.vk(unsafe { (sh.f.create_sampler)(sh.dev, &si, core::ptr::null(), &mut h) })?;
642 Ok(Sampler(Arc::new(SamplerInner { sh: sh.clone(), h })))
643 }
644}
645
646pub(crate) struct ShaderInner {
647 sh: Arc<Shared>,
648 pub h: vk::Handle,
649 words: Vec<u32>,
651 entries: Vec<shader::reflect::Entry>,
652 spec: Vec<shader::reflect::SpecConst>,
653 local_size_id: bool,
654}
655
656impl Drop for ShaderInner {
657 fn drop(&mut self) {
658 self.sh.defer(Garbage::Shader(self.h));
659 }
660}
661
662#[derive(Clone)]
664pub struct ShaderModule(pub(crate) Arc<ShaderInner>);
665
666impl ShaderModule {
667 pub fn from_spirv(device: &Device, code: &[u32]) -> Result<ShaderModule> {
670 let sh = &device.sh;
671 sh.check()?;
672 if code.len() < 5 || code[0] != 0x0723_0203 {
673 return Err(GpuError::Invalid("not SPIR-V (magic number)"));
674 }
675 let (maj, min) = (sh.info.api_version.0, sh.info.api_version.1);
676 let max = match (maj, min) {
677 (1, 1) => 0x0001_0300,
678 (1, 2) => 0x0001_0500,
679 _ => 0x0001_0600,
680 };
681 if code[1] > max || code[1] < 0x0001_0000 {
682 return Err(GpuError::Unsupported("this SPIR-V version"));
683 }
684 let entries = shader::reflect::entry_points(code).map_err(|_| GpuError::Invalid("SPIR-V instructions do not fit the words"))?;
685 let spec = shader::reflect::spec_constants(code).map_err(|_| GpuError::Invalid("SpecId on something else than a scalar specialization constant"))?;
686 if spec.iter().enumerate().any(|(i, a)| spec[..i].iter().any(|b| b.spec_id == a.spec_id)) {
687 return Err(GpuError::Invalid("a SpecId twice in the module"));
688 }
689 let ci = vk::ShaderModuleCreateInfo {
690 s_type: vk::STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
691 code_size: code.len() * 4,
692 p_code: code.as_ptr(),
693 ..vk::Zero::zero()
694 };
695 let mut h = 0;
696 sh.vk(unsafe { (sh.f.create_shader_module)(sh.dev, &ci, core::ptr::null(), &mut h) })?;
698 let local_size_id = shader::reflect::uses_local_size_id(code);
699 Ok(ShaderModule(Arc::new(ShaderInner { sh: sh.clone(), h, words: code.to_vec(), entries, spec, local_size_id })))
700 }
701
702 pub fn entry_points(&self) -> Vec<(u32, &str)> {
704 self.0.entries.iter().map(|e| (e.model, e.name.as_str())).collect()
705 }
706
707 pub fn constants(&self) -> Vec<(u32, shader::reflect::Kind, f64)> {
709 self.0.spec.iter().map(|c| (c.spec_id, c.kind, kind_value(c.kind, c.default))).collect()
710 }
711}
712
713fn kind_value(k: shader::reflect::Kind, v: u64) -> f64 {
715 use shader::reflect::Kind;
716 match k {
717 Kind::Bool => (v != 0) as u32 as f64,
718 Kind::Int { width, signed: true } => {
719 let s = 64 - width.min(64);
720 (((v << s) as i64) >> s) as f64
721 }
722 Kind::Int { .. } => v as f64,
723 Kind::Float { width: 16 } => shader::reflect::f16_to_f32(v as u16) as f64,
724 Kind::Float { width: 32 } => f32::from_bits(v as u32) as f64,
725 Kind::Float { .. } => f64::from_bits(v),
726 }
727}
728
729fn constant_bits(k: shader::reflect::Kind, x: f64) -> Result<u64> {
731 use shader::reflect::Kind;
732 let bad = GpuError::Invalid("pipeline constant: the value does not fit the override's type");
733 match k {
734 Kind::Bool => Ok((x != 0.0 && !x.is_nan()) as u64),
735 Kind::Int { width, signed } => {
736 if !x.is_finite() {
737 return Err(bad);
738 }
739 let t = x as i128; let w = width.clamp(1, 64);
741 let (lo, hi) = if signed { (-(1i128 << (w - 1)), (1i128 << (w - 1)) - 1) } else { (0, (1i128 << w) - 1) };
742 if t < lo || t > hi {
743 return Err(bad);
744 }
745 let m = if w >= 64 { u64::MAX } else { (1u64 << w) - 1 };
746 Ok(t as u64 & m)
747 }
748 Kind::Float { width } => {
749 if !x.is_finite() {
750 return Err(bad);
751 }
752 match width {
753 16 => {
754 let h = shader::reflect::f16_from_f32(x as f32);
755 if h & 0x7c00 == 0x7c00 || !(x as f32).is_finite() {
756 return Err(bad);
757 }
758 Ok(h as u64)
759 }
760 32 => {
761 let f = x as f32;
762 if !f.is_finite() {
763 return Err(bad);
764 }
765 Ok(f.to_bits() as u64)
766 }
767 _ => Ok(x.to_bits()),
768 }
769 }
770 }
771}
772
773fn specialize(m: &ShaderInner, constants: &[Constant]) -> Result<(Vec<vk::SpecializationMapEntry>, Vec<u8>, Vec<(u32, u64)>)> {
776 let mut map = Vec::with_capacity(constants.len());
777 let mut data = Vec::new();
778 let mut set = Vec::with_capacity(constants.len());
779 for (i, c) in constants.iter().enumerate() {
780 if constants[..i].iter().any(|d| d.id == c.id) {
781 return Err(GpuError::Invalid("pipeline constant: an id twice"));
782 }
783 let Some(decl) = m.spec.iter().find(|s| s.spec_id == c.id) else {
784 return Err(GpuError::Invalid("pipeline constant: the module has no override with this id"));
785 };
786 let bits = constant_bits(decl.kind, c.value)?;
787 let size = match decl.kind {
788 shader::reflect::Kind::Bool => 4, shader::reflect::Kind::Int { width, .. } | shader::reflect::Kind::Float { width } => (width as usize).div_ceil(8),
790 };
791 map.push(vk::SpecializationMapEntry { constant_id: c.id, offset: data.len() as u32, size });
792 data.extend_from_slice(&bits.to_le_bytes()[..size]);
793 while data.len() % 4 != 0 {
794 data.push(0);
795 }
796 set.push((c.id, bits));
797 }
798 Ok((map, data, set))
799}
800
801fn stage_info(sh: &Shared, st: &ShaderStage, model: shader::Model) -> Result<(Vec<vk::SpecializationMapEntry>, Vec<u8>, Vec<(u32, u64)>)> {
804 let m = &st.module.0;
805 if !m.entries.iter().any(|e| e.name == st.entry && e.model == model as u32) {
806 return Err(GpuError::Invalid("the module has no entry point of this name for the stage"));
807 }
808 if model == shader::Model::GLCompute && m.local_size_id && !sh.maintenance4 {
809 return Err(GpuError::Unsupported("LocalSizeId (maintenance4, Vulkan 1.3)"));
810 }
811 specialize(m, st.constants)
812}
813
814pub(crate) struct BglInner {
817 sh: Arc<Shared>,
818 pub h: vk::Handle,
819 pub bindings: Vec<Binding>, needs: Vec<vk::DescriptorPoolSize>,
821}
822
823impl Drop for BglInner {
824 fn drop(&mut self) {
825 self.sh.defer(Garbage::SetLayout(self.h));
826 }
827}
828
829#[derive(Clone)]
831pub struct BindGroupLayout(pub(crate) Arc<BglInner>);
832
833impl Device {
834 pub fn create_bind_group_layout(&self, bindings: &[Binding]) -> Result<BindGroupLayout> {
835 let sh = &self.sh;
836 sh.check()?;
837 let mut b: Vec<Binding> = bindings.to_vec();
838 b.sort_by_key(|x| x.binding);
839 if b.windows(2).any(|w| w[0].binding == w[1].binding) {
840 return Err(GpuError::Invalid("a binding number twice"));
841 }
842 for x in &b {
843 if stage_bits(x.stages) == 0 {
844 return Err(GpuError::Invalid("binding without stages"));
845 }
846 if x.kind == BindingKind::StorageBuffer && x.stages.intersects(Stages::VERTEX) && !sh.features.vertex_stores {
847 return Err(GpuError::Unsupported("writable storage buffers in vertex shaders"));
848 }
849 }
850 let vb: Vec<vk::DescriptorSetLayoutBinding> = b
851 .iter()
852 .map(|x| vk::DescriptorSetLayoutBinding {
853 binding: x.binding,
854 descriptor_type: descriptor_type(x.kind),
855 descriptor_count: 1,
856 stage_flags: stage_bits(x.stages),
857 p_immutable_samplers: core::ptr::null(),
858 })
859 .collect();
860 let ci = vk::DescriptorSetLayoutCreateInfo {
861 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
862 binding_count: vb.len() as u32,
863 p_bindings: vb.as_ptr(),
864 ..vk::Zero::zero()
865 };
866 let mut h = 0;
867 sh.vk(unsafe { (sh.f.create_descriptor_set_layout)(sh.dev, &ci, core::ptr::null(), &mut h) })?;
869 let needs = vb.iter().map(|x| vk::DescriptorPoolSize { type_: x.descriptor_type, descriptor_count: 1 }).collect();
870 Ok(BindGroupLayout(Arc::new(BglInner { sh: sh.clone(), h, bindings: b, needs })))
871 }
872}
873
874pub(crate) struct PlInner {
875 sh: Arc<Shared>,
876 pub h: vk::Handle,
877 pub sets: Vec<vk::Handle>,
878 pub push: u32,
879 _groups: Vec<BindGroupLayout>,
880}
881
882impl Drop for PlInner {
883 fn drop(&mut self) {
884 self.sh.defer(Garbage::PipeLayout(self.h));
885 }
886}
887
888#[derive(Clone)]
890pub struct PipelineLayout(pub(crate) Arc<PlInner>);
891
892impl Device {
893 pub fn create_pipeline_layout(&self, desc: &PipelineLayoutDesc) -> Result<PipelineLayout> {
894 let sh = &self.sh;
895 sh.check()?;
896 if desc.groups.len() > BIND_GROUPS_MAX as usize || desc.groups.len() > sh.raw.max_bound_descriptor_sets as usize {
897 return Err(GpuError::Invalid("too many bind groups"));
898 }
899 if desc.push_constants % 4 != 0 || desc.push_constants > PUSH_CONSTANTS_MAX {
900 return Err(GpuError::Invalid("push constants: a multiple of 4, at most PUSH_CONSTANTS_MAX"));
901 }
902 let sets: Vec<vk::Handle> = desc.groups.iter().map(|g| g.0.h).collect();
903 let range = vk::PushConstantRange { stage_flags: vk::SHADER_STAGE_ALL, offset: 0, size: desc.push_constants };
904 let ci = vk::PipelineLayoutCreateInfo {
905 s_type: vk::STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
906 set_layout_count: sets.len() as u32,
907 p_set_layouts: sets.as_ptr(),
908 push_constant_range_count: (desc.push_constants > 0) as u32,
909 p_push_constant_ranges: &range,
910 ..vk::Zero::zero()
911 };
912 let mut h = 0;
913 sh.vk(unsafe { (sh.f.create_pipeline_layout)(sh.dev, &ci, core::ptr::null(), &mut h) })?;
915 Ok(PipelineLayout(Arc::new(PlInner {
916 sh: sh.clone(),
917 h,
918 sets,
919 push: desc.push_constants,
920 _groups: desc.groups.iter().map(|g| (*g).clone()).collect(),
921 })))
922 }
923}
924
925#[derive(Clone)]
927#[allow(dead_code)]
928enum Keep {
929 Buf(Buffer),
930 View(TextureView),
931 Sampler(Sampler),
932}
933
934pub(crate) struct BgInner {
935 sh: Arc<Shared>,
936 pub set: vk::Handle,
937 pool: vk::Handle,
938 own_pool: bool,
939 pub layout: vk::Handle,
940 _layout: BindGroupLayout,
941 _keep: Vec<Keep>,
942}
943
944impl Drop for BgInner {
945 fn drop(&mut self) {
946 if self.own_pool {
947 self.sh.defer(Garbage::Pool(self.pool));
948 } else {
949 self.sh.defer(Garbage::Set(self.pool, self.set));
950 }
951 }
952}
953
954#[derive(Clone)]
956pub struct BindGroup(pub(crate) Arc<BgInner>);
957
958impl Device {
959 pub fn create_bind_group(&self, desc: &BindGroupDesc) -> Result<BindGroup> {
960 let sh = &self.sh;
961 sh.check()?;
962 let lay = &desc.layout.0;
963 if desc.entries.len() != lay.bindings.len() {
964 return Err(GpuError::Invalid("bind group: one entry per binding of the layout"));
965 }
966 let mut keep = Vec::with_capacity(desc.entries.len());
967 let mut bufs: Vec<vk::DescriptorBufferInfo> = Vec::with_capacity(desc.entries.len());
968 let mut imgs: Vec<vk::DescriptorImageInfo> = Vec::with_capacity(desc.entries.len());
969 let mut which: Vec<(u32, u32, bool, usize)> = Vec::with_capacity(desc.entries.len()); for e in desc.entries {
971 let Some(b) = lay.bindings.iter().find(|b| b.binding == e.binding) else {
972 return Err(GpuError::Invalid("bind group: entry for a binding the layout lacks"));
973 };
974 if which.iter().any(|w| w.0 == e.binding) {
975 return Err(GpuError::Invalid("bind group: a binding twice"));
976 }
977 let ty = descriptor_type(b.kind);
978 match (b.kind, e.resource) {
979 (BindingKind::UniformBuffer | BindingKind::StorageBuffer | BindingKind::StorageBufferRead,
980 Resource::Buffer { buffer, offset, size }) => {
981 let uniform = b.kind == BindingKind::UniformBuffer;
982 let (need, align, max) = if uniform {
983 (BufferUsage::UNIFORM, sh.limits.min_uniform_offset_alignment, sh.limits.max_uniform_binding)
984 } else {
985 (BufferUsage::STORAGE, sh.limits.min_storage_offset_alignment, sh.limits.max_storage_binding)
986 };
987 if !buffer.usage().contains(need) {
988 return Err(GpuError::Invalid("bind group: buffer lacks UNIFORM / STORAGE usage"));
989 }
990 let range = if size == 0 { buffer.size().saturating_sub(offset) } else { size };
991 if offset % align.max(1) as u64 != 0 || range == 0 || offset + range > buffer.size() || range > max as u64 {
992 return Err(GpuError::Invalid("bind group: buffer range (alignment, size, limit)"));
993 }
994 which.push((e.binding, ty, true, bufs.len()));
995 bufs.push(vk::DescriptorBufferInfo { buffer: buffer.handle(), offset, range });
996 keep.push(Keep::Buf(buffer.clone()));
997 }
998 (BindingKind::Texture, Resource::Texture(v)) | (BindingKind::StorageTexture, Resource::StorageTexture(v)) => {
999 let storage = b.kind == BindingKind::StorageTexture;
1000 let need = if storage { TextureUsage::STORAGE } else { TextureUsage::SAMPLED };
1001 if !v.usage().contains(need) {
1002 return Err(GpuError::Invalid("bind group: texture lacks SAMPLED / STORAGE usage"));
1003 }
1004 which.push((e.binding, ty, false, imgs.len()));
1005 imgs.push(vk::DescriptorImageInfo {
1006 sampler: 0,
1007 image_view: v.0.h,
1008 image_layout: if storage { vk::IMAGE_LAYOUT_GENERAL } else { vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
1009 });
1010 keep.push(Keep::View(v.clone()));
1011 }
1012 (BindingKind::Sampler, Resource::Sampler(s)) => {
1013 which.push((e.binding, ty, false, imgs.len()));
1014 imgs.push(vk::DescriptorImageInfo { sampler: s.0.h, image_view: 0, image_layout: 0 });
1015 keep.push(Keep::Sampler(s.clone()));
1016 }
1017 _ => return Err(GpuError::Invalid("bind group: resource does not match the binding's kind")),
1018 }
1019 }
1020 let (pool, set) = sh.alloc_set(lay.h, &lay.needs)?;
1021 let writes: Vec<vk::WriteDescriptorSet> = which
1022 .iter()
1023 .map(|&(binding, ty, is_buf, i)| vk::WriteDescriptorSet {
1024 s_type: vk::STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1025 dst_set: set,
1026 dst_binding: binding,
1027 descriptor_count: 1,
1028 descriptor_type: ty,
1029 p_image_info: if is_buf { core::ptr::null() } else { &imgs[i] },
1030 p_buffer_info: if is_buf { &bufs[i] } else { core::ptr::null() },
1031 ..vk::Zero::zero()
1032 })
1033 .collect();
1034 unsafe { (sh.f.update_descriptor_sets)(sh.dev, writes.len() as u32, writes.as_ptr(), 0, core::ptr::null()) };
1036 Ok(BindGroup(Arc::new(BgInner {
1037 sh: sh.clone(),
1038 set,
1039 pool,
1040 own_pool: false,
1041 layout: lay.h,
1042 _layout: desc.layout.clone(),
1043 _keep: keep,
1044 })))
1045 }
1046}
1047
1048struct Slots {
1052 keep: [Vec<Option<Keep>>; 4],
1053}
1054
1055pub struct Bindless {
1059 group: BindGroup,
1060 layout: BindGroupLayout,
1061 counts: [u32; 4],
1062 slots: SpinLock<Slots>,
1063}
1064
1065impl Device {
1066 pub fn create_bindless(&self, desc: &BindlessDesc) -> Result<Bindless> {
1067 let sh = &self.sh;
1068 sh.check()?;
1069 if !sh.features.bindless {
1070 return Err(GpuError::Unsupported("bindless tables (level Voll)"));
1071 }
1072 let l = &sh.limits;
1073 let counts = [desc.textures, desc.samplers, desc.buffers, desc.storage_textures];
1074 let maxes = [l.max_bindless_textures, l.max_bindless_samplers, l.max_bindless_buffers, l.max_bindless_storage_textures];
1075 if counts.iter().zip(maxes.iter()).any(|(c, m)| c > m) {
1076 return Err(GpuError::Invalid("bindless: more slots than Limits::max_bindless_*"));
1077 }
1078 let types = [vk::DESCRIPTOR_TYPE_SAMPLED_IMAGE, vk::DESCRIPTOR_TYPE_SAMPLER, vk::DESCRIPTOR_TYPE_STORAGE_BUFFER,
1079 vk::DESCRIPTOR_TYPE_STORAGE_IMAGE];
1080 let vb: Vec<vk::DescriptorSetLayoutBinding> = (0..4)
1081 .filter(|&i| counts[i] > 0)
1082 .map(|i| vk::DescriptorSetLayoutBinding {
1083 binding: i as u32,
1084 descriptor_type: types[i],
1085 descriptor_count: counts[i],
1086 stage_flags: vk::SHADER_STAGE_ALL,
1087 p_immutable_samplers: core::ptr::null(),
1088 })
1089 .collect();
1090 if vb.is_empty() {
1091 return Err(GpuError::Invalid("bindless table without slots"));
1092 }
1093 let flags = vec![vk::DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | vk::DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT
1094 | vk::DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; vb.len()];
1095 let fi = vk::DescriptorSetLayoutBindingFlagsCreateInfo {
1096 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
1097 binding_count: flags.len() as u32,
1098 p_binding_flags: flags.as_ptr(),
1099 ..vk::Zero::zero()
1100 };
1101 let ci = vk::DescriptorSetLayoutCreateInfo {
1102 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
1103 p_next: &fi as *const _ as *const c_void,
1104 flags: vk::DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
1105 binding_count: vb.len() as u32,
1106 p_bindings: vb.as_ptr(),
1107 };
1108 let mut lh = 0;
1109 sh.vk(unsafe { (sh.f.create_descriptor_set_layout)(sh.dev, &ci, core::ptr::null(), &mut lh) })?;
1111 let layout = BindGroupLayout(Arc::new(BglInner { sh: sh.clone(), h: lh, bindings: Vec::new(), needs: Vec::new() }));
1112 let sizes: Vec<vk::DescriptorPoolSize> =
1113 vb.iter().map(|b| vk::DescriptorPoolSize { type_: b.descriptor_type, descriptor_count: b.descriptor_count }).collect();
1114 let pi = vk::DescriptorPoolCreateInfo {
1115 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
1116 flags: vk::DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
1117 max_sets: 1,
1118 pool_size_count: sizes.len() as u32,
1119 p_pool_sizes: sizes.as_ptr(),
1120 ..vk::Zero::zero()
1121 };
1122 let mut pool = 0;
1123 sh.vk(unsafe { (sh.f.create_descriptor_pool)(sh.dev, &pi, core::ptr::null(), &mut pool) })?;
1125 let ai = vk::DescriptorSetAllocateInfo {
1126 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
1127 descriptor_pool: pool,
1128 descriptor_set_count: 1,
1129 p_set_layouts: &lh,
1130 ..vk::Zero::zero()
1131 };
1132 let mut set = 0;
1133 if let Err(e) = sh.vk(unsafe { (sh.f.allocate_descriptor_sets)(sh.dev, &ai, &mut set) }) {
1135 sh.defer(Garbage::Pool(pool));
1136 return Err(e);
1137 }
1138 let group = BindGroup(Arc::new(BgInner {
1139 sh: sh.clone(),
1140 set,
1141 pool,
1142 own_pool: true,
1143 layout: lh,
1144 _layout: layout.clone(),
1145 _keep: Vec::new(),
1146 }));
1147 let slots = Slots { keep: counts.map(|n| vec![None; n as usize]) };
1148 Ok(Bindless { group, layout, counts, slots: SpinLock::new(slots) })
1149 }
1150}
1151
1152impl Bindless {
1153 pub fn group(&self) -> &BindGroup {
1155 &self.group
1156 }
1157 pub fn layout(&self) -> &BindGroupLayout {
1159 &self.layout
1160 }
1161 pub fn counts(&self) -> [u32; 4] {
1163 self.counts
1164 }
1165
1166 fn write(&self, binding: usize, slot: u32, keep: Keep, img: Option<vk::DescriptorImageInfo>,
1167 buf: Option<vk::DescriptorBufferInfo>) -> Result<()> {
1168 let sh = &self.group.0.sh;
1169 sh.check()?;
1170 if slot >= self.counts[binding] {
1171 return Err(GpuError::Invalid("bindless: slot outside the table"));
1172 }
1173 let types = [vk::DESCRIPTOR_TYPE_SAMPLED_IMAGE, vk::DESCRIPTOR_TYPE_SAMPLER, vk::DESCRIPTOR_TYPE_STORAGE_BUFFER,
1174 vk::DESCRIPTOR_TYPE_STORAGE_IMAGE];
1175 let w = vk::WriteDescriptorSet {
1176 s_type: vk::STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1177 dst_set: self.group.0.set,
1178 dst_binding: binding as u32,
1179 dst_array_element: slot,
1180 descriptor_count: 1,
1181 descriptor_type: types[binding],
1182 p_image_info: img.as_ref().map_or(core::ptr::null(), |i| i as *const _),
1183 p_buffer_info: buf.as_ref().map_or(core::ptr::null(), |b| b as *const _),
1184 ..vk::Zero::zero()
1185 };
1186 let mut slots = self.slots.lock();
1187 unsafe { (sh.f.update_descriptor_sets)(sh.dev, 1, &w, 0, core::ptr::null()) };
1189 slots.keep[binding][slot as usize] = Some(keep); Ok(())
1191 }
1192
1193 pub fn set_texture(&self, slot: u32, view: &TextureView) -> Result<()> {
1195 if !view.usage().contains(TextureUsage::SAMPLED) {
1196 return Err(GpuError::Invalid("bindless: texture lacks SAMPLED usage"));
1197 }
1198 let i = vk::DescriptorImageInfo { sampler: 0, image_view: view.0.h, image_layout: vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
1199 self.write(0, slot, Keep::View(view.clone()), Some(i), None)
1200 }
1201
1202 pub fn set_sampler(&self, slot: u32, s: &Sampler) -> Result<()> {
1204 let i = vk::DescriptorImageInfo { sampler: s.0.h, image_view: 0, image_layout: 0 };
1205 self.write(1, slot, Keep::Sampler(s.clone()), Some(i), None)
1206 }
1207
1208 pub fn set_buffer(&self, slot: u32, buf: &Buffer, offset: u64, size: u64) -> Result<()> {
1210 let sh = &self.group.0.sh;
1211 let range = if size == 0 { buf.size().saturating_sub(offset) } else { size };
1212 let align = sh.limits.min_storage_offset_alignment.max(1) as u64;
1213 if !buf.usage().contains(BufferUsage::STORAGE) || offset % align != 0 || range == 0 || offset + range > buf.size() {
1214 return Err(GpuError::Invalid("bindless: buffer range (usage, alignment, size)"));
1215 }
1216 let b = vk::DescriptorBufferInfo { buffer: buf.handle(), offset, range };
1217 self.write(2, slot, Keep::Buf(buf.clone()), None, Some(b))
1218 }
1219
1220 pub fn set_storage_texture(&self, slot: u32, view: &TextureView) -> Result<()> {
1222 if !view.usage().contains(TextureUsage::STORAGE) {
1223 return Err(GpuError::Invalid("bindless: texture lacks STORAGE usage"));
1224 }
1225 let i = vk::DescriptorImageInfo { sampler: 0, image_view: view.0.h, image_layout: vk::IMAGE_LAYOUT_GENERAL };
1226 self.write(3, slot, Keep::View(view.clone()), Some(i), None)
1227 }
1228}
1229
1230pub(crate) struct PipeInner {
1233 sh: Arc<Shared>,
1234 pub h: vk::Handle,
1235 pub layout: PipelineLayout,
1236 pub colors: [u32; COLOR_TARGETS_MAX],
1237 pub ncolor: usize,
1238 pub depth: u32,
1239}
1240
1241impl Drop for PipeInner {
1242 fn drop(&mut self) {
1243 self.sh.defer(Garbage::Pipeline(self.h));
1244 }
1245}
1246
1247#[derive(Clone)]
1249pub struct ComputePipeline(pub(crate) Arc<PipeInner>);
1250
1251#[derive(Clone)]
1253pub struct RenderPipeline(pub(crate) Arc<PipeInner>);
1254
1255impl Device {
1256 pub fn create_compute_pipeline(&self, desc: &ComputePipelineDesc) -> Result<ComputePipeline> {
1257 self.compute_pipeline(desc, 0)
1258 }
1259
1260 pub fn create_compute_pipeline_sized(&self, desc: &ComputePipelineDesc, size: u32) -> Result<ComputePipeline> {
1264 if size != 0 {
1265 if !self.sh.features.subgroup_size_control {
1266 return Err(GpuError::Unsupported("subgroup size control"));
1267 }
1268 let lim = &self.sh.limits;
1269 if !size.is_power_of_two() || size < lim.subgroup_min_size || size > lim.subgroup_max_size {
1270 return Err(GpuError::Invalid("the required subgroup size is not a power of two within Limits::subgroup_min_size ..= subgroup_max_size"));
1271 }
1272 }
1273 self.compute_pipeline(desc, size)
1274 }
1275
1276 fn compute_pipeline(&self, desc: &ComputePipelineDesc, size: u32) -> Result<ComputePipeline> {
1277 let sh = &self.sh;
1278 sh.check()?;
1279 let (map, data, set) = stage_info(sh, &desc.shader, shader::Model::GLCompute)?;
1280 let wg = shader::reflect::workgroup_size(&desc.shader.module.0.words, desc.shader.entry, &set)
1282 .map_err(|_| GpuError::Invalid("pipeline constants: a specialization constant is undefined (division by 0, shift)"))?;
1283 let Some(wg) = wg else { return Err(GpuError::Invalid("compute entry point without a workgroup size")) };
1284 let lim = &sh.limits;
1285 let count = wg.iter().map(|&x| x as u64).product::<u64>();
1286 if wg.contains(&0) || (0..3).any(|i| wg[i] > lim.max_compute_workgroup_size[i]) || count > lim.max_compute_invocations as u64 {
1287 return Err(GpuError::Invalid("workgroup size 0 or beyond Limits::max_compute_workgroup_size / max_compute_invocations"));
1288 }
1289 let si = vk::SpecializationInfo {
1290 map_entry_count: map.len() as u32,
1291 p_map_entries: map.as_ptr(),
1292 data_size: data.len(),
1293 p_data: data.as_ptr() as *const c_void,
1294 };
1295 let name = nul_name(desc.shader.entry);
1296 if size != 0 && wg[0] % size != 0 {
1297 return Err(GpuError::Invalid("the workgroup size in x is not a multiple of the required subgroup size"));
1298 }
1299 let required = vk::PipelineShaderStageRequiredSubgroupSizeCreateInfo {
1300 s_type: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO,
1301 p_next: core::ptr::null_mut(),
1302 required_subgroup_size: size,
1303 };
1304 let ci = vk::ComputePipelineCreateInfo {
1305 s_type: vk::STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
1306 stage: vk::PipelineShaderStageCreateInfo {
1307 s_type: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1308 p_next: if size != 0 { &required as *const _ as *const c_void } else { core::ptr::null() },
1309 stage: vk::SHADER_STAGE_COMPUTE_BIT,
1310 module: desc.shader.module.0.h,
1311 p_name: name.as_ptr(),
1312 p_specialization_info: if map.is_empty() { core::ptr::null() } else { &si as *const _ as *const c_void },
1313 ..vk::Zero::zero()
1314 },
1315 layout: desc.layout.0.h,
1316 base_pipeline_index: -1,
1317 ..vk::Zero::zero()
1318 };
1319 let mut h = 0;
1320 sh.vk(unsafe { (sh.f.create_compute_pipelines)(sh.dev, 0, 1, &ci, core::ptr::null(), &mut h) })?;
1322 Ok(ComputePipeline(Arc::new(PipeInner {
1323 sh: sh.clone(),
1324 h,
1325 layout: desc.layout.clone(),
1326 colors: [0; COLOR_TARGETS_MAX],
1327 ncolor: 0,
1328 depth: 0,
1329 })))
1330 }
1331
1332 pub fn create_render_pipeline(&self, desc: &RenderPipelineDesc) -> Result<RenderPipeline> {
1333 let sh = &self.sh;
1334 sh.check()?;
1335 if desc.targets.len() > sh.limits.max_color_targets as usize {
1336 return Err(GpuError::Invalid("too many colour targets"));
1337 }
1338 if desc.targets.iter().any(|t| t.format.is_depth() || t.format.is_compressed()) {
1339 return Err(GpuError::Invalid("colour target with a depth or compressed format"));
1340 }
1341 if desc.depth.is_some_and(|d| !d.format.is_depth()) {
1342 return Err(GpuError::Invalid("depth state with a colour format"));
1343 }
1344 let (vmap, vdata, _) = stage_info(sh, &desc.vertex, shader::Model::Vertex)?;
1345 let (fmap, fdata, _) = match &desc.fragment {
1346 Some(fs) => stage_info(sh, fs, shader::Model::Fragment)?,
1347 None => (Vec::new(), Vec::new(), Vec::new()),
1348 };
1349 let spec = |map: &Vec<vk::SpecializationMapEntry>, data: &Vec<u8>| vk::SpecializationInfo {
1350 map_entry_count: map.len() as u32,
1351 p_map_entries: map.as_ptr(),
1352 data_size: data.len(),
1353 p_data: data.as_ptr() as *const c_void,
1354 };
1355 let (vsi, fsi) = (spec(&vmap, &vdata), spec(&fmap, &fdata));
1356 let ptr = |map: &Vec<vk::SpecializationMapEntry>, si: &vk::SpecializationInfo| {
1357 if map.is_empty() { core::ptr::null() } else { si as *const _ as *const c_void }
1358 };
1359 let vname = nul_name(desc.vertex.entry);
1360 let fname = nul_name(desc.fragment.map_or("", |f| f.entry));
1361 let mut stages = vec![vk::PipelineShaderStageCreateInfo {
1362 s_type: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1363 stage: vk::SHADER_STAGE_VERTEX_BIT,
1364 module: desc.vertex.module.0.h,
1365 p_name: vname.as_ptr(),
1366 p_specialization_info: ptr(&vmap, &vsi),
1367 ..vk::Zero::zero()
1368 }];
1369 if let Some(fs) = desc.fragment {
1370 stages.push(vk::PipelineShaderStageCreateInfo {
1371 s_type: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1372 stage: vk::SHADER_STAGE_FRAGMENT_BIT,
1373 module: fs.module.0.h,
1374 p_name: fname.as_ptr(),
1375 p_specialization_info: ptr(&fmap, &fsi),
1376 ..vk::Zero::zero()
1377 });
1378 }
1379 let mut vbind = Vec::new();
1380 let mut vattr = Vec::new();
1381 for (i, b) in desc.buffers.iter().enumerate() {
1382 vbind.push(vk::VertexInputBindingDescription {
1383 binding: i as u32,
1384 stride: b.stride,
1385 input_rate: if b.per_instance { vk::VERTEX_INPUT_RATE_INSTANCE } else { vk::VERTEX_INPUT_RATE_VERTEX },
1386 });
1387 for a in b.attributes {
1388 vattr.push(vk::VertexInputAttributeDescription {
1389 location: a.location,
1390 binding: i as u32,
1391 format: match a.format {
1392 VertexFormat::F32 => vk::FORMAT_R32_SFLOAT,
1393 VertexFormat::F32x2 => vk::FORMAT_R32G32_SFLOAT,
1394 VertexFormat::F32x3 => vk::FORMAT_R32G32B32_SFLOAT,
1395 VertexFormat::F32x4 => vk::FORMAT_R32G32B32A32_SFLOAT,
1396 VertexFormat::U32 => vk::FORMAT_R32_UINT,
1397 VertexFormat::U32x2 => vk::FORMAT_R32G32_UINT,
1398 VertexFormat::U32x4 => vk::FORMAT_R32G32B32A32_UINT,
1399 VertexFormat::Unorm8x4 => vk::FORMAT_R8G8B8A8_UNORM,
1400 },
1401 offset: a.offset,
1402 });
1403 }
1404 }
1405 if vbind.len() > sh.limits.max_vertex_buffers as usize || vattr.len() > sh.limits.max_vertex_attributes as usize {
1406 return Err(GpuError::Invalid("too many vertex buffers or attributes"));
1407 }
1408 let vi = vk::PipelineVertexInputStateCreateInfo {
1409 s_type: vk::STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
1410 vertex_binding_description_count: vbind.len() as u32,
1411 p_vertex_binding_descriptions: vbind.as_ptr(),
1412 vertex_attribute_description_count: vattr.len() as u32,
1413 p_vertex_attribute_descriptions: vattr.as_ptr(),
1414 ..vk::Zero::zero()
1415 };
1416 let ia = vk::PipelineInputAssemblyStateCreateInfo {
1417 s_type: vk::STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
1418 topology: match desc.topology {
1419 Topology::Points => vk::PRIMITIVE_TOPOLOGY_POINT_LIST,
1420 Topology::Lines => vk::PRIMITIVE_TOPOLOGY_LINE_LIST,
1421 Topology::LineStrip => vk::PRIMITIVE_TOPOLOGY_LINE_STRIP,
1422 Topology::Triangles => vk::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
1423 Topology::TriangleStrip => vk::PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
1424 },
1425 ..vk::Zero::zero()
1426 };
1427 let vp = vk::PipelineViewportStateCreateInfo {
1428 s_type: vk::STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
1429 viewport_count: 1,
1430 scissor_count: 1,
1431 ..vk::Zero::zero()
1432 };
1433 let rs = vk::PipelineRasterizationStateCreateInfo {
1434 s_type: vk::STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
1435 polygon_mode: vk::POLYGON_MODE_FILL,
1436 cull_mode: match desc.cull {
1437 Cull::None => vk::CULL_MODE_NONE,
1438 Cull::Front => vk::CULL_MODE_FRONT_BIT,
1439 Cull::Back => vk::CULL_MODE_BACK_BIT,
1440 },
1441 front_face: if desc.front_cw { vk::FRONT_FACE_CLOCKWISE } else { vk::FRONT_FACE_COUNTER_CLOCKWISE },
1442 line_width: 1.0,
1443 ..vk::Zero::zero()
1444 };
1445 let ms = vk::PipelineMultisampleStateCreateInfo {
1446 s_type: vk::STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
1447 rasterization_samples: vk::SAMPLE_COUNT_1_BIT,
1448 min_sample_shading: 1.0,
1449 ..vk::Zero::zero()
1450 };
1451 let ds = vk::PipelineDepthStencilStateCreateInfo {
1452 s_type: vk::STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
1453 depth_test_enable: desc.depth.is_some() as u32,
1454 depth_write_enable: desc.depth.is_some_and(|d| d.write) as u32,
1455 depth_compare_op: desc.depth.map_or(vk::COMPARE_OP_ALWAYS, |d| compare_op(d.compare)),
1456 max_depth_bounds: 1.0,
1457 ..vk::Zero::zero()
1458 };
1459 let factor = |f: BlendFactor| match f {
1460 BlendFactor::Zero => vk::BLEND_FACTOR_ZERO,
1461 BlendFactor::One => vk::BLEND_FACTOR_ONE,
1462 BlendFactor::Src => vk::BLEND_FACTOR_SRC_COLOR,
1463 BlendFactor::OneMinusSrc => vk::BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
1464 BlendFactor::Dst => vk::BLEND_FACTOR_DST_COLOR,
1465 BlendFactor::OneMinusDst => vk::BLEND_FACTOR_ONE_MINUS_DST_COLOR,
1466 BlendFactor::SrcAlpha => vk::BLEND_FACTOR_SRC_ALPHA,
1467 BlendFactor::OneMinusSrcAlpha => vk::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
1468 BlendFactor::DstAlpha => vk::BLEND_FACTOR_DST_ALPHA,
1469 BlendFactor::OneMinusDstAlpha => vk::BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
1470 };
1471 let op = |o: BlendOp| match o {
1472 BlendOp::Add => vk::BLEND_OP_ADD,
1473 BlendOp::Subtract => vk::BLEND_OP_SUBTRACT,
1474 BlendOp::ReverseSubtract => vk::BLEND_OP_REVERSE_SUBTRACT,
1475 BlendOp::Min => vk::BLEND_OP_MIN,
1476 BlendOp::Max => vk::BLEND_OP_MAX,
1477 };
1478 let att: Vec<vk::PipelineColorBlendAttachmentState> = desc
1479 .targets
1480 .iter()
1481 .map(|t| {
1482 let b = t.blend.unwrap_or(Blend::ADD);
1483 vk::PipelineColorBlendAttachmentState {
1484 blend_enable: t.blend.is_some() as u32,
1485 src_color_blend_factor: factor(b.color.src),
1486 dst_color_blend_factor: factor(b.color.dst),
1487 color_blend_op: op(b.color.op),
1488 src_alpha_blend_factor: factor(b.alpha.src),
1489 dst_alpha_blend_factor: factor(b.alpha.dst),
1490 alpha_blend_op: op(b.alpha.op),
1491 color_write_mask: t.write_mask & 0xF,
1492 }
1493 })
1494 .collect();
1495 let cb = vk::PipelineColorBlendStateCreateInfo {
1496 s_type: vk::STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
1497 attachment_count: att.len() as u32,
1498 p_attachments: att.as_ptr(),
1499 ..vk::Zero::zero()
1500 };
1501 let dyns = [vk::DYNAMIC_STATE_VIEWPORT, vk::DYNAMIC_STATE_SCISSOR];
1502 let dy = vk::PipelineDynamicStateCreateInfo {
1503 s_type: vk::STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
1504 dynamic_state_count: 2,
1505 p_dynamic_states: dyns.as_ptr(),
1506 ..vk::Zero::zero()
1507 };
1508 let mut colors = [0u32; COLOR_TARGETS_MAX];
1509 for (i, t) in desc.targets.iter().enumerate() {
1510 colors[i] = vk_format(t.format);
1511 }
1512 let depth = desc.depth.map_or(vk::FORMAT_UNDEFINED, |d| vk_format(d.format));
1513 let stencil = desc.depth.filter(|d| d.format.has_stencil()).map_or(vk::FORMAT_UNDEFINED, |d| vk_format(d.format));
1514 let ri = vk::PipelineRenderingCreateInfo {
1515 s_type: vk::STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
1516 color_attachment_count: desc.targets.len() as u32,
1517 p_color_attachment_formats: colors.as_ptr(),
1518 depth_attachment_format: depth,
1519 stencil_attachment_format: stencil,
1520 ..vk::Zero::zero()
1521 };
1522 let gi = vk::GraphicsPipelineCreateInfo {
1523 s_type: vk::STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
1524 p_next: &ri as *const _ as *const c_void,
1525 stage_count: stages.len() as u32,
1526 p_stages: stages.as_ptr(),
1527 p_vertex_input_state: &vi,
1528 p_input_assembly_state: &ia,
1529 p_viewport_state: &vp,
1530 p_rasterization_state: &rs,
1531 p_multisample_state: &ms,
1532 p_depth_stencil_state: &ds,
1533 p_color_blend_state: &cb,
1534 p_dynamic_state: &dy,
1535 layout: desc.layout.0.h,
1536 base_pipeline_index: -1,
1537 ..vk::Zero::zero()
1538 };
1539 let mut h = 0;
1540 sh.vk(unsafe { (sh.f.create_graphics_pipelines)(sh.dev, 0, 1, &gi, core::ptr::null(), &mut h) })?;
1542 Ok(RenderPipeline(Arc::new(PipeInner {
1543 sh: sh.clone(),
1544 h,
1545 layout: desc.layout.clone(),
1546 colors,
1547 ncolor: desc.targets.len(),
1548 depth,
1549 })))
1550 }
1551}
1552
1553pub(crate) struct TsInner {
1556 sh: Arc<Shared>,
1557 pub pool: vk::Handle,
1558 pub count: u32,
1559}
1560
1561impl Drop for TsInner {
1562 fn drop(&mut self) {
1563 self.sh.defer(Garbage::Query(self.pool));
1564 }
1565}
1566
1567#[derive(Clone)]
1569pub struct Timestamps(pub(crate) Arc<TsInner>);
1570
1571impl Device {
1572 pub fn create_timestamps(&self, count: u32) -> Result<Timestamps> {
1573 let sh = &self.sh;
1574 sh.check()?;
1575 if !sh.features.timestamps {
1576 return Err(GpuError::Unsupported("timestamps"));
1577 }
1578 if count == 0 {
1579 return Err(GpuError::Invalid("0 timestamps"));
1580 }
1581 let ci = vk::QueryPoolCreateInfo {
1582 s_type: vk::STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
1583 query_type: vk::QUERY_TYPE_TIMESTAMP,
1584 query_count: count,
1585 ..vk::Zero::zero()
1586 };
1587 let mut pool = 0;
1588 sh.vk(unsafe { (sh.f.create_query_pool)(sh.dev, &ci, core::ptr::null(), &mut pool) })?;
1590 let ts = Timestamps(Arc::new(TsInner { sh: sh.clone(), pool, count }));
1591 match sh.f.reset_query_pool.filter(|_| sh.host_query_reset) {
1594 Some(reset) => unsafe { reset(sh.dev, pool, 0, count) },
1596 None => {
1597 let mut st = sh.st.lock();
1598 let cb = sh.upload_cb(&mut st)?;
1599 unsafe { (sh.f.cmd_reset_query_pool)(cb, pool, 0, count) };
1601 }
1602 }
1603 Ok(ts)
1604 }
1605}
1606
1607impl Timestamps {
1608 pub fn count(&self) -> u32 {
1609 self.0.count
1610 }
1611
1612 pub fn read_ticks(&self, first: u32, n: u32) -> Result<Vec<u64>> {
1614 let t = &self.0;
1615 t.sh.check()?;
1616 if n == 0 || first.checked_add(n).is_none_or(|e| e > t.count) {
1617 return Err(GpuError::Invalid("timestamps outside the set"));
1618 }
1619 let mut raw = vec![[0u64; 2]; n as usize];
1620 let r = unsafe {
1622 (t.sh.f.get_query_pool_results)(t.sh.dev, t.pool, first, n, raw.len() * 16, raw.as_mut_ptr() as *mut c_void, 16,
1623 vk::QUERY_RESULT_64_BIT | vk::QUERY_RESULT_WITH_AVAILABILITY_BIT)
1624 };
1625 if r == vk::NOT_READY {
1626 return Err(GpuError::NotReady);
1627 }
1628 t.sh.vk(r)?;
1629 if raw.iter().any(|p| p[1] == 0) {
1630 return Err(GpuError::NotReady);
1631 }
1632 Ok(raw.iter().map(|p| p[0] & t.sh.ts_mask).collect())
1633 }
1634
1635 pub fn read_ms(&self, first: u32, n: u32) -> Result<Vec<f64>> {
1638 let ticks = self.read_ticks(first, n)?;
1639 let sh = &self.0.sh;
1640 let low = ticks.iter().copied().min().unwrap_or(0).max(1);
1641 let base = match sh.ts_base.compare_exchange(0, low, core::sync::atomic::Ordering::AcqRel,
1642 core::sync::atomic::Ordering::Acquire) {
1643 Ok(_) => low,
1644 Err(b) => b,
1645 };
1646 let period = sh.limits.timestamp_period_ns as f64;
1647 Ok(ticks.into_iter().map(|t| (t as i128 - base as i128) as f64 * period / 1.0e6).collect())
1648 }
1649}