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 fn usage(&self) -> TextureUsage {
311 self.0.tex.desc.usage
312 }
313 pub fn same(&self, other: &TextureView) -> bool {
315 Arc::ptr_eq(&self.0, &other.0)
316 }
317}
318
319#[derive(Clone)]
321pub struct Texture {
322 pub(crate) t: Arc<TexInner>,
323 view: TextureView,
324}
325
326impl Texture {
327 pub fn view(&self) -> &TextureView {
329 &self.view
330 }
331 pub fn width(&self) -> u32 {
332 self.t.desc.width
333 }
334 pub fn height(&self) -> u32 {
335 self.t.desc.height
336 }
337 pub fn depth(&self) -> u32 {
338 self.t.desc.depth
339 }
340 pub fn layers(&self) -> u32 {
341 self.t.desc.layers
342 }
343 pub fn mips(&self) -> u32 {
344 self.t.desc.mips
345 }
346 pub fn format(&self) -> Format {
347 self.t.desc.format
348 }
349 pub fn usage(&self) -> TextureUsage {
350 self.t.desc.usage
351 }
352 pub(crate) fn image(&self) -> vk::Handle {
353 self.t.image
354 }
355
356 pub fn create_view(&self, desc: &ViewDesc) -> Result<TextureView> {
358 let d = &self.t.desc;
359 let mips = if desc.mips == 0 { d.mips.saturating_sub(desc.base_mip) } else { desc.mips };
360 let layers = if desc.layers == 0 { d.layers.saturating_sub(desc.base_layer) } else { desc.layers };
361 if mips == 0 || desc.base_mip.checked_add(mips).is_none_or(|e| e > d.mips) || layers == 0
362 || desc.base_layer.checked_add(layers).is_none_or(|e| e > d.layers) {
363 return Err(GpuError::Invalid("view range outside the texture"));
364 }
365 let ok = match desc.dim {
366 ViewDim::D2 => d.depth == 1 && layers == 1,
367 ViewDim::D2Array => d.depth == 1,
368 ViewDim::Cube => d.cube && layers == 6,
369 ViewDim::CubeArray => d.cube && layers % 6 == 0,
370 ViewDim::D3 => d.depth > 1,
371 };
372 if !ok {
373 return Err(GpuError::Invalid("view dimension does not fit the texture"));
374 }
375 make_view(&self.t, desc.dim, desc.base_mip, mips, desc.base_layer, layers)
376 }
377}
378
379fn make_view(t: &Arc<TexInner>, dim: ViewDim, base_mip: u32, mips: u32, base_layer: u32, layers: u32) -> Result<TextureView> {
380 let sh = &t.sh;
381 let vi = vk::ImageViewCreateInfo {
382 s_type: vk::STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
383 image: t.image,
384 view_type: match dim {
385 ViewDim::D2 => vk::IMAGE_VIEW_TYPE_2D,
386 ViewDim::D2Array => vk::IMAGE_VIEW_TYPE_2D_ARRAY,
387 ViewDim::Cube => vk::IMAGE_VIEW_TYPE_CUBE,
388 ViewDim::CubeArray => vk::IMAGE_VIEW_TYPE_CUBE_ARRAY,
389 ViewDim::D3 => vk::IMAGE_VIEW_TYPE_3D,
390 },
391 format: vk_format(t.desc.format),
392 subresource_range: vk::ImageSubresourceRange {
393 aspect_mask: t.aspect_copy,
394 base_mip_level: base_mip,
395 level_count: mips,
396 base_array_layer: base_layer,
397 layer_count: layers,
398 },
399 ..vk::Zero::zero()
400 };
401 let mut h = 0;
402 sh.vk(unsafe { (sh.f.create_image_view)(sh.dev, &vi, core::ptr::null(), &mut h) })?;
404 Ok(TextureView(Arc::new(ViewInner { sh: sh.clone(), h, tex: t.clone(), base_mip, mips, base_layer, layers })))
405}
406
407fn default_dim(d: &TextureDesc) -> ViewDim {
409 if d.depth > 1 {
410 ViewDim::D3
411 } else if d.cube {
412 if d.layers == 6 { ViewDim::Cube } else { ViewDim::CubeArray }
413 } else if d.layers > 1 {
414 ViewDim::D2Array
415 } else {
416 ViewDim::D2
417 }
418}
419
420pub(crate) fn wrap_image(sh: &Arc<Shared>, image: vk::Handle, desc: TextureDesc) -> Result<Texture> {
422 let (aspect_all, aspect_copy) = aspects(desc.format);
423 let t = Arc::new(TexInner { sh: sh.clone(), image, a: None, owned: false, desc, aspect_all, aspect_copy });
424 let view = make_view(&t, ViewDim::D2, 0, 1, 0, 1)?;
425 Ok(Texture { t, view })
426}
427
428impl Device {
429 pub fn create_texture(&self, desc: &TextureDesc) -> Result<Texture> {
430 let sh = &self.sh;
431 sh.check()?;
432 let d = *desc;
433 let lim = &sh.limits;
434 let max_dim = if d.depth > 1 { lim.max_texture_3d } else { lim.max_texture_2d };
435 if d.width == 0 || d.height == 0 || d.depth == 0 || d.layers == 0 || d.mips == 0 {
436 return Err(GpuError::Invalid("texture size, layers or mips 0"));
437 }
438 if d.width > max_dim || d.height > max_dim || d.depth > max_dim || d.layers > lim.max_texture_layers {
439 return Err(GpuError::Invalid("texture larger than the device's limits"));
440 }
441 let longest = d.width.max(d.height).max(d.depth);
442 if d.mips > 32 - longest.leading_zeros() {
443 return Err(GpuError::Invalid("more mips than the size has"));
444 }
445 if d.depth > 1 && (d.layers > 1 || d.cube || d.format.is_depth()) {
446 return Err(GpuError::Invalid("3D textures have one layer and a colour format"));
447 }
448 if d.cube && (d.layers % 6 != 0 || d.width != d.height) {
449 return Err(GpuError::Invalid("cube textures are square with 6 * n layers"));
450 }
451 if d.usage == TextureUsage::NONE {
452 return Err(GpuError::Invalid("texture without usage"));
453 }
454 if d.format.is_compressed() && !sh.features.texture_bc {
455 return Err(GpuError::Unsupported("BC compressed textures"));
456 }
457 let fmt = vk_format(d.format);
458 let mut fp: vk::FormatProperties = vk::Zero::zero();
459 unsafe { (sh.inst.f.get_physical_device_format_properties)(sh.pd, fmt, &mut fp) };
461 let of = fp.optimal_tiling_features;
462 let mut usage = 0;
463 for (u, v, need) in [
464 (TextureUsage::COPY_SRC, vk::IMAGE_USAGE_TRANSFER_SRC_BIT, 0),
465 (TextureUsage::COPY_DST, vk::IMAGE_USAGE_TRANSFER_DST_BIT, 0),
466 (TextureUsage::SAMPLED, vk::IMAGE_USAGE_SAMPLED_BIT, vk::FORMAT_FEATURE_SAMPLED_IMAGE_BIT),
467 (TextureUsage::STORAGE, vk::IMAGE_USAGE_STORAGE_BIT, vk::FORMAT_FEATURE_STORAGE_IMAGE_BIT),
468 (TextureUsage::COLOR_TARGET, vk::IMAGE_USAGE_COLOR_ATTACHMENT_BIT, vk::FORMAT_FEATURE_COLOR_ATTACHMENT_BIT),
469 (TextureUsage::DEPTH_TARGET, vk::IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
470 vk::FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT),
471 ] {
472 if d.usage.contains(u) {
473 if of & need != need {
474 return Err(GpuError::Unsupported("format with this usage"));
475 }
476 usage |= v;
477 }
478 }
479 if d.usage.contains(TextureUsage::COLOR_TARGET) && d.format.is_depth()
480 || d.usage.contains(TextureUsage::DEPTH_TARGET) && !d.format.is_depth()
481 {
482 return Err(GpuError::Invalid("colour targets need colour formats, depth targets depth formats"));
483 }
484 let fams = sh.sharing().filter(|_| !d.usage.intersects(TextureUsage::COLOR_TARGET | TextureUsage::DEPTH_TARGET));
486 let ii = vk::ImageCreateInfo {
487 s_type: vk::STRUCTURE_TYPE_IMAGE_CREATE_INFO,
488 queue_family_index_count: if fams.is_some() { 2 } else { 0 },
489 p_queue_family_indices: fams.as_ref().map_or(core::ptr::null(), |f| f.as_ptr()),
490 flags: if d.cube { vk::IMAGE_CREATE_CUBE_COMPATIBLE_BIT } else { 0 },
491 image_type: if d.depth > 1 { vk::IMAGE_TYPE_3D } else { vk::IMAGE_TYPE_2D },
492 format: fmt,
493 extent: vk::Extent3D { width: d.width, height: d.height, depth: d.depth },
494 mip_levels: d.mips,
495 array_layers: d.layers,
496 samples: vk::SAMPLE_COUNT_1_BIT,
497 tiling: vk::IMAGE_TILING_OPTIMAL,
498 usage,
499 sharing_mode: if fams.is_some() { vk::SHARING_MODE_CONCURRENT } else { vk::SHARING_MODE_EXCLUSIVE },
500 initial_layout: vk::IMAGE_LAYOUT_UNDEFINED,
501 ..vk::Zero::zero()
502 };
503 let mut image = 0;
504 sh.vk(unsafe { (sh.f.create_image)(sh.dev, &ii, core::ptr::null(), &mut image) })?;
506 let mut req: vk::MemoryRequirements = vk::Zero::zero();
507 unsafe { (sh.f.get_image_memory_requirements)(sh.dev, image, &mut req) };
509 let mut st = sh.st.lock();
510 let a = match st.mem.alloc(&sh.f, sh.dev, &req, MemoryKind::Device, false) {
511 Ok(a) => a,
512 Err(e) => {
513 unsafe { (sh.f.destroy_image)(sh.dev, image, core::ptr::null()) };
515 return Err(e);
516 }
517 };
518 drop(st);
519 if let Err(e) = sh.vk(unsafe { (sh.f.bind_image_memory)(sh.dev, image, a.mem, a.offset) }) {
521 sh.defer(Garbage::Image(image, Some(a)));
522 return Err(e);
523 }
524 let (aspect_all, aspect_copy) = aspects(d.format);
525 let t = Arc::new(TexInner { sh: sh.clone(), image, a: Some(a), owned: true, desc: d, aspect_all, aspect_copy });
526 let view = make_view(&t, default_dim(&d), 0, d.mips, 0, d.layers)?;
527 Ok(Texture { t, view })
528 }
529}
530
531pub(crate) fn subresources(t: &Texture, base_mip: u32, mips: u32, base_layer: u32, layers: u32) -> vk::ImageSubresourceRange {
533 vk::ImageSubresourceRange {
534 aspect_mask: t.t.aspect_all,
535 base_mip_level: base_mip,
536 level_count: mips,
537 base_array_layer: base_layer,
538 layer_count: layers,
539 }
540}
541
542pub(crate) fn copy_region(r: &TextureRegion, l: &DataLayout, data_len: u64) -> Result<(vk::BufferImageCopy, u64)> {
546 let t = r.texture;
547 let d = &t.t.desc;
548 if r.mip >= d.mips || r.layers == 0 || r.base_layer.checked_add(r.layers).is_none_or(|e| e > d.layers) {
549 return Err(GpuError::Invalid("copy: mip or layers outside the texture"));
550 }
551 let (mw, mh, md) = ((d.width >> r.mip).max(1), (d.height >> r.mip).max(1), (d.depth >> r.mip).max(1));
552 let [x, y, z] = r.origin;
553 let [w, h, dd] = r.size;
554 let past = |a: u32, n: u32, m: u32| a.checked_add(n).is_none_or(|e| e > m);
555 if w == 0 || h == 0 || dd == 0 || past(x, w, mw) || past(y, h, mh) || past(z, dd, md) {
556 return Err(GpuError::Invalid("copy: region outside the mip"));
557 }
558 let f = d.format;
559 let (bw, bb) = (f.block_dim(), f.block_bytes());
560 if bw > 1 && (x % bw != 0 || y % bw != 0 || (w % bw != 0 && x + w != mw) || (h % bw != 0 && y + h != mh)) {
561 return Err(GpuError::Invalid("copy: compressed regions are whole blocks"));
562 }
563 if f.has_stencil() {
564 return Err(GpuError::Unsupported("copies of depth-stencil formats"));
565 }
566 let bx = w.div_ceil(bw) as u64;
567 let by = h.div_ceil(bw) as u64;
568 let row = bx * bb as u64;
569 let bpr = if l.bytes_per_row == 0 { row } else { l.bytes_per_row as u64 };
570 let rows = if l.rows_per_image == 0 { by } else { l.rows_per_image as u64 };
571 if bpr < row || bpr % bb as u64 != 0 || rows < by {
572 return Err(GpuError::Invalid("copy: bytes_per_row / rows_per_image too small or not whole texels"));
573 }
574 let align = if f.is_depth() { 4 } else { bb as u64 };
575 if l.offset % align != 0 {
576 return Err(GpuError::Invalid("copy: data offset not a multiple of the texel size"));
577 }
578 let slices = dd as u64 * r.layers as u64;
579 let span = bpr.checked_mul(rows).and_then(|s| s.checked_mul(slices - 1))
581 .and_then(|s| s.checked_add(bpr * (by - 1) + row))
582 .filter(|&s| l.offset.checked_add(s).is_some_and(|e| e <= data_len));
583 let Some(span) = span else {
584 return Err(GpuError::Invalid("copy: data smaller than the region"));
585 };
586 let copy = vk::BufferImageCopy {
587 buffer_offset: l.offset,
588 buffer_row_length: (bpr / bb as u64 * bw as u64) as u32,
589 buffer_image_height: (rows * bw as u64) as u32,
590 image_subresource: vk::ImageSubresourceLayers {
591 aspect_mask: t.t.aspect_copy,
592 mip_level: r.mip,
593 base_array_layer: r.base_layer,
594 layer_count: r.layers,
595 },
596 image_offset: vk::Offset3D { x: x as i32, y: y as i32, z: z as i32 },
597 image_extent: vk::Extent3D { width: w, height: h, depth: dd },
598 };
599 Ok((copy, span))
600}
601
602pub(crate) struct SamplerInner {
605 sh: Arc<Shared>,
606 pub h: vk::Handle,
607}
608
609impl Drop for SamplerInner {
610 fn drop(&mut self) {
611 self.sh.defer(Garbage::Sampler(self.h));
612 }
613}
614
615#[derive(Clone)]
617pub struct Sampler(pub(crate) Arc<SamplerInner>);
618
619impl Device {
620 pub fn create_sampler(&self, desc: &SamplerDesc) -> Result<Sampler> {
621 let sh = &self.sh;
622 sh.check()?;
623 let filter = |f: Filter| if f == Filter::Linear { vk::FILTER_LINEAR } else { vk::FILTER_NEAREST };
624 let addr = |a: AddressMode| match a {
625 AddressMode::Repeat => vk::SAMPLER_ADDRESS_MODE_REPEAT,
626 AddressMode::MirrorRepeat => vk::SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
627 AddressMode::ClampToEdge => vk::SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
628 };
629 let aniso = desc.anisotropy > 1;
630 if aniso && !sh.features.anisotropy {
631 return Err(GpuError::Unsupported("anisotropic filtering"));
632 }
633 if desc.lod_min < 0.0 || desc.lod_max < desc.lod_min {
634 return Err(GpuError::Invalid("sampler lod range"));
635 }
636 let si = vk::SamplerCreateInfo {
637 s_type: vk::STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
638 mag_filter: filter(desc.mag),
639 min_filter: filter(desc.min),
640 mipmap_mode: if desc.mip == Filter::Linear { vk::SAMPLER_MIPMAP_MODE_LINEAR } else { vk::SAMPLER_MIPMAP_MODE_NEAREST },
641 address_mode_u: addr(desc.address[0]),
642 address_mode_v: addr(desc.address[1]),
643 address_mode_w: addr(desc.address[2]),
644 anisotropy_enable: aniso as u32,
645 max_anisotropy: (desc.anisotropy as f32).min(sh.limits.max_anisotropy).max(1.0),
646 compare_enable: desc.compare.is_some() as u32,
647 compare_op: desc.compare.map_or(vk::COMPARE_OP_NEVER, compare_op),
648 min_lod: desc.lod_min,
649 max_lod: desc.lod_max,
650 border_color: vk::BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,
651 ..vk::Zero::zero()
652 };
653 let mut h = 0;
654 sh.vk(unsafe { (sh.f.create_sampler)(sh.dev, &si, core::ptr::null(), &mut h) })?;
656 Ok(Sampler(Arc::new(SamplerInner { sh: sh.clone(), h })))
657 }
658}
659
660pub(crate) struct ShaderInner {
661 sh: Arc<Shared>,
662 pub h: vk::Handle,
663 words: Vec<u32>,
665 entries: Vec<shader::reflect::Entry>,
666 spec: Vec<shader::reflect::SpecConst>,
667 local_size_id: bool,
668}
669
670impl Drop for ShaderInner {
671 fn drop(&mut self) {
672 self.sh.defer(Garbage::Shader(self.h));
673 }
674}
675
676#[derive(Clone)]
678pub struct ShaderModule(pub(crate) Arc<ShaderInner>);
679
680impl ShaderModule {
681 pub fn from_spirv(device: &Device, code: &[u32]) -> Result<ShaderModule> {
684 let sh = &device.sh;
685 sh.check()?;
686 if code.len() < 5 || code[0] != 0x0723_0203 {
687 return Err(GpuError::Invalid("not SPIR-V (magic number)"));
688 }
689 let (maj, min) = (sh.info.api_version.0, sh.info.api_version.1);
690 let max = match (maj, min) {
691 (1, 1) => 0x0001_0300,
692 (1, 2) => 0x0001_0500,
693 _ => 0x0001_0600,
694 };
695 if code[1] > max || code[1] < 0x0001_0000 {
696 return Err(GpuError::Unsupported("this SPIR-V version"));
697 }
698 let entries = shader::reflect::entry_points(code).map_err(|_| GpuError::Invalid("SPIR-V instructions do not fit the words"))?;
699 let spec = shader::reflect::spec_constants(code).map_err(|_| GpuError::Invalid("SpecId on something else than a scalar specialization constant"))?;
700 if spec.iter().enumerate().any(|(i, a)| spec[..i].iter().any(|b| b.spec_id == a.spec_id)) {
701 return Err(GpuError::Invalid("a SpecId twice in the module"));
702 }
703 let ci = vk::ShaderModuleCreateInfo {
704 s_type: vk::STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
705 code_size: code.len() * 4,
706 p_code: code.as_ptr(),
707 ..vk::Zero::zero()
708 };
709 let mut h = 0;
710 sh.vk(unsafe { (sh.f.create_shader_module)(sh.dev, &ci, core::ptr::null(), &mut h) })?;
712 let local_size_id = shader::reflect::uses_local_size_id(code);
713 Ok(ShaderModule(Arc::new(ShaderInner { sh: sh.clone(), h, words: code.to_vec(), entries, spec, local_size_id })))
714 }
715
716 pub fn entry_points(&self) -> Vec<(u32, &str)> {
718 self.0.entries.iter().map(|e| (e.model, e.name.as_str())).collect()
719 }
720
721 pub fn constants(&self) -> Vec<(u32, shader::reflect::Kind, f64)> {
723 self.0.spec.iter().map(|c| (c.spec_id, c.kind, kind_value(c.kind, c.default))).collect()
724 }
725}
726
727fn kind_value(k: shader::reflect::Kind, v: u64) -> f64 {
729 use shader::reflect::Kind;
730 match k {
731 Kind::Bool => (v != 0) as u32 as f64,
732 Kind::Int { width, signed: true } => {
733 let s = 64 - width.min(64);
734 (((v << s) as i64) >> s) as f64
735 }
736 Kind::Int { .. } => v as f64,
737 Kind::Float { width: 16 } => shader::reflect::f16_to_f32(v as u16) as f64,
738 Kind::Float { width: 32 } => f32::from_bits(v as u32) as f64,
739 Kind::Float { .. } => f64::from_bits(v),
740 }
741}
742
743fn constant_bits(k: shader::reflect::Kind, x: f64) -> Result<u64> {
745 use shader::reflect::Kind;
746 let bad = GpuError::Invalid("pipeline constant: the value does not fit the override's type");
747 match k {
748 Kind::Bool => Ok((x != 0.0 && !x.is_nan()) as u64),
749 Kind::Int { width, signed } => {
750 if !x.is_finite() {
751 return Err(bad);
752 }
753 let t = x as i128; let w = width.clamp(1, 64);
755 let (lo, hi) = if signed { (-(1i128 << (w - 1)), (1i128 << (w - 1)) - 1) } else { (0, (1i128 << w) - 1) };
756 if t < lo || t > hi {
757 return Err(bad);
758 }
759 let m = if w >= 64 { u64::MAX } else { (1u64 << w) - 1 };
760 Ok(t as u64 & m)
761 }
762 Kind::Float { width } => {
763 if !x.is_finite() {
764 return Err(bad);
765 }
766 match width {
767 16 => {
768 let h = shader::reflect::f16_from_f32(x as f32);
769 if h & 0x7c00 == 0x7c00 || !(x as f32).is_finite() {
770 return Err(bad);
771 }
772 Ok(h as u64)
773 }
774 32 => {
775 let f = x as f32;
776 if !f.is_finite() {
777 return Err(bad);
778 }
779 Ok(f.to_bits() as u64)
780 }
781 _ => Ok(x.to_bits()),
782 }
783 }
784 }
785}
786
787fn specialize(m: &ShaderInner, constants: &[Constant]) -> Result<(Vec<vk::SpecializationMapEntry>, Vec<u8>, Vec<(u32, u64)>)> {
790 let mut map = Vec::with_capacity(constants.len());
791 let mut data = Vec::new();
792 let mut set = Vec::with_capacity(constants.len());
793 for (i, c) in constants.iter().enumerate() {
794 if constants[..i].iter().any(|d| d.id == c.id) {
795 return Err(GpuError::Invalid("pipeline constant: an id twice"));
796 }
797 let Some(decl) = m.spec.iter().find(|s| s.spec_id == c.id) else {
798 return Err(GpuError::Invalid("pipeline constant: the module has no override with this id"));
799 };
800 let bits = constant_bits(decl.kind, c.value)?;
801 let size = match decl.kind {
802 shader::reflect::Kind::Bool => 4, shader::reflect::Kind::Int { width, .. } | shader::reflect::Kind::Float { width } => (width as usize).div_ceil(8),
804 };
805 map.push(vk::SpecializationMapEntry { constant_id: c.id, offset: data.len() as u32, size });
806 data.extend_from_slice(&bits.to_le_bytes()[..size]);
807 while data.len() % 4 != 0 {
808 data.push(0);
809 }
810 set.push((c.id, bits));
811 }
812 Ok((map, data, set))
813}
814
815fn stage_info(sh: &Shared, st: &ShaderStage, model: shader::Model) -> Result<(Vec<vk::SpecializationMapEntry>, Vec<u8>, Vec<(u32, u64)>)> {
818 let m = &st.module.0;
819 if !m.entries.iter().any(|e| e.name == st.entry && e.model == model as u32) {
820 return Err(GpuError::Invalid("the module has no entry point of this name for the stage"));
821 }
822 if model == shader::Model::GLCompute && m.local_size_id && !sh.maintenance4 {
823 return Err(GpuError::Unsupported("LocalSizeId (maintenance4, Vulkan 1.3)"));
824 }
825 specialize(m, st.constants)
826}
827
828pub(crate) struct BglInner {
831 sh: Arc<Shared>,
832 pub h: vk::Handle,
833 pub bindings: Vec<Binding>, needs: Vec<vk::DescriptorPoolSize>,
835}
836
837impl Drop for BglInner {
838 fn drop(&mut self) {
839 self.sh.defer(Garbage::SetLayout(self.h));
840 }
841}
842
843#[derive(Clone)]
845pub struct BindGroupLayout(pub(crate) Arc<BglInner>);
846
847impl Device {
848 pub fn create_bind_group_layout(&self, bindings: &[Binding]) -> Result<BindGroupLayout> {
849 let sh = &self.sh;
850 sh.check()?;
851 let mut b: Vec<Binding> = bindings.to_vec();
852 b.sort_by_key(|x| x.binding);
853 if b.windows(2).any(|w| w[0].binding == w[1].binding) {
854 return Err(GpuError::Invalid("a binding number twice"));
855 }
856 for x in &b {
857 if stage_bits(x.stages) == 0 {
858 return Err(GpuError::Invalid("binding without stages"));
859 }
860 if x.kind == BindingKind::StorageBuffer && x.stages.intersects(Stages::VERTEX) && !sh.features.vertex_stores {
861 return Err(GpuError::Unsupported("writable storage buffers in vertex shaders"));
862 }
863 }
864 let vb: Vec<vk::DescriptorSetLayoutBinding> = b
865 .iter()
866 .map(|x| vk::DescriptorSetLayoutBinding {
867 binding: x.binding,
868 descriptor_type: descriptor_type(x.kind),
869 descriptor_count: 1,
870 stage_flags: stage_bits(x.stages),
871 p_immutable_samplers: core::ptr::null(),
872 })
873 .collect();
874 let ci = vk::DescriptorSetLayoutCreateInfo {
875 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
876 binding_count: vb.len() as u32,
877 p_bindings: vb.as_ptr(),
878 ..vk::Zero::zero()
879 };
880 let mut h = 0;
881 sh.vk(unsafe { (sh.f.create_descriptor_set_layout)(sh.dev, &ci, core::ptr::null(), &mut h) })?;
883 let needs = vb.iter().map(|x| vk::DescriptorPoolSize { type_: x.descriptor_type, descriptor_count: 1 }).collect();
884 Ok(BindGroupLayout(Arc::new(BglInner { sh: sh.clone(), h, bindings: b, needs })))
885 }
886}
887
888pub(crate) struct PlInner {
889 sh: Arc<Shared>,
890 pub h: vk::Handle,
891 pub sets: Vec<vk::Handle>,
892 pub push: u32,
893 _groups: Vec<BindGroupLayout>,
894}
895
896impl Drop for PlInner {
897 fn drop(&mut self) {
898 self.sh.defer(Garbage::PipeLayout(self.h));
899 }
900}
901
902#[derive(Clone)]
904pub struct PipelineLayout(pub(crate) Arc<PlInner>);
905
906impl Device {
907 pub fn create_pipeline_layout(&self, desc: &PipelineLayoutDesc) -> Result<PipelineLayout> {
908 let sh = &self.sh;
909 sh.check()?;
910 if desc.groups.len() > BIND_GROUPS_MAX as usize || desc.groups.len() > sh.raw.max_bound_descriptor_sets as usize {
911 return Err(GpuError::Invalid("too many bind groups"));
912 }
913 if desc.push_constants % 4 != 0 || desc.push_constants > PUSH_CONSTANTS_MAX {
914 return Err(GpuError::Invalid("push constants: a multiple of 4, at most PUSH_CONSTANTS_MAX"));
915 }
916 let sets: Vec<vk::Handle> = desc.groups.iter().map(|g| g.0.h).collect();
917 let range = vk::PushConstantRange { stage_flags: vk::SHADER_STAGE_ALL, offset: 0, size: desc.push_constants };
918 let ci = vk::PipelineLayoutCreateInfo {
919 s_type: vk::STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
920 set_layout_count: sets.len() as u32,
921 p_set_layouts: sets.as_ptr(),
922 push_constant_range_count: (desc.push_constants > 0) as u32,
923 p_push_constant_ranges: &range,
924 ..vk::Zero::zero()
925 };
926 let mut h = 0;
927 sh.vk(unsafe { (sh.f.create_pipeline_layout)(sh.dev, &ci, core::ptr::null(), &mut h) })?;
929 Ok(PipelineLayout(Arc::new(PlInner {
930 sh: sh.clone(),
931 h,
932 sets,
933 push: desc.push_constants,
934 _groups: desc.groups.iter().map(|g| (*g).clone()).collect(),
935 })))
936 }
937}
938
939#[derive(Clone)]
941#[allow(dead_code)]
942enum Keep {
943 Buf(Buffer),
944 View(TextureView),
945 Sampler(Sampler),
946}
947
948pub(crate) struct BgInner {
949 sh: Arc<Shared>,
950 pub set: vk::Handle,
951 pool: vk::Handle,
952 own_pool: bool,
953 pub layout: vk::Handle,
954 _layout: BindGroupLayout,
955 _keep: Vec<Keep>,
956}
957
958impl Drop for BgInner {
959 fn drop(&mut self) {
960 if self.own_pool {
961 self.sh.defer(Garbage::Pool(self.pool));
962 } else {
963 self.sh.defer(Garbage::Set(self.pool, self.set));
964 }
965 }
966}
967
968#[derive(Clone)]
970pub struct BindGroup(pub(crate) Arc<BgInner>);
971
972impl Device {
973 pub fn create_bind_group(&self, desc: &BindGroupDesc) -> Result<BindGroup> {
974 let sh = &self.sh;
975 sh.check()?;
976 let lay = &desc.layout.0;
977 if desc.entries.len() != lay.bindings.len() {
978 return Err(GpuError::Invalid("bind group: one entry per binding of the layout"));
979 }
980 let mut keep = Vec::with_capacity(desc.entries.len());
981 let mut bufs: Vec<vk::DescriptorBufferInfo> = Vec::with_capacity(desc.entries.len());
982 let mut imgs: Vec<vk::DescriptorImageInfo> = Vec::with_capacity(desc.entries.len());
983 let mut which: Vec<(u32, u32, bool, usize)> = Vec::with_capacity(desc.entries.len()); for e in desc.entries {
985 let Some(b) = lay.bindings.iter().find(|b| b.binding == e.binding) else {
986 return Err(GpuError::Invalid("bind group: entry for a binding the layout lacks"));
987 };
988 if which.iter().any(|w| w.0 == e.binding) {
989 return Err(GpuError::Invalid("bind group: a binding twice"));
990 }
991 let ty = descriptor_type(b.kind);
992 match (b.kind, e.resource) {
993 (BindingKind::UniformBuffer | BindingKind::StorageBuffer | BindingKind::StorageBufferRead,
994 Resource::Buffer { buffer, offset, size }) => {
995 let uniform = b.kind == BindingKind::UniformBuffer;
996 let (need, align, max) = if uniform {
997 (BufferUsage::UNIFORM, sh.limits.min_uniform_offset_alignment, sh.limits.max_uniform_binding)
998 } else {
999 (BufferUsage::STORAGE, sh.limits.min_storage_offset_alignment, sh.limits.max_storage_binding)
1000 };
1001 if !buffer.usage().contains(need) {
1002 return Err(GpuError::Invalid("bind group: buffer lacks UNIFORM / STORAGE usage"));
1003 }
1004 let range = if size == 0 { buffer.size().saturating_sub(offset) } else { size };
1005 if offset % align.max(1) as u64 != 0 || range == 0 || offset.checked_add(range).is_none_or(|e| e > buffer.size()) || range > max as u64 {
1006 return Err(GpuError::Invalid("bind group: buffer range (alignment, size, limit)"));
1007 }
1008 which.push((e.binding, ty, true, bufs.len()));
1009 bufs.push(vk::DescriptorBufferInfo { buffer: buffer.handle(), offset, range });
1010 keep.push(Keep::Buf(buffer.clone()));
1011 }
1012 (BindingKind::Texture, Resource::Texture(v)) | (BindingKind::StorageTexture, Resource::StorageTexture(v)) => {
1013 let storage = b.kind == BindingKind::StorageTexture;
1014 let need = if storage { TextureUsage::STORAGE } else { TextureUsage::SAMPLED };
1015 if !v.usage().contains(need) {
1016 return Err(GpuError::Invalid("bind group: texture lacks SAMPLED / STORAGE usage"));
1017 }
1018 which.push((e.binding, ty, false, imgs.len()));
1019 imgs.push(vk::DescriptorImageInfo {
1020 sampler: 0,
1021 image_view: v.0.h,
1022 image_layout: if storage { vk::IMAGE_LAYOUT_GENERAL } else { vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
1023 });
1024 keep.push(Keep::View(v.clone()));
1025 }
1026 (BindingKind::Sampler, Resource::Sampler(s)) => {
1027 which.push((e.binding, ty, false, imgs.len()));
1028 imgs.push(vk::DescriptorImageInfo { sampler: s.0.h, image_view: 0, image_layout: 0 });
1029 keep.push(Keep::Sampler(s.clone()));
1030 }
1031 _ => return Err(GpuError::Invalid("bind group: resource does not match the binding's kind")),
1032 }
1033 }
1034 let (pool, set) = sh.alloc_set(lay.h, &lay.needs)?;
1035 let writes: Vec<vk::WriteDescriptorSet> = which
1036 .iter()
1037 .map(|&(binding, ty, is_buf, i)| vk::WriteDescriptorSet {
1038 s_type: vk::STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1039 dst_set: set,
1040 dst_binding: binding,
1041 descriptor_count: 1,
1042 descriptor_type: ty,
1043 p_image_info: if is_buf { core::ptr::null() } else { &imgs[i] },
1044 p_buffer_info: if is_buf { &bufs[i] } else { core::ptr::null() },
1045 ..vk::Zero::zero()
1046 })
1047 .collect();
1048 unsafe { (sh.f.update_descriptor_sets)(sh.dev, writes.len() as u32, writes.as_ptr(), 0, core::ptr::null()) };
1050 Ok(BindGroup(Arc::new(BgInner {
1051 sh: sh.clone(),
1052 set,
1053 pool,
1054 own_pool: false,
1055 layout: lay.h,
1056 _layout: desc.layout.clone(),
1057 _keep: keep,
1058 })))
1059 }
1060}
1061
1062struct Slots {
1066 keep: [Vec<Option<Keep>>; 4],
1067}
1068
1069pub struct Bindless {
1073 group: BindGroup,
1074 layout: BindGroupLayout,
1075 counts: [u32; 4],
1076 slots: SpinLock<Slots>,
1077}
1078
1079impl Device {
1080 pub fn create_bindless(&self, desc: &BindlessDesc) -> Result<Bindless> {
1081 let sh = &self.sh;
1082 sh.check()?;
1083 if !sh.features.bindless {
1084 return Err(GpuError::Unsupported("bindless tables (level Voll)"));
1085 }
1086 let l = &sh.limits;
1087 let counts = [desc.textures, desc.samplers, desc.buffers, desc.storage_textures];
1088 let maxes = [l.max_bindless_textures, l.max_bindless_samplers, l.max_bindless_buffers, l.max_bindless_storage_textures];
1089 if counts.iter().zip(maxes.iter()).any(|(c, m)| c > m) {
1090 return Err(GpuError::Invalid("bindless: more slots than Limits::max_bindless_*"));
1091 }
1092 let types = [vk::DESCRIPTOR_TYPE_SAMPLED_IMAGE, vk::DESCRIPTOR_TYPE_SAMPLER, vk::DESCRIPTOR_TYPE_STORAGE_BUFFER,
1093 vk::DESCRIPTOR_TYPE_STORAGE_IMAGE];
1094 let vb: Vec<vk::DescriptorSetLayoutBinding> = (0..4)
1095 .filter(|&i| counts[i] > 0)
1096 .map(|i| vk::DescriptorSetLayoutBinding {
1097 binding: i as u32,
1098 descriptor_type: types[i],
1099 descriptor_count: counts[i],
1100 stage_flags: vk::SHADER_STAGE_ALL,
1101 p_immutable_samplers: core::ptr::null(),
1102 })
1103 .collect();
1104 if vb.is_empty() {
1105 return Err(GpuError::Invalid("bindless table without slots"));
1106 }
1107 let flags = vec![vk::DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | vk::DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT
1108 | vk::DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; vb.len()];
1109 let fi = vk::DescriptorSetLayoutBindingFlagsCreateInfo {
1110 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
1111 binding_count: flags.len() as u32,
1112 p_binding_flags: flags.as_ptr(),
1113 ..vk::Zero::zero()
1114 };
1115 let ci = vk::DescriptorSetLayoutCreateInfo {
1116 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
1117 p_next: &fi as *const _ as *const c_void,
1118 flags: vk::DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
1119 binding_count: vb.len() as u32,
1120 p_bindings: vb.as_ptr(),
1121 };
1122 let mut lh = 0;
1123 sh.vk(unsafe { (sh.f.create_descriptor_set_layout)(sh.dev, &ci, core::ptr::null(), &mut lh) })?;
1125 let layout = BindGroupLayout(Arc::new(BglInner { sh: sh.clone(), h: lh, bindings: Vec::new(), needs: Vec::new() }));
1126 let sizes: Vec<vk::DescriptorPoolSize> =
1127 vb.iter().map(|b| vk::DescriptorPoolSize { type_: b.descriptor_type, descriptor_count: b.descriptor_count }).collect();
1128 let pi = vk::DescriptorPoolCreateInfo {
1129 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
1130 flags: vk::DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
1131 max_sets: 1,
1132 pool_size_count: sizes.len() as u32,
1133 p_pool_sizes: sizes.as_ptr(),
1134 ..vk::Zero::zero()
1135 };
1136 let mut pool = 0;
1137 sh.vk(unsafe { (sh.f.create_descriptor_pool)(sh.dev, &pi, core::ptr::null(), &mut pool) })?;
1139 let ai = vk::DescriptorSetAllocateInfo {
1140 s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
1141 descriptor_pool: pool,
1142 descriptor_set_count: 1,
1143 p_set_layouts: &lh,
1144 ..vk::Zero::zero()
1145 };
1146 let mut set = 0;
1147 if let Err(e) = sh.vk(unsafe { (sh.f.allocate_descriptor_sets)(sh.dev, &ai, &mut set) }) {
1149 sh.defer(Garbage::Pool(pool));
1150 return Err(e);
1151 }
1152 let group = BindGroup(Arc::new(BgInner {
1153 sh: sh.clone(),
1154 set,
1155 pool,
1156 own_pool: true,
1157 layout: lh,
1158 _layout: layout.clone(),
1159 _keep: Vec::new(),
1160 }));
1161 let slots = Slots { keep: counts.map(|n| vec![None; n as usize]) };
1162 Ok(Bindless { group, layout, counts, slots: SpinLock::new(slots) })
1163 }
1164}
1165
1166impl Bindless {
1167 pub fn group(&self) -> &BindGroup {
1169 &self.group
1170 }
1171 pub fn layout(&self) -> &BindGroupLayout {
1173 &self.layout
1174 }
1175 pub fn counts(&self) -> [u32; 4] {
1177 self.counts
1178 }
1179
1180 fn write(&self, binding: usize, slot: u32, keep: Keep, img: Option<vk::DescriptorImageInfo>,
1181 buf: Option<vk::DescriptorBufferInfo>) -> Result<()> {
1182 let sh = &self.group.0.sh;
1183 sh.check()?;
1184 if slot >= self.counts[binding] {
1185 return Err(GpuError::Invalid("bindless: slot outside the table"));
1186 }
1187 let types = [vk::DESCRIPTOR_TYPE_SAMPLED_IMAGE, vk::DESCRIPTOR_TYPE_SAMPLER, vk::DESCRIPTOR_TYPE_STORAGE_BUFFER,
1188 vk::DESCRIPTOR_TYPE_STORAGE_IMAGE];
1189 let w = vk::WriteDescriptorSet {
1190 s_type: vk::STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
1191 dst_set: self.group.0.set,
1192 dst_binding: binding as u32,
1193 dst_array_element: slot,
1194 descriptor_count: 1,
1195 descriptor_type: types[binding],
1196 p_image_info: img.as_ref().map_or(core::ptr::null(), |i| i as *const _),
1197 p_buffer_info: buf.as_ref().map_or(core::ptr::null(), |b| b as *const _),
1198 ..vk::Zero::zero()
1199 };
1200 let mut slots = self.slots.lock();
1201 unsafe { (sh.f.update_descriptor_sets)(sh.dev, 1, &w, 0, core::ptr::null()) };
1203 slots.keep[binding][slot as usize] = Some(keep); Ok(())
1205 }
1206
1207 pub fn set_texture(&self, slot: u32, view: &TextureView) -> Result<()> {
1209 if !view.usage().contains(TextureUsage::SAMPLED) {
1210 return Err(GpuError::Invalid("bindless: texture lacks SAMPLED usage"));
1211 }
1212 let i = vk::DescriptorImageInfo { sampler: 0, image_view: view.0.h, image_layout: vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
1213 self.write(0, slot, Keep::View(view.clone()), Some(i), None)
1214 }
1215
1216 pub fn set_sampler(&self, slot: u32, s: &Sampler) -> Result<()> {
1218 let i = vk::DescriptorImageInfo { sampler: s.0.h, image_view: 0, image_layout: 0 };
1219 self.write(1, slot, Keep::Sampler(s.clone()), Some(i), None)
1220 }
1221
1222 pub fn set_buffer(&self, slot: u32, buf: &Buffer, offset: u64, size: u64) -> Result<()> {
1224 let sh = &self.group.0.sh;
1225 let range = if size == 0 { buf.size().saturating_sub(offset) } else { size };
1226 let align = sh.limits.min_storage_offset_alignment.max(1) as u64;
1227 if !buf.usage().contains(BufferUsage::STORAGE) || offset % align != 0 || range == 0 || offset.checked_add(range).is_none_or(|e| e > buf.size()) {
1228 return Err(GpuError::Invalid("bindless: buffer range (usage, alignment, size)"));
1229 }
1230 let b = vk::DescriptorBufferInfo { buffer: buf.handle(), offset, range };
1231 self.write(2, slot, Keep::Buf(buf.clone()), None, Some(b))
1232 }
1233
1234 pub fn set_storage_texture(&self, slot: u32, view: &TextureView) -> Result<()> {
1236 if !view.usage().contains(TextureUsage::STORAGE) {
1237 return Err(GpuError::Invalid("bindless: texture lacks STORAGE usage"));
1238 }
1239 let i = vk::DescriptorImageInfo { sampler: 0, image_view: view.0.h, image_layout: vk::IMAGE_LAYOUT_GENERAL };
1240 self.write(3, slot, Keep::View(view.clone()), Some(i), None)
1241 }
1242}
1243
1244pub(crate) struct PipeInner {
1247 sh: Arc<Shared>,
1248 pub h: vk::Handle,
1249 pub layout: PipelineLayout,
1250 pub colors: [u32; COLOR_TARGETS_MAX],
1251 pub ncolor: usize,
1252 pub depth: u32,
1253}
1254
1255impl Drop for PipeInner {
1256 fn drop(&mut self) {
1257 self.sh.defer(Garbage::Pipeline(self.h));
1258 }
1259}
1260
1261#[derive(Clone)]
1263pub struct ComputePipeline(pub(crate) Arc<PipeInner>);
1264
1265#[derive(Clone)]
1267pub struct RenderPipeline(pub(crate) Arc<PipeInner>);
1268
1269impl Device {
1270 pub fn create_compute_pipeline(&self, desc: &ComputePipelineDesc) -> Result<ComputePipeline> {
1271 self.compute_pipeline(desc, 0)
1272 }
1273
1274 pub fn create_compute_pipeline_sized(&self, desc: &ComputePipelineDesc, size: u32) -> Result<ComputePipeline> {
1278 if size != 0 {
1279 if !self.sh.features.subgroup_size_control {
1280 return Err(GpuError::Unsupported("subgroup size control"));
1281 }
1282 let lim = &self.sh.limits;
1283 if !size.is_power_of_two() || size < lim.subgroup_min_size || size > lim.subgroup_max_size {
1284 return Err(GpuError::Invalid("the required subgroup size is not a power of two within Limits::subgroup_min_size ..= subgroup_max_size"));
1285 }
1286 }
1287 self.compute_pipeline(desc, size)
1288 }
1289
1290 fn compute_pipeline(&self, desc: &ComputePipelineDesc, size: u32) -> Result<ComputePipeline> {
1291 let sh = &self.sh;
1292 sh.check()?;
1293 let (map, data, set) = stage_info(sh, &desc.shader, shader::Model::GLCompute)?;
1294 let wg = shader::reflect::workgroup_size(&desc.shader.module.0.words, desc.shader.entry, &set)
1296 .map_err(|_| GpuError::Invalid("pipeline constants: a specialization constant is undefined (division by 0, shift)"))?;
1297 let Some(wg) = wg else { return Err(GpuError::Invalid("compute entry point without a workgroup size")) };
1298 let lim = &sh.limits;
1299 let count = wg.iter().map(|&x| x as u64).product::<u64>();
1300 if wg.contains(&0) || (0..3).any(|i| wg[i] > lim.max_compute_workgroup_size[i]) || count > lim.max_compute_invocations as u64 {
1301 return Err(GpuError::Invalid("workgroup size 0 or beyond Limits::max_compute_workgroup_size / max_compute_invocations"));
1302 }
1303 let si = vk::SpecializationInfo {
1304 map_entry_count: map.len() as u32,
1305 p_map_entries: map.as_ptr(),
1306 data_size: data.len(),
1307 p_data: data.as_ptr() as *const c_void,
1308 };
1309 let name = nul_name(desc.shader.entry);
1310 if size != 0 && wg[0] % size != 0 {
1311 return Err(GpuError::Invalid("the workgroup size in x is not a multiple of the required subgroup size"));
1312 }
1313 let required = vk::PipelineShaderStageRequiredSubgroupSizeCreateInfo {
1314 s_type: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO,
1315 p_next: core::ptr::null_mut(),
1316 required_subgroup_size: size,
1317 };
1318 let ci = vk::ComputePipelineCreateInfo {
1319 s_type: vk::STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
1320 stage: vk::PipelineShaderStageCreateInfo {
1321 s_type: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1322 p_next: if size != 0 { &required as *const _ as *const c_void } else { core::ptr::null() },
1323 stage: vk::SHADER_STAGE_COMPUTE_BIT,
1324 module: desc.shader.module.0.h,
1325 p_name: name.as_ptr(),
1326 p_specialization_info: if map.is_empty() { core::ptr::null() } else { &si as *const _ as *const c_void },
1327 ..vk::Zero::zero()
1328 },
1329 layout: desc.layout.0.h,
1330 base_pipeline_index: -1,
1331 ..vk::Zero::zero()
1332 };
1333 let mut h = 0;
1334 sh.vk(unsafe { (sh.f.create_compute_pipelines)(sh.dev, 0, 1, &ci, core::ptr::null(), &mut h) })?;
1336 Ok(ComputePipeline(Arc::new(PipeInner {
1337 sh: sh.clone(),
1338 h,
1339 layout: desc.layout.clone(),
1340 colors: [0; COLOR_TARGETS_MAX],
1341 ncolor: 0,
1342 depth: 0,
1343 })))
1344 }
1345
1346 pub fn create_render_pipeline(&self, desc: &RenderPipelineDesc) -> Result<RenderPipeline> {
1347 let sh = &self.sh;
1348 sh.check()?;
1349 if desc.targets.len() > sh.limits.max_color_targets as usize {
1350 return Err(GpuError::Invalid("too many colour targets"));
1351 }
1352 if desc.targets.iter().any(|t| t.format.is_depth() || t.format.is_compressed()) {
1353 return Err(GpuError::Invalid("colour target with a depth or compressed format"));
1354 }
1355 if desc.depth.is_some_and(|d| !d.format.is_depth()) {
1356 return Err(GpuError::Invalid("depth state with a colour format"));
1357 }
1358 let (vmap, vdata, _) = stage_info(sh, &desc.vertex, shader::Model::Vertex)?;
1359 let (fmap, fdata, _) = match &desc.fragment {
1360 Some(fs) => stage_info(sh, fs, shader::Model::Fragment)?,
1361 None => (Vec::new(), Vec::new(), Vec::new()),
1362 };
1363 let spec = |map: &Vec<vk::SpecializationMapEntry>, data: &Vec<u8>| vk::SpecializationInfo {
1364 map_entry_count: map.len() as u32,
1365 p_map_entries: map.as_ptr(),
1366 data_size: data.len(),
1367 p_data: data.as_ptr() as *const c_void,
1368 };
1369 let (vsi, fsi) = (spec(&vmap, &vdata), spec(&fmap, &fdata));
1370 let ptr = |map: &Vec<vk::SpecializationMapEntry>, si: &vk::SpecializationInfo| {
1371 if map.is_empty() { core::ptr::null() } else { si as *const _ as *const c_void }
1372 };
1373 let vname = nul_name(desc.vertex.entry);
1374 let fname = nul_name(desc.fragment.map_or("", |f| f.entry));
1375 let mut stages = vec![vk::PipelineShaderStageCreateInfo {
1376 s_type: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1377 stage: vk::SHADER_STAGE_VERTEX_BIT,
1378 module: desc.vertex.module.0.h,
1379 p_name: vname.as_ptr(),
1380 p_specialization_info: ptr(&vmap, &vsi),
1381 ..vk::Zero::zero()
1382 }];
1383 if let Some(fs) = desc.fragment {
1384 stages.push(vk::PipelineShaderStageCreateInfo {
1385 s_type: vk::STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
1386 stage: vk::SHADER_STAGE_FRAGMENT_BIT,
1387 module: fs.module.0.h,
1388 p_name: fname.as_ptr(),
1389 p_specialization_info: ptr(&fmap, &fsi),
1390 ..vk::Zero::zero()
1391 });
1392 }
1393 let mut vbind = Vec::new();
1394 let mut vattr = Vec::new();
1395 for (i, b) in desc.buffers.iter().enumerate() {
1396 vbind.push(vk::VertexInputBindingDescription {
1397 binding: i as u32,
1398 stride: b.stride,
1399 input_rate: if b.per_instance { vk::VERTEX_INPUT_RATE_INSTANCE } else { vk::VERTEX_INPUT_RATE_VERTEX },
1400 });
1401 for a in b.attributes {
1402 vattr.push(vk::VertexInputAttributeDescription {
1403 location: a.location,
1404 binding: i as u32,
1405 format: match a.format {
1406 VertexFormat::F32 => vk::FORMAT_R32_SFLOAT,
1407 VertexFormat::F32x2 => vk::FORMAT_R32G32_SFLOAT,
1408 VertexFormat::F32x3 => vk::FORMAT_R32G32B32_SFLOAT,
1409 VertexFormat::F32x4 => vk::FORMAT_R32G32B32A32_SFLOAT,
1410 VertexFormat::U32 => vk::FORMAT_R32_UINT,
1411 VertexFormat::U32x2 => vk::FORMAT_R32G32_UINT,
1412 VertexFormat::U32x4 => vk::FORMAT_R32G32B32A32_UINT,
1413 VertexFormat::Unorm8x4 => vk::FORMAT_R8G8B8A8_UNORM,
1414 },
1415 offset: a.offset,
1416 });
1417 }
1418 }
1419 if vbind.len() > sh.limits.max_vertex_buffers as usize || vattr.len() > sh.limits.max_vertex_attributes as usize {
1420 return Err(GpuError::Invalid("too many vertex buffers or attributes"));
1421 }
1422 let vi = vk::PipelineVertexInputStateCreateInfo {
1423 s_type: vk::STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
1424 vertex_binding_description_count: vbind.len() as u32,
1425 p_vertex_binding_descriptions: vbind.as_ptr(),
1426 vertex_attribute_description_count: vattr.len() as u32,
1427 p_vertex_attribute_descriptions: vattr.as_ptr(),
1428 ..vk::Zero::zero()
1429 };
1430 let ia = vk::PipelineInputAssemblyStateCreateInfo {
1431 s_type: vk::STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
1432 topology: match desc.topology {
1433 Topology::Points => vk::PRIMITIVE_TOPOLOGY_POINT_LIST,
1434 Topology::Lines => vk::PRIMITIVE_TOPOLOGY_LINE_LIST,
1435 Topology::LineStrip => vk::PRIMITIVE_TOPOLOGY_LINE_STRIP,
1436 Topology::Triangles => vk::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
1437 Topology::TriangleStrip => vk::PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
1438 },
1439 ..vk::Zero::zero()
1440 };
1441 let vp = vk::PipelineViewportStateCreateInfo {
1442 s_type: vk::STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
1443 viewport_count: 1,
1444 scissor_count: 1,
1445 ..vk::Zero::zero()
1446 };
1447 let rs = vk::PipelineRasterizationStateCreateInfo {
1448 s_type: vk::STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
1449 polygon_mode: vk::POLYGON_MODE_FILL,
1450 cull_mode: match desc.cull {
1451 Cull::None => vk::CULL_MODE_NONE,
1452 Cull::Front => vk::CULL_MODE_FRONT_BIT,
1453 Cull::Back => vk::CULL_MODE_BACK_BIT,
1454 },
1455 front_face: if desc.front_cw { vk::FRONT_FACE_CLOCKWISE } else { vk::FRONT_FACE_COUNTER_CLOCKWISE },
1456 line_width: 1.0,
1457 ..vk::Zero::zero()
1458 };
1459 let ms = vk::PipelineMultisampleStateCreateInfo {
1460 s_type: vk::STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
1461 rasterization_samples: vk::SAMPLE_COUNT_1_BIT,
1462 min_sample_shading: 1.0,
1463 ..vk::Zero::zero()
1464 };
1465 let ds = vk::PipelineDepthStencilStateCreateInfo {
1466 s_type: vk::STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
1467 depth_test_enable: desc.depth.is_some() as u32,
1468 depth_write_enable: desc.depth.is_some_and(|d| d.write) as u32,
1469 depth_compare_op: desc.depth.map_or(vk::COMPARE_OP_ALWAYS, |d| compare_op(d.compare)),
1470 max_depth_bounds: 1.0,
1471 ..vk::Zero::zero()
1472 };
1473 let factor = |f: BlendFactor| match f {
1474 BlendFactor::Zero => vk::BLEND_FACTOR_ZERO,
1475 BlendFactor::One => vk::BLEND_FACTOR_ONE,
1476 BlendFactor::Src => vk::BLEND_FACTOR_SRC_COLOR,
1477 BlendFactor::OneMinusSrc => vk::BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
1478 BlendFactor::Dst => vk::BLEND_FACTOR_DST_COLOR,
1479 BlendFactor::OneMinusDst => vk::BLEND_FACTOR_ONE_MINUS_DST_COLOR,
1480 BlendFactor::SrcAlpha => vk::BLEND_FACTOR_SRC_ALPHA,
1481 BlendFactor::OneMinusSrcAlpha => vk::BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
1482 BlendFactor::DstAlpha => vk::BLEND_FACTOR_DST_ALPHA,
1483 BlendFactor::OneMinusDstAlpha => vk::BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
1484 };
1485 let op = |o: BlendOp| match o {
1486 BlendOp::Add => vk::BLEND_OP_ADD,
1487 BlendOp::Subtract => vk::BLEND_OP_SUBTRACT,
1488 BlendOp::ReverseSubtract => vk::BLEND_OP_REVERSE_SUBTRACT,
1489 BlendOp::Min => vk::BLEND_OP_MIN,
1490 BlendOp::Max => vk::BLEND_OP_MAX,
1491 };
1492 let att: Vec<vk::PipelineColorBlendAttachmentState> = desc
1493 .targets
1494 .iter()
1495 .map(|t| {
1496 let b = t.blend.unwrap_or(Blend::ADD);
1497 vk::PipelineColorBlendAttachmentState {
1498 blend_enable: t.blend.is_some() as u32,
1499 src_color_blend_factor: factor(b.color.src),
1500 dst_color_blend_factor: factor(b.color.dst),
1501 color_blend_op: op(b.color.op),
1502 src_alpha_blend_factor: factor(b.alpha.src),
1503 dst_alpha_blend_factor: factor(b.alpha.dst),
1504 alpha_blend_op: op(b.alpha.op),
1505 color_write_mask: t.write_mask & 0xF,
1506 }
1507 })
1508 .collect();
1509 let cb = vk::PipelineColorBlendStateCreateInfo {
1510 s_type: vk::STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
1511 attachment_count: att.len() as u32,
1512 p_attachments: att.as_ptr(),
1513 ..vk::Zero::zero()
1514 };
1515 let dyns = [vk::DYNAMIC_STATE_VIEWPORT, vk::DYNAMIC_STATE_SCISSOR];
1516 let dy = vk::PipelineDynamicStateCreateInfo {
1517 s_type: vk::STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
1518 dynamic_state_count: 2,
1519 p_dynamic_states: dyns.as_ptr(),
1520 ..vk::Zero::zero()
1521 };
1522 let mut colors = [0u32; COLOR_TARGETS_MAX];
1523 for (i, t) in desc.targets.iter().enumerate() {
1524 colors[i] = vk_format(t.format);
1525 }
1526 let depth = desc.depth.map_or(vk::FORMAT_UNDEFINED, |d| vk_format(d.format));
1527 let stencil = desc.depth.filter(|d| d.format.has_stencil()).map_or(vk::FORMAT_UNDEFINED, |d| vk_format(d.format));
1528 let ri = vk::PipelineRenderingCreateInfo {
1529 s_type: vk::STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
1530 color_attachment_count: desc.targets.len() as u32,
1531 p_color_attachment_formats: colors.as_ptr(),
1532 depth_attachment_format: depth,
1533 stencil_attachment_format: stencil,
1534 ..vk::Zero::zero()
1535 };
1536 let gi = vk::GraphicsPipelineCreateInfo {
1537 s_type: vk::STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
1538 p_next: &ri as *const _ as *const c_void,
1539 stage_count: stages.len() as u32,
1540 p_stages: stages.as_ptr(),
1541 p_vertex_input_state: &vi,
1542 p_input_assembly_state: &ia,
1543 p_viewport_state: &vp,
1544 p_rasterization_state: &rs,
1545 p_multisample_state: &ms,
1546 p_depth_stencil_state: &ds,
1547 p_color_blend_state: &cb,
1548 p_dynamic_state: &dy,
1549 layout: desc.layout.0.h,
1550 base_pipeline_index: -1,
1551 ..vk::Zero::zero()
1552 };
1553 let mut h = 0;
1554 sh.vk(unsafe { (sh.f.create_graphics_pipelines)(sh.dev, 0, 1, &gi, core::ptr::null(), &mut h) })?;
1556 Ok(RenderPipeline(Arc::new(PipeInner {
1557 sh: sh.clone(),
1558 h,
1559 layout: desc.layout.clone(),
1560 colors,
1561 ncolor: desc.targets.len(),
1562 depth,
1563 })))
1564 }
1565}
1566
1567pub(crate) struct TsInner {
1570 sh: Arc<Shared>,
1571 pub pool: vk::Handle,
1572 pub count: u32,
1573}
1574
1575impl Drop for TsInner {
1576 fn drop(&mut self) {
1577 self.sh.defer(Garbage::Query(self.pool));
1578 }
1579}
1580
1581#[derive(Clone)]
1583pub struct Timestamps(pub(crate) Arc<TsInner>);
1584
1585impl Device {
1586 pub fn create_timestamps(&self, count: u32) -> Result<Timestamps> {
1587 let sh = &self.sh;
1588 sh.check()?;
1589 if !sh.features.timestamps {
1590 return Err(GpuError::Unsupported("timestamps"));
1591 }
1592 if count == 0 {
1593 return Err(GpuError::Invalid("0 timestamps"));
1594 }
1595 let ci = vk::QueryPoolCreateInfo {
1596 s_type: vk::STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
1597 query_type: vk::QUERY_TYPE_TIMESTAMP,
1598 query_count: count,
1599 ..vk::Zero::zero()
1600 };
1601 let mut pool = 0;
1602 sh.vk(unsafe { (sh.f.create_query_pool)(sh.dev, &ci, core::ptr::null(), &mut pool) })?;
1604 let ts = Timestamps(Arc::new(TsInner { sh: sh.clone(), pool, count }));
1605 match sh.f.reset_query_pool.filter(|_| sh.host_query_reset) {
1608 Some(reset) => unsafe { reset(sh.dev, pool, 0, count) },
1610 None => {
1611 let mut st = sh.st.lock();
1612 let cb = sh.upload_cb(&mut st)?;
1613 unsafe { (sh.f.cmd_reset_query_pool)(cb, pool, 0, count) };
1615 }
1616 }
1617 Ok(ts)
1618 }
1619}
1620
1621impl Timestamps {
1622 pub fn count(&self) -> u32 {
1623 self.0.count
1624 }
1625
1626 pub fn read_ticks(&self, first: u32, n: u32) -> Result<Vec<u64>> {
1628 let t = &self.0;
1629 t.sh.check()?;
1630 if n == 0 || first.checked_add(n).is_none_or(|e| e > t.count) {
1631 return Err(GpuError::Invalid("timestamps outside the set"));
1632 }
1633 let mut raw = vec![[0u64; 2]; n as usize];
1634 let r = unsafe {
1636 (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,
1637 vk::QUERY_RESULT_64_BIT | vk::QUERY_RESULT_WITH_AVAILABILITY_BIT)
1638 };
1639 if r == vk::NOT_READY {
1640 return Err(GpuError::NotReady);
1641 }
1642 t.sh.vk(r)?;
1643 if raw.iter().any(|p| p[1] == 0) {
1644 return Err(GpuError::NotReady);
1645 }
1646 Ok(raw.iter().map(|p| p[0] & t.sh.ts_mask).collect())
1647 }
1648
1649 pub fn read_ms(&self, first: u32, n: u32) -> Result<Vec<f64>> {
1652 let ticks = self.read_ticks(first, n)?;
1653 let sh = &self.0.sh;
1654 let low = ticks.iter().copied().min().unwrap_or(0).max(1);
1655 let base = match sh.ts_base.compare_exchange(0, low, core::sync::atomic::Ordering::AcqRel,
1656 core::sync::atomic::Ordering::Acquire) {
1657 Ok(_) => low,
1658 Err(b) => b,
1659 };
1660 let period = sh.limits.timestamp_period_ns as f64;
1661 Ok(ticks.into_iter().map(|t| (t as i128 - base as i128) as f64 * period / 1.0e6).collect())
1662 }
1663}