Skip to main content

gpu/
vk_dev.rs

1// vk_dev -- Vulkan device and queue: creation, the timeline, deferred destruction, command
2// buffer slots, staged uploads, submission
3//
4//   adapter.create_device(&DeviceDesc) -> Result<Device>
5//        one queue (the first family with graphics + compute, presentation checked per
6//        surface); level Voll when the adapter has it and desc.max_level allows it.  Enabled:
7//        timeline semaphores, dynamic rendering (core 1.3 or the KHR extensions on 1.1 / 1.2),
8//        VK_KHR_swapchain when the instance has surfaces, the optional features of Features,
9//        the Vulkan memory model only together with its device scope (else Device-scope
10//        atomics, as in the benchmark's cull, would be invalid); on Voll also descriptor
11//        indexing (update after bind, partially bound, non-uniform indexing) and
12//        draw-indirect-count; maintenance4 on Vulkan 1.3 (LocalSizeId); for shaders compiled
13//        from WGSL (lib/wgsl): f16 in uniform buffers and push constants, the standard uniform
14//        buffer layout, demote-to-helper (discard on Vulkan 1.3), clip distances, the
15//        primitive index (geometryShader), sample-rate shading, dual-source blending in
16//        shaders, the extended storage image formats, storage reads without format.  Not
17//        enabled: robust buffer access (speed; lib/wgsl's back end clamps indices instead).
18//   device.queue() -> &Queue        queue.submit([cmd, ..]) -> Result<SubmitId>, submit_after(&[id], [cmd, ..])
19//   device.compute_queue() -> Option<&ComputeQueue>    (G5d) a queue of its own for compute and copies when
20//        DeviceOptions::compute_queue asked for it (Adapter::create_device_with): a family without graphics where the
21//        device has one (jfgui_GPU_COMPUTE=0: none); encoder(), submit, submit_after
22//   queue.write_buffer(&buf, off, &[T])            staged into an upload command buffer that
23//   queue.write_texture(&region, &[u8], layout, before, after)   runs first in the next submit
24//   queue.write_texture_rects(&tex, &rects, &[u8], stride, before, after)   rectangles of a frame
25//   queue.write_texture_rects_at(&tex, &rects, origin, &[u8], stride, before, after)   of a larger image
26//   queue.read_buffer(&buf, off, &mut [T])          copies, submits and waits (slow path)
27//   queue.flush() -> Result<SubmitId>               submit the pending uploads now
28//   device.wait(id, timeout_ns) -> Result<bool>     false: timed out
29//   device.wait_idle(), poll() (frees what the GPU has finished with), is_lost(),
30//   test_lose() (a test aid: the device behaves as lost from now on)
31//
32// The timeline: one timeline semaphore per device; every submit signals the next value
33// (SubmitId).  completed caches the last value seen reached (vkGetSemaphoreCounterValue at
34// submits, polls and waits).  A dropped resource goes to the garbage with the value of the
35// latest submit and is destroyed once that value is reached -- earlier if no submit is
36// pending; a buffer or image, which the open upload command buffer may name, with the value of
37// the next submit, which runs it.  Command buffers come from slots (a transient command pool with one buffer each),
38// reused once their submission completed.  Uploads: the data goes into the staging ring, the
39// copies into one upload command buffer that ends with a global barrier (copy writes -> every
40// later access) and is put before the caller's command buffers in the next vkQueueSubmit.
41// The staging ring: 256 KiB chunks of Upload memory, filled front to back across submissions;
42// a full chunk is reused once the GPU has passed the last submission that reads from it (two
43// are kept free, more go back).  An upload above half a chunk gets a buffer of its own, reused
44// by the next one that fits and released when none came for a second (Device::poll): a frame of
45// small rectangles costs what they hold, not a full frame or a new allocation.
46// Device lost: VK_ERROR_DEVICE_LOST from any call sets the device's lost flag; every later
47// call returns GpuError::DeviceLost (objects are still destroyed normally).
48// (G5d) The compute queue comes only when asked for (DeviceOptions; without it a device is what it was in 0.1.0: one
49// queue, every resource exclusive).  It has a timeline of its own (two queues signalling one timeline semaphore could reach its
50// values out of order); its SubmitIds carry COMPUTE_ID.  A submission waits for the other queue's values through
51// the timeline (submit_after, at all commands).  A dropped resource waits for the latest submission of both queues;
52// command slots come from a pool of each family.  Resources are shared between the families concurrently: every
53// buffer, and every texture that is no render target (render targets stay exclusive to the device's queue: render
54// passes run there only, and some GPUs compress exclusive targets) -- no ownership transfers in the caller's code.
55// Pending uploads run on the device's queue: a compute submission flushes them first and waits for them.
56
57use alloc::sync::Arc;
58use alloc::vec::Vec;
59use core::ffi::c_void;
60use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
61
62use rt::sync::SpinLock;
63
64use crate::vk_ffi as vk;
65use crate::vk_inst::{Adapter, InstShared, err};
66use crate::vk_mem::{Alloc, Allocator};
67use crate::{AdapterInfo, DataLayout, DeviceDesc, DeviceOptions, Features, GpuError, Layout, Level, Limits, MemoryKind, Pod, Result,
68            SubmitId, TextureRegion};
69
70/// what waits for the GPU before it is destroyed
71pub(crate) enum Garbage {
72    Buffer(vk::Handle, Alloc),
73    Image(vk::Handle, Option<Alloc>),
74    View(vk::Handle),
75    Sampler(vk::Handle),
76    Shader(vk::Handle),
77    SetLayout(vk::Handle),
78    PipeLayout(vk::Handle),
79    Pipeline(vk::Handle),
80    Set(vk::Handle, vk::Handle),
81    Pool(vk::Handle),
82    Query(vk::Handle),
83    #[cfg_attr(jfgui_glibc, allow(dead_code))] // (vk_swap.rs)
84    Semaphore(vk::Handle),
85    #[cfg_attr(jfgui_glibc, allow(dead_code))]
86    Swapchain(vk::Handle),
87    #[cfg_attr(not(jfgui_glibc), allow(dead_code))] // (vk_dmabuf.rs: an image's dedicated allocation)
88    Memory(vk::Handle),
89}
90
91/// a command pool with its one command buffer (of the compute queue's family, or the device queue's)
92pub(crate) struct Slot {
93    pub pool: vk::Handle,
94    pub cb: vk::CommandBuffer,
95    pub compute: bool,
96}
97
98/// (G5d) the bit of a SubmitId of the compute queue (its timeline's value below it)
99pub(crate) const COMPUTE_ID: u64 = 1 << 63;
100
101/// (G5d) the compute queue: its family, its timeline, what was submitted and is known done
102pub(crate) struct Lane {
103    pub queue: vk::Queue,
104    pub family: u32,
105    pub timeline: vk::Handle,
106    pub ts_bits: u32,
107    submitted: AtomicU64,
108    completed: AtomicU64,
109    qlock: SpinLock<()>,
110}
111
112/// a buffer of the staging ring
113struct Chunk {
114    buf: vk::Handle,
115    a: Alloc,
116    used: u64, // bytes used, front to back
117}
118
119/// the pending staged uploads: the command buffer, the chunks full since the last submission
120/// that it reads from
121struct Upload {
122    slot: Slot,
123    retired: Vec<Chunk>,
124}
125
126/// the staging ring
127#[derive(Default)]
128struct Ring {
129    cur: Option<Chunk>, // the chunk being filled (what is behind `used` stays free across submissions)
130    cur_value: u64,     // the last submission that reads from it (0: none)
131    cur_open: bool,     // the open upload command buffer reads from it
132    busy: Vec<(u64, Chunk)>, // retired: free once the GPU reached the value
133    free: Vec<Chunk>,
134    big_at: u64,        // plat::time_us of the last upload above half a chunk
135    big_size: u64,      // its bytes
136}
137
138pub(crate) struct State {
139    pub mem: Allocator,
140    /// (the device queue's value, the compute queue's)
141    garbage: Vec<(u64, u64, Garbage)>,
142    free: Vec<Slot>,
143    /// (G5d) free slots of the compute queue's family
144    cfree: Vec<Slot>,
145    /// (a SubmitId's value: COMPUTE_ID for the compute queue)
146    busy: Vec<(u64, Slot)>,
147    pools: Vec<vk::Handle>, // descriptor pools of the bind groups (the last one is filled)
148    upload: Option<Upload>,
149    ring: Ring,
150}
151
152/// a chunk of the staging ring
153const CHUNK: u64 = 256 << 10;
154/// a large chunk stays while large uploads need at least this part of it
155const BIG_KEEP_RATIO: u64 = 8;
156/// free chunks kept beyond the one being filled
157const CHUNKS_FREE: usize = 2;
158/// an upload above half a chunk keeps its buffer this long after the last one (us)
159const BIG_KEEP_US: u64 = 1_000_000;
160
161pub(crate) struct Shared {
162    pub inst: Arc<InstShared>,
163    pub pd: vk::PhysicalDevice,
164    pub dev: vk::Device,
165    pub f: vk::DeviceFns,
166    pub queue: vk::Queue,
167    pub family: u32,
168    pub timeline: vk::Handle,
169    pub info: AdapterInfo,
170    pub features: Features,
171    pub limits: Limits,
172    pub level: Level,
173    pub raw: vk::PhysicalDeviceLimits,
174    pub ts_mask: u64,
175    #[cfg_attr(jfgui_glibc, allow(dead_code))] // (vk_swap.rs)
176    pub swapchain: bool,
177    /// images for a window go to VK_QUEUE_FAMILY_FOREIGN_EXT (vk_dmabuf.rs)
178    #[cfg_attr(not(jfgui_glibc), allow(dead_code))]
179    pub queue_foreign: bool,
180    /// the first timestamp tick read on the device: Timestamps::read_ms counts from it
181    pub ts_base: AtomicU64,
182    /// vkResetQueryPool from the host (hostQueryReset enabled)
183    pub host_query_reset: bool,
184    /// maintenance4 enabled (Vulkan 1.3): shaders may set their workgroup size with LocalSizeId
185    pub maintenance4: bool,
186    /// (G5d) the compute queue
187    pub lane: Option<Lane>,
188    lost: AtomicBool,
189    submitted: AtomicU64,
190    completed: AtomicU64,
191    qlock: SpinLock<()>,
192    pub st: SpinLock<State>,
193}
194
195/// a logical GPU device: creates resources, records commands, owns the queue
196#[derive(Clone)]
197pub struct Device {
198    pub(crate) sh: Arc<Shared>,
199    queue: Queue,
200    compute: Option<ComputeQueue>,
201}
202
203/// (G5d) a queue of its own for compute shaders and copies, beside the device's queue (Device::compute_queue): its
204/// encoders record no render passes, its submissions run in parallel with the device queue's unless one waits for the
205/// other (submit_after)
206#[derive(Clone)]
207pub struct ComputeQueue {
208    pub(crate) sh: Arc<Shared>,
209}
210
211/// the device's queue (graphics, compute, copies, presentation)
212#[derive(Clone)]
213pub struct Queue {
214    pub(crate) sh: Arc<Shared>,
215}
216
217// features enabled when present (indices into the VkBool32 arrays)
218const E10: &[usize] = &[
219    vk::F10_FULL_DRAW_INDEX_UINT32,
220    vk::F10_IMAGE_CUBE_ARRAY,
221    vk::F10_INDEPENDENT_BLEND,
222    vk::F10_GEOMETRY_SHADER,
223    vk::F10_SAMPLE_RATE_SHADING,
224    vk::F10_DUAL_SRC_BLEND,
225    vk::F10_MULTI_DRAW_INDIRECT,
226    vk::F10_DRAW_INDIRECT_FIRST_INSTANCE,
227    vk::F10_DEPTH_CLAMP,
228    vk::F10_FILL_MODE_NON_SOLID,
229    vk::F10_SAMPLER_ANISOTROPY,
230    vk::F10_TEXTURE_COMPRESSION_BC,
231    vk::F10_VERTEX_PIPELINE_STORES_AND_ATOMICS,
232    vk::F10_FRAGMENT_STORES_AND_ATOMICS,
233    vk::F10_SHADER_STORAGE_IMAGE_EXTENDED_FORMATS,
234    vk::F10_SHADER_STORAGE_IMAGE_READ_WITHOUT_FORMAT,
235    vk::F10_SHADER_STORAGE_IMAGE_WRITE_WITHOUT_FORMAT,
236    vk::F10_SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING,
237    vk::F10_SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING,
238    vk::F10_SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING,
239    vk::F10_SHADER_STORAGE_IMAGE_ARRAY_DYNAMIC_INDEXING,
240    vk::F10_SHADER_CLIP_DISTANCE,
241    vk::F10_SHADER_FLOAT64,
242    vk::F10_SHADER_INT64,
243    vk::F10_SHADER_INT16,
244];
245const E11: &[usize] = &[
246    vk::F11_STORAGE_BUFFER_16BIT_ACCESS,
247    vk::F11_UNIFORM_AND_STORAGE_BUFFER_16BIT_ACCESS,
248    vk::F11_STORAGE_PUSH_CONSTANT16,
249    vk::F11_SHADER_DRAW_PARAMETERS,
250];
251const E12: &[usize] = &[
252    vk::F12_STORAGE_BUFFER_8BIT_ACCESS,
253    vk::F12_SHADER_FLOAT16,
254    vk::F12_SHADER_INT8,
255    vk::F12_SCALAR_BLOCK_LAYOUT,
256    vk::F12_UNIFORM_BUFFER_STANDARD_LAYOUT,
257    vk::F12_SHADER_SUBGROUP_EXTENDED_TYPES,
258    vk::F12_HOST_QUERY_RESET,
259    vk::F12_TIMELINE_SEMAPHORE,
260    vk::F12_BUFFER_DEVICE_ADDRESS,
261    vk::F12_VULKAN_MEMORY_MODEL,
262    vk::F12_VULKAN_MEMORY_MODEL_DEVICE_SCOPE,
263];
264const E12_VOLL: &[usize] = &[
265    vk::F12_DRAW_INDIRECT_COUNT,
266    vk::F12_DESCRIPTOR_INDEXING,
267    vk::F12_SHADER_SAMPLED_IMAGE_ARRAY_NON_UNIFORM_INDEXING,
268    vk::F12_SHADER_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
269    vk::F12_SHADER_STORAGE_IMAGE_ARRAY_NON_UNIFORM_INDEXING,
270    vk::F12_DESCRIPTOR_BINDING_SAMPLED_IMAGE_UPDATE_AFTER_BIND,
271    vk::F12_DESCRIPTOR_BINDING_STORAGE_IMAGE_UPDATE_AFTER_BIND,
272    vk::F12_DESCRIPTOR_BINDING_STORAGE_BUFFER_UPDATE_AFTER_BIND,
273    vk::F12_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING,
274    vk::F12_DESCRIPTOR_BINDING_PARTIALLY_BOUND,
275    vk::F12_RUNTIME_DESCRIPTOR_ARRAY,
276];
277const E13: &[usize] = &[
278    vk::F13_SHADER_DEMOTE_TO_HELPER_INVOCATION,
279    vk::F13_SUBGROUP_SIZE_CONTROL,
280    vk::F13_SYNCHRONIZATION2,
281    vk::F13_DYNAMIC_RENDERING,
282    vk::F13_MAINTENANCE4,
283];
284
285fn pick<const N: usize>(have: &[u32; N], want: &[usize]) -> [u32; N] {
286    let mut out = [0u32; N];
287    for &i in want {
288        out[i] = have[i];
289    }
290    out
291}
292
293impl Adapter {
294    /// a device of this adapter (level: the highest up to desc.max_level), with one queue
295    pub fn create_device(&self, desc: &DeviceDesc) -> Result<Device> {
296        self.create_device_with(desc, &DeviceOptions::DEFAULT)
297    }
298
299    /// (G5d) a device of this adapter with options: a compute queue of its own (Device::compute_queue) where the
300    /// adapter has a family without graphics and jfgui_GPU_COMPUTE is not 0
301    pub fn create_device_with(&self, desc: &DeviceDesc, opts: &DeviceOptions) -> Result<Device> {
302        if let Some(why) = self.info.missing {
303            return Err(GpuError::Unsupported(why));
304        }
305        let inst = &self.inst;
306        let level = if self.voll && desc.max_level == Level::Voll { Level::Voll } else { Level::Basis };
307        let voll = level == Level::Voll;
308        let v12 = self.api >= vk::API_VERSION_1_2;
309        let v13 = self.api >= vk::API_VERSION_1_3;
310        let mut e10 = pick(&self.f10, E10);
311        if !voll {
312            e10[vk::F10_MULTI_DRAW_INDIRECT] = 0;
313        }
314        let e11 = pick(&self.f11, E11);
315        let mut e12 = pick(&self.f12, E12);
316        // with the Vulkan memory model on, Device-scope atomics (any shader's, also under the
317        // GLSL450 model) need its device scope (VUID-RuntimeSpirv-vulkanMemoryModel-06265)
318        if e12[vk::F12_VULKAN_MEMORY_MODEL_DEVICE_SCOPE] == 0 {
319            e12[vk::F12_VULKAN_MEMORY_MODEL] = 0;
320        }
321        if voll {
322            for &i in E12_VOLL {
323                e12[i] = self.f12[i];
324            }
325        }
326        let e13 = pick(&self.f13, E13);
327        let mut f13: vk::PhysicalDeviceVulkan13Features = vk::Zero::zero();
328        f13.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
329        f13.f = e13;
330        let mut f12: vk::PhysicalDeviceVulkan12Features = vk::Zero::zero();
331        f12.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
332        f12.f = e12;
333        let mut f11: vk::PhysicalDeviceVulkan11Features = vk::Zero::zero();
334        f11.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
335        f11.f = e11;
336        let mut dynr: vk::PhysicalDeviceOneFeature = vk::Zero::zero();
337        dynr.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES;
338        dynr.f = 1;
339        let mut tl: vk::PhysicalDeviceOneFeature = vk::Zero::zero();
340        tl.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES;
341        tl.f = 1;
342        let mut f2: vk::PhysicalDeviceFeatures2 = vk::Zero::zero();
343        f2.s_type = vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
344        f2.f = e10;
345        let mut names: Vec<*const u8> = Vec::new();
346        // the chain is linked from its tail: no structure is written after a pointer to it exists
347        if v12 {
348            if v13 {
349                f12.p_next = &mut f13 as *mut _ as *mut c_void;
350            } else {
351                f12.p_next = &mut dynr as *mut _ as *mut c_void;
352                names.push(b"VK_KHR_dynamic_rendering\0".as_ptr());
353            }
354            f11.p_next = &mut f12 as *mut _ as *mut c_void;
355            f2.p_next = &mut f11 as *mut _ as *mut c_void;
356        } else {
357            // Vulkan 1.1: the extensions and their dependencies
358            tl.p_next = &mut dynr as *mut _ as *mut c_void;
359            f2.p_next = &mut tl as *mut _ as *mut c_void;
360            names.push(b"VK_KHR_timeline_semaphore\0".as_ptr());
361            names.push(b"VK_KHR_create_renderpass2\0".as_ptr());
362            names.push(b"VK_KHR_depth_stencil_resolve\0".as_ptr());
363            names.push(b"VK_KHR_dynamic_rendering\0".as_ptr());
364        }
365        let swapchain = self.exts.swapchain;
366        if swapchain {
367            names.push(b"VK_KHR_swapchain\0".as_ptr());
368        }
369        let dmabuf = self.exts.dmabuf;
370        if dmabuf {
371            names.push(b"VK_KHR_external_memory_fd\0".as_ptr());
372            names.push(b"VK_EXT_external_memory_dma_buf\0".as_ptr());
373            names.push(b"VK_EXT_image_drm_format_modifier\0".as_ptr());
374            if !v12 {
375                names.push(b"VK_KHR_image_format_list\0".as_ptr());
376            }
377        }
378        let queue_foreign = dmabuf && self.exts.queue_foreign;
379        if queue_foreign {
380            names.push(b"VK_EXT_queue_family_foreign\0".as_ptr());
381        }
382        let prio = 1.0f32;
383        let qi = vk::DeviceQueueCreateInfo {
384            s_type: vk::STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
385            queue_family_index: self.family,
386            queue_count: 1,
387            p_queue_priorities: &prio,
388            ..vk::Zero::zero()
389        };
390        // (G5d) the compute queue: a family of its own when asked for, unless jfgui_GPU_COMPUTE=0
391        let off = plat::env("jfgui_GPU_COMPUTE").is_some_and(|v| v == "0" || v.eq_ignore_ascii_case("off"));
392        let cfam = self.compute_family.filter(|_| opts.compute_queue && !off);
393        let qis = [qi, vk::DeviceQueueCreateInfo { queue_family_index: cfam.map_or(0, |c| c.0), ..qi }];
394        let info = vk::DeviceCreateInfo {
395            s_type: vk::STRUCTURE_TYPE_DEVICE_CREATE_INFO,
396            p_next: &f2 as *const _ as *const c_void,
397            queue_create_info_count: 1 + cfam.is_some() as u32,
398            p_queue_create_infos: qis.as_ptr(),
399            enabled_extension_count: names.len() as u32,
400            pp_enabled_extension_names: names.as_ptr(),
401            ..vk::Zero::zero()
402        };
403        let mut dev: vk::Device = 0;
404        // SAFETY: the chain and the arrays live until the call returns.
405        let r = unsafe { (inst.f.create_device)(self.pd, &info, core::ptr::null(), &mut dev) };
406        if r != vk::SUCCESS {
407            return Err(err(r));
408        }
409        let gdpa = inst.f.get_device_proc_addr;
410        // SAFETY: names are NUL-terminated; dev is the device just created.
411        let f = match vk::DeviceFns::load(&|n| unsafe { gdpa(dev, n.as_ptr()) }) {
412            Ok(f) => f,
413            Err(name) => {
414                // SAFETY: vkDestroyDevice of the fresh device (nothing created from it).
415                let d = unsafe { gdpa(dev, b"vkDestroyDevice\0".as_ptr()) };
416                if d != 0 {
417                    // SAFETY: vkDestroyDevice's prototype.
418                    unsafe {
419                        core::mem::transmute::<usize, unsafe extern "system" fn(vk::Device, *const c_void)>(d)(
420                            dev,
421                            core::ptr::null(),
422                        )
423                    };
424                }
425                return Err(GpuError::Unsupported(name));
426            }
427        };
428        let mut queue: vk::Queue = 0;
429        // SAFETY: family / index 0 were requested above.
430        unsafe { (f.get_device_queue)(dev, self.family, 0, &mut queue) };
431        let ti = vk::SemaphoreTypeCreateInfo {
432            s_type: vk::STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
433            semaphore_type: vk::SEMAPHORE_TYPE_TIMELINE,
434            initial_value: 0,
435            ..vk::Zero::zero()
436        };
437        let si = vk::SemaphoreCreateInfo {
438            s_type: vk::STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
439            p_next: &ti as *const _ as *const c_void,
440            flags: 0,
441        };
442        let mut timeline = 0;
443        // SAFETY: valid create info.
444        let r = unsafe { (f.create_semaphore)(dev, &si, core::ptr::null(), &mut timeline) };
445        if r != vk::SUCCESS {
446            // SAFETY: nothing else was created from the device.
447            unsafe { (f.destroy_device)(dev, core::ptr::null()) };
448            return Err(err(r));
449        }
450        let mut lane = None;
451        if let Some((family, ts_bits)) = cfam {
452            let mut cq: vk::Queue = 0;
453            let mut ct = 0;
454            // SAFETY: the family / index 0 requested above; a valid create info.
455            let r = unsafe {
456                (f.get_device_queue)(dev, family, 0, &mut cq);
457                (f.create_semaphore)(dev, &si, core::ptr::null(), &mut ct)
458            };
459            if r != vk::SUCCESS {
460                // SAFETY: only the timeline was created from the device.
461                unsafe {
462                    (f.destroy_semaphore)(dev, timeline, core::ptr::null());
463                    (f.destroy_device)(dev, core::ptr::null());
464                }
465                return Err(err(r));
466            }
467            lane = Some(Lane { queue: cq, family, timeline: ct, ts_bits, submitted: AtomicU64::new(0), completed: AtomicU64::new(0), qlock: SpinLock::new(()) });
468        }
469        let mut features = self.features;
470        if !voll {
471            features.bindless = false;
472            features.draw_indirect_count = false;
473            features.multi_draw_indirect = false;
474        }
475        let mut limits = self.limits;
476        if !voll {
477            limits.max_bindless_textures = 0;
478            limits.max_bindless_samplers = 0;
479            limits.max_bindless_buffers = 0;
480            limits.max_bindless_storage_textures = 0;
481        }
482        let ts_mask = match self.ts_bits {
483            0 => 0,
484            64.. => !0,
485            b => (1u64 << b) - 1,
486        };
487        let sh = Arc::new(Shared {
488            inst: inst.clone(),
489            pd: self.pd,
490            dev,
491            f,
492            queue,
493            family: self.family,
494            timeline,
495            info: self.info.clone(),
496            features,
497            limits,
498            level,
499            raw: self.raw,
500            ts_mask,
501            swapchain,
502            queue_foreign,
503            ts_base: AtomicU64::new(0),
504            host_query_reset: v12 && e12[vk::F12_HOST_QUERY_RESET] != 0,
505            maintenance4: v13 && e13[vk::F13_MAINTENANCE4] != 0,
506            lane,
507            lost: AtomicBool::new(false),
508            submitted: AtomicU64::new(0),
509            completed: AtomicU64::new(0),
510            qlock: SpinLock::new(()),
511            st: SpinLock::new(State {
512                mem: Allocator::new(self.mem),
513                garbage: Vec::new(),
514                free: Vec::new(),
515                cfree: Vec::new(),
516                busy: Vec::new(),
517                pools: Vec::new(),
518                upload: None,
519                ring: Ring::default(),
520            }),
521        });
522        let compute = sh.lane.as_ref().map(|_| ComputeQueue { sh: sh.clone() });
523        Ok(Device { queue: Queue { sh: sh.clone() }, compute, sh })
524    }
525}
526
527impl Shared {
528    /// Err(DeviceLost) once the device is lost
529    #[inline]
530    pub fn check(&self) -> Result<()> {
531        if self.lost.load(Ordering::Relaxed) { Err(GpuError::DeviceLost) } else { Ok(()) }
532    }
533
534    /// a VkResult as Result (DEVICE_LOST marks the device)
535    pub fn vk(&self, r: vk::VkResult) -> Result<()> {
536        if r == vk::SUCCESS {
537            return Ok(());
538        }
539        if r == vk::ERROR_DEVICE_LOST {
540            self.lost.store(true, Ordering::Relaxed);
541        }
542        Err(err(r))
543    }
544
545    /// run f with the queue held (vkQueuePresentKHR and vkQueueSubmit need it alone)
546    #[cfg_attr(jfgui_glibc, allow(dead_code))]
547    pub fn with_queue<R>(&self, f: impl FnOnce(vk::Queue) -> R) -> R {
548        let _q = self.qlock.lock();
549        f(self.queue)
550    }
551
552    /// wait until the queue has finished everything, presents included (vkQueueWaitIdle;
553    /// also on a lost device, where it returns at once)
554    #[cfg_attr(jfgui_glibc, allow(dead_code))]
555    pub fn queue_idle(&self) -> Result<()> {
556        // the value is read while the queue is held: nothing is submitted until the wait ends
557        // SAFETY: the queue is held while the driver waits on it.
558        let (r, v) = self.with_queue(|q| (unsafe { (self.f.queue_wait_idle)(q) }, self.submitted()));
559        self.vk(r)?;
560        self.completed.fetch_max(v, Ordering::AcqRel);
561        let mut st = self.st.lock();
562        self.collect(&mut st);
563        Ok(())
564    }
565
566    pub fn mark_lost(&self) {
567        self.lost.store(true, Ordering::Relaxed);
568    }
569
570    pub fn submitted(&self) -> u64 {
571        self.submitted.load(Ordering::Acquire)
572    }
573
574    /// (G5d) the compute queue's last submitted value (0 without one)
575    fn lane_submitted(&self) -> u64 {
576        self.lane.as_ref().map_or(0, |l| l.submitted.load(Ordering::Acquire))
577    }
578
579    /// (G5d) the compute queue's value the GPU has reached (asks the driver; 0 without one)
580    fn lane_refresh(&self) -> u64 {
581        let Some(l) = &self.lane else { return 0 };
582        // (nothing pending: no call; a device that never uses the queue never asks its timeline)
583        let done = l.completed.load(Ordering::Acquire);
584        if l.submitted.load(Ordering::Acquire) <= done {
585            return done;
586        }
587        let mut v = 0u64;
588        // SAFETY: the compute queue's timeline semaphore.
589        let r = unsafe { (self.f.get_semaphore_counter_value)(self.dev, l.timeline, &mut v) };
590        if r != vk::SUCCESS {
591            if r == vk::ERROR_DEVICE_LOST {
592                self.lost.store(true, Ordering::Relaxed);
593            }
594            return l.completed.load(Ordering::Acquire);
595        }
596        l.completed.fetch_max(v, Ordering::AcqRel);
597        v
598    }
599
600    /// the timeline value the GPU has reached (asks the driver)
601    pub fn refresh(&self) -> u64 {
602        let mut v = 0u64;
603        // SAFETY: the device's timeline semaphore.
604        let r = unsafe { (self.f.get_semaphore_counter_value)(self.dev, self.timeline, &mut v) };
605        if r != vk::SUCCESS {
606            if r == vk::ERROR_DEVICE_LOST {
607                self.lost.store(true, Ordering::Relaxed);
608            }
609            return self.completed.load(Ordering::Acquire);
610        }
611        self.completed.fetch_max(v, Ordering::AcqRel);
612        v
613    }
614
615    /// destroy g once the GPU has finished every submission made so far; a buffer or image also
616    /// the open upload buffer, whose copies may name it (a texture written and dropped before the
617    /// next submit).  Nothing else waits for that: a swapchain must go before its surface, which
618    /// its drop releases right after this
619    pub fn defer(&self, g: Garbage) {
620        let mut st = self.st.lock();
621        let named = st.upload.is_some() && matches!(g, Garbage::Buffer(..) | Garbage::Image(..));
622        let v = self.submitted() + named as u64;
623        // (G5d) and the compute queue's latest submission
624        let vc = self.lane_submitted();
625        let lane_done = self.lane.as_ref().is_none_or(|l| vc <= l.completed.load(Ordering::Acquire));
626        if (v <= self.completed.load(Ordering::Acquire) && lane_done) || self.lost.load(Ordering::Relaxed) {
627            self.destroy(&mut st, g);
628        } else {
629            st.garbage.push((v, vc, g));
630        }
631    }
632
633    fn destroy(&self, st: &mut State, g: Garbage) {
634        let (f, d, n) = (&self.f, self.dev, core::ptr::null());
635        // SAFETY: the GPU no longer uses the object (its timeline value is reached, or no
636        // submission is pending, or the device is lost); each handle is destroyed once.
637        unsafe {
638            match g {
639                Garbage::Buffer(h, a) => {
640                    (f.destroy_buffer)(d, h, n);
641                    st.mem.free(f, d, &a);
642                }
643                Garbage::Image(h, a) => {
644                    (f.destroy_image)(d, h, n);
645                    if let Some(a) = a {
646                        st.mem.free(f, d, &a);
647                    }
648                }
649                Garbage::View(h) => (f.destroy_image_view)(d, h, n),
650                Garbage::Sampler(h) => (f.destroy_sampler)(d, h, n),
651                Garbage::Shader(h) => (f.destroy_shader_module)(d, h, n),
652                Garbage::SetLayout(h) => (f.destroy_descriptor_set_layout)(d, h, n),
653                Garbage::PipeLayout(h) => (f.destroy_pipeline_layout)(d, h, n),
654                Garbage::Pipeline(h) => (f.destroy_pipeline)(d, h, n),
655                Garbage::Set(pool, set) => {
656                    (f.free_descriptor_sets)(d, pool, 1, &set);
657                }
658                Garbage::Pool(h) => (f.destroy_descriptor_pool)(d, h, n),
659                Garbage::Query(h) => (f.destroy_query_pool)(d, h, n),
660                Garbage::Semaphore(h) => (f.destroy_semaphore)(d, h, n),
661                Garbage::Swapchain(h) => {
662                    if let Some(ds) = f.destroy_swapchain_khr {
663                        ds(d, h, n);
664                    }
665                }
666                Garbage::Memory(h) => (f.free_memory)(d, h, n),
667            }
668        }
669    }
670
671    /// free the garbage and the command slots whose submissions completed
672    fn collect(&self, st: &mut State) {
673        if st.garbage.is_empty() && st.busy.is_empty() && st.ring.busy.is_empty() && st.ring.free.is_empty() {
674            return;
675        }
676        let lost = self.lost.load(Ordering::Relaxed);
677        let done = if lost { u64::MAX } else { self.refresh() };
678        let done_c = if lost { u64::MAX } else { self.lane_refresh() };
679        let mut i = 0;
680        while i < st.ring.busy.len() {
681            if st.ring.busy[i].0 <= done {
682                let (_, mut c) = st.ring.busy.swap_remove(i);
683                c.used = 0;
684                st.ring.free.push(c);
685            } else {
686                i += 1;
687            }
688        }
689        self.ring_trim(st);
690        let mut i = 0;
691        while i < st.busy.len() {
692            let v = st.busy[i].0;
693            let finished = if v & COMPUTE_ID != 0 { v & !COMPUTE_ID <= done_c } else { v <= done };
694            if finished {
695                let (_, s) = st.busy.swap_remove(i);
696                // SAFETY: the pool's command buffer finished executing.
697                unsafe { (self.f.reset_command_pool)(self.dev, s.pool, 0) };
698                if s.compute { st.cfree.push(s) } else { st.free.push(s) }
699            } else {
700                i += 1;
701            }
702        }
703        let mut i = 0;
704        while i < st.garbage.len() {
705            if st.garbage[i].0 <= done && st.garbage[i].1 <= done_c {
706                let (_, _, g) = st.garbage.swap_remove(i);
707                self.destroy(st, g);
708            } else {
709                i += 1;
710            }
711        }
712    }
713
714    /// a command buffer slot (reused or new), begun for one submission
715    #[cfg_attr(not(jfgui_glibc), allow(dead_code))] // (vk_dmabuf.rs)
716    pub fn take_slot(&self) -> Result<Slot> {
717        self.take_slot_for(false)
718    }
719
720    /// (G5d) a slot of the compute queue's family (compute) or the device queue's
721    pub fn take_slot_for(&self, compute: bool) -> Result<Slot> {
722        self.check()?;
723        if compute && self.lane.is_none() {
724            return Err(GpuError::Unsupported("a compute queue"));
725        }
726        let slot = {
727            let mut st = self.st.lock();
728            let empty = if compute { st.cfree.is_empty() } else { st.free.is_empty() };
729            if empty {
730                self.collect(&mut st);
731            }
732            if compute { st.cfree.pop() } else { st.free.pop() }
733        };
734        let slot = match slot {
735            Some(s) => s,
736            None => self.new_slot(compute)?,
737        };
738        let bi = vk::CommandBufferBeginInfo {
739            s_type: vk::STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
740            flags: vk::COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
741            ..vk::Zero::zero()
742        };
743        // SAFETY: the slot's buffer is in the initial state (new or its pool reset).
744        let r = unsafe { (self.f.begin_command_buffer)(slot.cb, &bi) };
745        if let Err(e) = self.vk(r) {
746            self.give_slot(slot);
747            return Err(e);
748        }
749        Ok(slot)
750    }
751
752    fn new_slot(&self, compute: bool) -> Result<Slot> {
753        let family = match (&self.lane, compute) {
754            (Some(l), true) => l.family,
755            _ => self.family,
756        };
757        let pi = vk::CommandPoolCreateInfo {
758            s_type: vk::STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
759            flags: vk::COMMAND_POOL_CREATE_TRANSIENT_BIT,
760            queue_family_index: family,
761            ..vk::Zero::zero()
762        };
763        let mut pool = 0;
764        // SAFETY: valid create info.
765        self.vk(unsafe { (self.f.create_command_pool)(self.dev, &pi, core::ptr::null(), &mut pool) })?;
766        let ai = vk::CommandBufferAllocateInfo {
767            s_type: vk::STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
768            command_pool: pool,
769            level: vk::COMMAND_BUFFER_LEVEL_PRIMARY,
770            command_buffer_count: 1,
771            ..vk::Zero::zero()
772        };
773        let mut cb = 0;
774        // SAFETY: one buffer from the new pool.
775        let r = unsafe { (self.f.allocate_command_buffers)(self.dev, &ai, &mut cb) };
776        if let Err(e) = self.vk(r) {
777            // SAFETY: the pool is unused.
778            unsafe { (self.f.destroy_command_pool)(self.dev, pool, core::ptr::null()) };
779            return Err(e);
780        }
781        Ok(Slot { pool, cb, compute })
782    }
783
784    /// a slot that was never submitted: reset, back to the free list
785    pub fn give_slot(&self, s: Slot) {
786        // SAFETY: the buffer is not pending (never submitted).
787        unsafe { (self.f.reset_command_pool)(self.dev, s.pool, 0) };
788        let mut st = self.st.lock();
789        if s.compute { st.cfree.push(s) } else { st.free.push(s) }
790    }
791
792    /// (G5d) the families a resource is shared by (concurrently), when there is a compute queue
793    pub fn sharing(&self) -> Option<[u32; 2]> {
794        self.lane.as_ref().map(|l| [self.family, l.family])
795    }
796
797    /// the free chunks beyond what the ring keeps go back: ordinary ones above CHUNKS_FREE,
798    /// large ones a second after the last large upload (all but the largest at once; the largest
799    /// too when the last large upload was far smaller: one big image's buffer does not stay
800    /// around for the frames after it)
801    fn ring_trim(&self, st: &mut State) {
802        let now = plat::time_us();
803        let keep_big = now.saturating_sub(st.ring.big_at) < BIG_KEEP_US;
804        let largest = st.ring.free.iter().map(|c| c.a.size).max().unwrap_or(0);
805        let mut small = 0;
806        let mut i = 0;
807        while i < st.ring.free.len() {
808            let size = st.ring.free[i].a.size;
809            let keep = if size >= CHUNK * 2 {
810                keep_big && size == largest && size <= st.ring.big_size.saturating_mul(BIG_KEEP_RATIO).max(CHUNK * 2)
811            } else {
812                small += 1;
813                small <= CHUNKS_FREE
814            };
815            if keep {
816                i += 1;
817            } else {
818                let c = st.ring.free.swap_remove(i);
819                self.destroy(st, Garbage::Buffer(c.buf, c.a));
820            }
821        }
822    }
823
824    /// a chunk of at least size bytes, reused or new (its `used` is 0); the ring's own chunks
825    /// (size = CHUNK) are never large ones, those would stay around
826    fn ring_chunk(&self, st: &mut State, size: u64) -> Result<Chunk> {
827        let top = if size == CHUNK { CHUNK * 2 } else { u64::MAX };
828        let best = st.ring.free.iter().enumerate().filter(|(_, c)| c.a.size >= size && c.a.size < top).min_by_key(|(_, c)| c.a.size).map(|(i, _)| i);
829        if let Some(i) = best {
830            return Ok(st.ring.free.swap_remove(i));
831        }
832        let (buf, a) = self.raw_buffer(st, size, vk::BUFFER_USAGE_TRANSFER_SRC_BIT, MemoryKind::Upload)?;
833        Ok(Chunk { buf, a, used: 0 })
834    }
835
836    /// the upload command buffer (begun when there is none): it runs first in the next submit
837    pub fn upload_cb(&self, st: &mut State) -> Result<vk::CommandBuffer> {
838        self.stage(st, 0)?;
839        Ok(st.upload.as_ref().map_or(0, |u| u.slot.cb))
840    }
841
842    /// staging space of n bytes for the upload command buffer -> (buffer, offset, address)
843    /// (n = 0: only the command buffer)
844    fn stage(&self, st: &mut State, n: u64) -> Result<(vk::Handle, u64, usize)> {
845        if st.upload.is_none() {
846            // the upload buffer's slot: from the free list or new (the lock is held: no collect)
847            let slot = match st.free.pop() {
848                Some(s) => s,
849                None => self.new_slot(false)?,
850            };
851            let bi = vk::CommandBufferBeginInfo {
852                s_type: vk::STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
853                flags: vk::COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
854                ..vk::Zero::zero()
855            };
856            // SAFETY: a slot in the initial state.
857            let r = unsafe { (self.f.begin_command_buffer)(slot.cb, &bi) };
858            if let Err(e) = self.vk(r) {
859                st.free.push(slot);
860                return Err(e);
861            }
862            st.upload = Some(Upload { slot, retired: Vec::new() });
863        }
864        if n == 0 {
865            return Ok((0, 0, 0));
866        }
867        if n > CHUNK / 2 {
868            // a buffer of its own: free once the next submission is done
869            st.ring.big_at = plat::time_us();
870            st.ring.big_size = n;
871            let size = (n + 0xFFFF) & !0xFFFF;
872            let mut c = self.ring_chunk(st, size.max(CHUNK * 2))?;
873            c.used = n;
874            let (buf, ptr) = (c.buf, c.a.ptr);
875            st.upload.as_mut().unwrap_or_else(|| unreachable!()).retired.push(c);
876            return Ok((buf, 0, ptr));
877        }
878        let room = st.ring.cur.as_ref().is_some_and(|c| c.used + n <= c.a.size);
879        if !room {
880            if let Some(c) = st.ring.cur.take() {
881                if st.ring.cur_open {
882                    st.upload.as_mut().unwrap_or_else(|| unreachable!()).retired.push(c);
883                } else {
884                    let v = st.ring.cur_value;
885                    st.ring.busy.push((v, c));
886                }
887            }
888            let c = self.ring_chunk(st, CHUNK)?;
889            st.ring.cur = Some(c);
890            st.ring.cur_open = false;
891        }
892        st.ring.cur_open = true;
893        let c = st.ring.cur.as_mut().unwrap_or_else(|| unreachable!());
894        let off = c.used;
895        c.used = (off + n + 15) & !15;
896        Ok((c.buf, off, c.a.ptr + off as usize))
897    }
898
899    /// a VkBuffer with memory of kind (bound) -> (buffer, allocation)
900    pub fn raw_buffer(&self, st: &mut State, size: u64, usage: u32, kind: MemoryKind) -> Result<(vk::Handle, Alloc)> {
901        // (G5d) every buffer is shared with the compute queue's family, if there is one
902        let fams = self.sharing();
903        let bi = vk::BufferCreateInfo {
904            s_type: vk::STRUCTURE_TYPE_BUFFER_CREATE_INFO,
905            size,
906            usage,
907            sharing_mode: if fams.is_some() { vk::SHARING_MODE_CONCURRENT } else { vk::SHARING_MODE_EXCLUSIVE },
908            queue_family_index_count: if fams.is_some() { 2 } else { 0 },
909            p_queue_family_indices: fams.as_ref().map_or(core::ptr::null(), |f| f.as_ptr()),
910            ..vk::Zero::zero()
911        };
912        let mut buf = 0;
913        // SAFETY: valid create info.
914        self.vk(unsafe { (self.f.create_buffer)(self.dev, &bi, core::ptr::null(), &mut buf) })?;
915        let mut req: vk::MemoryRequirements = vk::Zero::zero();
916        // SAFETY: the buffer just created.
917        unsafe { (self.f.get_buffer_memory_requirements)(self.dev, buf, &mut req) };
918        let a = match st.mem.alloc(&self.f, self.dev, &req, kind, true) {
919            Ok(a) => a,
920            Err(e) => {
921                // SAFETY: unbound, unused buffer.
922                unsafe { (self.f.destroy_buffer)(self.dev, buf, core::ptr::null()) };
923                return Err(e);
924            }
925        };
926        // SAFETY: the allocation satisfies the buffer's requirements.
927        let r = unsafe { (self.f.bind_buffer_memory)(self.dev, buf, a.mem, a.offset) };
928        if let Err(e) = self.vk(r) {
929            // SAFETY: as above.
930            unsafe { (self.f.destroy_buffer)(self.dev, buf, core::ptr::null()) };
931            st.mem.free(&self.f, self.dev, &a);
932            return Err(e);
933        }
934        Ok((buf, a))
935    }
936
937    /// submit command buffers (their slots), after the pending uploads; wait: a binary
938    /// semaphore and its stages, signal: a binary semaphore -> the timeline value
939    pub fn submit(&self, cbs: &[vk::CommandBuffer], slots: Vec<Slot>, wait: Option<(vk::Handle, u32)>,
940                  signal: Option<vk::Handle>) -> Result<u64> {
941        self.submit_on(false, cbs, slots, wait, &[], signal)
942    }
943
944    /// (G5d) the semaphore and value a SubmitId waits on; Invalid when it was not submitted yet
945    fn wait_of(&self, id: u64) -> Result<(vk::Handle, u64)> {
946        if id & COMPUTE_ID == 0 {
947            return if id <= self.submitted() { Ok((self.timeline, id)) } else { Err(GpuError::Invalid("submit_after: a submission not made yet")) };
948        }
949        match &self.lane {
950            Some(l) if id & !COMPUTE_ID <= l.submitted.load(Ordering::Acquire) => Ok((l.timeline, id & !COMPUTE_ID)),
951            Some(_) => Err(GpuError::Invalid("submit_after: a submission not made yet")),
952            None => Err(GpuError::Invalid("submit_after: an id of a compute queue the device does not have")),
953        }
954    }
955
956    /// submit on the device's queue (compute false: after the pending uploads) or on the compute queue (the pending
957    /// uploads go first on the device's queue, and it waits for them); wait: a binary semaphore and its stages,
958    /// after: SubmitIds of either queue it waits for (at all commands), signal: a binary semaphore -> the SubmitId
959    /// (COMPUTE_ID on the compute queue)
960    pub fn submit_on(&self, compute: bool, cbs: &[vk::CommandBuffer], slots: Vec<Slot>, wait: Option<(vk::Handle, u32)>, after: &[u64],
961                     signal: Option<vk::Handle>) -> Result<u64> {
962        let mut slots = slots;
963        let fine = self.check().and_then(|_| {
964            if slots.iter().any(|s| s.compute != compute) {
965                return Err(GpuError::Invalid("submit: a command buffer of the other queue's family (Device::encoder / ComputeQueue::encoder)"));
966            }
967            if compute && (self.lane.is_none() || wait.is_some() || signal.is_some()) {
968                return Err(GpuError::Invalid("submit: no compute queue, or a semaphore on it"));
969            }
970            Ok(())
971        });
972        let mut waits: Vec<(vk::Handle, u64)> = Vec::with_capacity(after.len() + 1);
973        let fine = fine.and_then(|_| {
974            for &id in after {
975                waits.push(self.wait_of(id)?);
976            }
977            Ok(())
978        });
979        if let Err(e) = fine {
980            for s in slots {
981                self.give_slot(s);
982            }
983            return Err(e);
984        }
985        if compute {
986            return self.submit_lane(cbs, slots, waits);
987        }
988        let up = self.st.lock().upload.take();
989        let mut all: Vec<vk::CommandBuffer> = Vec::with_capacity(cbs.len() + 1);
990        let mut chunks: Vec<Chunk> = Vec::new();
991        let uploads = up.is_some();
992        if let Some(u) = up {
993            let mb = vk::MemoryBarrier {
994                s_type: vk::STRUCTURE_TYPE_MEMORY_BARRIER,
995                src_access_mask: vk::ACCESS_TRANSFER_WRITE_BIT,
996                dst_access_mask: vk::ACCESS_MEMORY_READ_BIT | vk::ACCESS_MEMORY_WRITE_BIT,
997                ..vk::Zero::zero()
998            };
999            // SAFETY: the upload buffer is recording; its copies are done before what follows.
1000            unsafe {
1001                (self.f.cmd_pipeline_barrier)(u.slot.cb, vk::PIPELINE_STAGE_TRANSFER_BIT,
1002                    vk::PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1, &mb, 0, core::ptr::null(), 0, core::ptr::null());
1003            }
1004            // SAFETY: as above.
1005            let r = unsafe { (self.f.end_command_buffer)(u.slot.cb) };
1006            all.push(u.slot.cb);
1007            slots.push(u.slot);
1008            chunks = u.retired;
1009            if let Err(e) = self.vk(r) {
1010                self.fail_submit(slots, chunks);
1011                return Err(e);
1012            }
1013        }
1014        all.extend_from_slice(cbs);
1015        // the binary semaphore (its value is ignored) and the timelines it waits for
1016        let mut ws: Vec<vk::Handle> = Vec::with_capacity(waits.len() + 1);
1017        let mut wv: Vec<u64> = Vec::with_capacity(waits.len() + 1);
1018        let mut wst: Vec<u32> = Vec::with_capacity(waits.len() + 1);
1019        if let Some((s, st)) = wait {
1020            ws.push(s);
1021            wv.push(0);
1022            wst.push(st);
1023        }
1024        for &(s, v) in &waits {
1025            ws.push(s);
1026            wv.push(v);
1027            wst.push(vk::PIPELINE_STAGE_ALL_COMMANDS_BIT);
1028        }
1029        let q = self.qlock.lock();
1030        let value = self.submitted.load(Ordering::Relaxed) + 1;
1031        let sems = [self.timeline, signal.unwrap_or(0)];
1032        let values = [value, 0];
1033        let nsig = 1 + signal.is_some() as u32;
1034        let tsi = vk::TimelineSemaphoreSubmitInfo {
1035            s_type: vk::STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
1036            wait_semaphore_value_count: wv.len() as u32,
1037            p_wait_semaphore_values: wv.as_ptr(),
1038            signal_semaphore_value_count: nsig,
1039            p_signal_semaphore_values: values.as_ptr(),
1040            ..vk::Zero::zero()
1041        };
1042        let si = vk::SubmitInfo {
1043            s_type: vk::STRUCTURE_TYPE_SUBMIT_INFO,
1044            p_next: &tsi as *const _ as *const c_void,
1045            wait_semaphore_count: ws.len() as u32,
1046            p_wait_semaphores: ws.as_ptr(),
1047            p_wait_dst_stage_mask: wst.as_ptr(),
1048            command_buffer_count: all.len() as u32,
1049            p_command_buffers: all.as_ptr(),
1050            signal_semaphore_count: nsig,
1051            p_signal_semaphores: sems.as_ptr(),
1052        };
1053        // SAFETY: every command buffer is executable; the queue is held (qlock).
1054        let r = unsafe { (self.f.queue_submit)(self.queue, 1, &si, 0) };
1055        if r == vk::SUCCESS {
1056            self.submitted.store(value, Ordering::Release);
1057        }
1058        drop(q);
1059        if let Err(e) = self.vk(r) {
1060            self.fail_submit(slots, chunks);
1061            return Err(e);
1062        }
1063        let mut st = self.st.lock();
1064        for s in slots {
1065            st.busy.push((value, s));
1066        }
1067        for c in chunks {
1068            st.ring.busy.push((value, c));
1069        }
1070        if uploads && st.ring.cur_open {
1071            st.ring.cur_value = value;
1072            st.ring.cur_open = false;
1073        }
1074        self.collect(&mut st);
1075        Ok(value)
1076    }
1077
1078    /// (G5d) a submission on the compute queue (checked by submit_on): the pending uploads first on the device's
1079    /// queue, waited for
1080    fn submit_lane(&self, cbs: &[vk::CommandBuffer], slots: Vec<Slot>, mut waits: Vec<(vk::Handle, u64)>) -> Result<u64> {
1081        let Some(l) = &self.lane else { return Err(GpuError::Invalid("no compute queue")) };
1082        if self.st.lock().upload.is_some() {
1083            match self.submit_on(false, &[], Vec::new(), None, &[], None) {
1084                Ok(v) => waits.push((self.timeline, v)),
1085                Err(e) => {
1086                    for s in slots {
1087                        self.give_slot(s);
1088                    }
1089                    return Err(e);
1090                }
1091            }
1092        }
1093        let wv: Vec<u64> = waits.iter().map(|w| w.1).collect();
1094        let ws: Vec<vk::Handle> = waits.iter().map(|w| w.0).collect();
1095        let wst: Vec<u32> = ws.iter().map(|_| vk::PIPELINE_STAGE_ALL_COMMANDS_BIT).collect();
1096        let q = l.qlock.lock();
1097        let value = l.submitted.load(Ordering::Relaxed) + 1;
1098        let tsi = vk::TimelineSemaphoreSubmitInfo {
1099            s_type: vk::STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
1100            wait_semaphore_value_count: wv.len() as u32,
1101            p_wait_semaphore_values: wv.as_ptr(),
1102            signal_semaphore_value_count: 1,
1103            p_signal_semaphore_values: &value,
1104            ..vk::Zero::zero()
1105        };
1106        let si = vk::SubmitInfo {
1107            s_type: vk::STRUCTURE_TYPE_SUBMIT_INFO,
1108            p_next: &tsi as *const _ as *const c_void,
1109            wait_semaphore_count: ws.len() as u32,
1110            p_wait_semaphores: ws.as_ptr(),
1111            p_wait_dst_stage_mask: wst.as_ptr(),
1112            command_buffer_count: cbs.len() as u32,
1113            p_command_buffers: cbs.as_ptr(),
1114            signal_semaphore_count: 1,
1115            p_signal_semaphores: &l.timeline,
1116        };
1117        // SAFETY: every command buffer is executable and of the compute family; the queue is held.
1118        let r = unsafe { (self.f.queue_submit)(l.queue, 1, &si, 0) };
1119        if r == vk::SUCCESS {
1120            l.submitted.store(value, Ordering::Release);
1121        }
1122        drop(q);
1123        if let Err(e) = self.vk(r) {
1124            for s in slots {
1125                self.give_slot(s);
1126            }
1127            return Err(e);
1128        }
1129        let mut st = self.st.lock();
1130        for s in slots {
1131            st.busy.push((value | COMPUTE_ID, s));
1132        }
1133        self.collect(&mut st);
1134        Ok(value | COMPUTE_ID)
1135    }
1136
1137    /// (G5d) wait for a SubmitId of either queue -> false on timeout
1138    pub fn wait_id(&self, id: u64, timeout_ns: u64) -> Result<bool> {
1139        if id & COMPUTE_ID == 0 {
1140            return self.wait_value(id, timeout_ns);
1141        }
1142        self.check()?;
1143        let Some(l) = &self.lane else { return Err(GpuError::Invalid("wait: an id of a compute queue the device does not have")) };
1144        let v = id & !COMPUTE_ID;
1145        if v <= l.completed.load(Ordering::Acquire) {
1146            return Ok(true);
1147        }
1148        if v > l.submitted.load(Ordering::Acquire) {
1149            return Err(GpuError::Invalid("wait: not submitted yet"));
1150        }
1151        let wi = vk::SemaphoreWaitInfo {
1152            s_type: vk::STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
1153            semaphore_count: 1,
1154            p_semaphores: &l.timeline,
1155            p_values: &v,
1156            ..vk::Zero::zero()
1157        };
1158        // SAFETY: the compute queue's timeline semaphore and a value already submitted.
1159        let r = unsafe { (self.f.wait_semaphores)(self.dev, &wi, timeout_ns) };
1160        if r == vk::TIMEOUT {
1161            return Ok(false);
1162        }
1163        self.vk(r)?;
1164        l.completed.fetch_max(v, Ordering::AcqRel);
1165        let mut st = self.st.lock();
1166        self.collect(&mut st);
1167        Ok(true)
1168    }
1169
1170    fn fail_submit(&self, slots: Vec<Slot>, chunks: Vec<Chunk>) {
1171        for s in slots {
1172            self.give_slot(s);
1173        }
1174        let mut st = self.st.lock();
1175        st.ring.cur_open = false; // its copies never ran
1176        for c in chunks {
1177            self.destroy(&mut st, Garbage::Buffer(c.buf, c.a));
1178        }
1179    }
1180
1181    /// wait until the timeline reaches v -> false on timeout
1182    pub fn wait_value(&self, v: u64, timeout_ns: u64) -> Result<bool> {
1183        self.check()?;
1184        if v <= self.completed.load(Ordering::Acquire) {
1185            return Ok(true);
1186        }
1187        if v > self.submitted() {
1188            return Err(GpuError::Invalid("wait: not submitted yet"));
1189        }
1190        let wi = vk::SemaphoreWaitInfo {
1191            s_type: vk::STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
1192            semaphore_count: 1,
1193            p_semaphores: &self.timeline,
1194            p_values: &v,
1195            ..vk::Zero::zero()
1196        };
1197        // SAFETY: the device's timeline semaphore and a value already submitted (or later).
1198        let r = unsafe { (self.f.wait_semaphores)(self.dev, &wi, timeout_ns) };
1199        if r == vk::TIMEOUT {
1200            return Ok(false);
1201        }
1202        self.vk(r)?;
1203        self.completed.fetch_max(v, Ordering::AcqRel);
1204        let mut st = self.st.lock();
1205        self.collect(&mut st);
1206        Ok(true)
1207    }
1208
1209    /// bind-group descriptor sets: a set of layout from the pools (a new pool when all are full)
1210    pub fn alloc_set(&self, layout: vk::Handle, needs: &[vk::DescriptorPoolSize]) -> Result<(vk::Handle, vk::Handle)> {
1211        let mut st = self.st.lock();
1212        let alloc = |pool: vk::Handle| {
1213            let ai = vk::DescriptorSetAllocateInfo {
1214                s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
1215                descriptor_pool: pool,
1216                descriptor_set_count: 1,
1217                p_set_layouts: &layout,
1218                ..vk::Zero::zero()
1219            };
1220            let mut set = 0;
1221            // SAFETY: the pool belongs to this device; the state lock serialises pool use.
1222            let r = unsafe { (self.f.allocate_descriptor_sets)(self.dev, &ai, &mut set) };
1223            (r, set)
1224        };
1225        for &pool in st.pools.iter().rev() {
1226            let (r, set) = alloc(pool);
1227            if r == vk::SUCCESS {
1228                return Ok((pool, set));
1229            }
1230            if r != vk::ERROR_OUT_OF_POOL_MEMORY && r != vk::ERROR_FRAGMENTED_POOL {
1231                self.vk(r)?;
1232            }
1233        }
1234        // a new pool, large enough for 64 groups of this layout as well
1235        let mut sizes: Vec<vk::DescriptorPoolSize> = [
1236            (vk::DESCRIPTOR_TYPE_UNIFORM_BUFFER, 256),
1237            (vk::DESCRIPTOR_TYPE_STORAGE_BUFFER, 512),
1238            (vk::DESCRIPTOR_TYPE_SAMPLED_IMAGE, 512),
1239            (vk::DESCRIPTOR_TYPE_STORAGE_IMAGE, 128),
1240            (vk::DESCRIPTOR_TYPE_SAMPLER, 256),
1241        ]
1242        .iter()
1243        .map(|&(t, n)| vk::DescriptorPoolSize { type_: t, descriptor_count: n })
1244        .collect();
1245        for s in sizes.iter_mut() {
1246            let need: u32 = needs.iter().filter(|x| x.type_ == s.type_).map(|x| x.descriptor_count).sum();
1247            s.descriptor_count = s.descriptor_count.max(need.saturating_mul(64));
1248        }
1249        let pi = vk::DescriptorPoolCreateInfo {
1250            s_type: vk::STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
1251            flags: vk::DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
1252            max_sets: 256,
1253            pool_size_count: sizes.len() as u32,
1254            p_pool_sizes: sizes.as_ptr(),
1255            ..vk::Zero::zero()
1256        };
1257        let mut pool = 0;
1258        // SAFETY: valid create info.
1259        self.vk(unsafe { (self.f.create_descriptor_pool)(self.dev, &pi, core::ptr::null(), &mut pool) })?;
1260        st.pools.push(pool);
1261        let (r, set) = alloc(pool);
1262        self.vk(r)?;
1263        Ok((pool, set))
1264    }
1265}
1266
1267impl Drop for Shared {
1268    fn drop(&mut self) {
1269        let (f, d, n) = (&self.f, self.dev, core::ptr::null());
1270        // SAFETY: the last handle of the device is gone: wait for the GPU, then destroy every
1271        // object the device still owns, the device last.
1272        unsafe {
1273            (f.device_wait_idle)(d);
1274            let mut st = self.st.lock();
1275            if let Some(u) = st.upload.take() {
1276                for c in u.retired {
1277                    st.garbage.push((0, 0, Garbage::Buffer(c.buf, c.a)));
1278                }
1279                st.free.push(u.slot);
1280            }
1281            let ring = core::mem::take(&mut st.ring);
1282            for c in ring.cur.into_iter().chain(ring.free).chain(ring.busy.into_iter().map(|(_, c)| c)) {
1283                st.garbage.push((0, 0, Garbage::Buffer(c.buf, c.a)));
1284            }
1285            let garbage = core::mem::take(&mut st.garbage);
1286            for (_, _, g) in garbage {
1287                self.destroy(&mut st, g);
1288            }
1289            for (_, s) in core::mem::take(&mut st.busy) {
1290                st.free.push(s);
1291            }
1292            let cfree = core::mem::take(&mut st.cfree);
1293            for s in st.free.drain(..).chain(cfree) {
1294                (f.destroy_command_pool)(d, s.pool, n);
1295            }
1296            for p in st.pools.drain(..) {
1297                (f.destroy_descriptor_pool)(d, p, n);
1298            }
1299            st.mem.destroy_all(f, d);
1300            (f.destroy_semaphore)(d, self.timeline, n);
1301            if let Some(l) = &self.lane {
1302                (f.destroy_semaphore)(d, l.timeline, n);
1303            }
1304            drop(st);
1305            (f.destroy_device)(d, n);
1306        }
1307    }
1308}
1309
1310impl Device {
1311    pub fn queue(&self) -> &Queue {
1312        &self.queue
1313    }
1314    /// (G5d) the queue of its own for compute and copies (a family without graphics), when DeviceOptions asked for it;
1315    /// None where the device has none, without asking and with jfgui_GPU_COMPUTE=0 (then everything goes through
1316    /// queue())
1317    pub fn compute_queue(&self) -> Option<&ComputeQueue> {
1318        self.compute.as_ref()
1319    }
1320    pub fn info(&self) -> &AdapterInfo {
1321        &self.sh.info
1322    }
1323    /// what this device has enabled
1324    pub fn features(&self) -> &Features {
1325        &self.sh.features
1326    }
1327    pub fn limits(&self) -> &Limits {
1328        &self.sh.limits
1329    }
1330    pub fn level(&self) -> Level {
1331        self.sh.level
1332    }
1333    /// the device was lost: every call fails with DeviceLost; create a new device
1334    pub fn is_lost(&self) -> bool {
1335        self.sh.check().is_err()
1336    }
1337    /// (test aid) behave as a lost device from now on (a lost device runs nothing any more and
1338    /// its resources may go at once; here the GPU may still work on the last submission: it
1339    /// finishes first)
1340    pub fn test_lose(&self) {
1341        let _ = self.wait_idle();
1342        self.sh.mark_lost();
1343    }
1344    /// the validation layer watches this device
1345    pub fn validating(&self) -> bool {
1346        self.sh.inst.validate
1347    }
1348    /// free what the GPU has finished with (also done by submit and wait)
1349    pub fn poll(&self) -> Result<()> {
1350        self.sh.check()?;
1351        let mut st = self.sh.st.lock();
1352        self.sh.collect(&mut st);
1353        drop(st);
1354        self.sh.check()
1355    }
1356    /// wait for a submission (of either queue) -> false when timeout_ns passed first
1357    pub fn wait(&self, id: SubmitId, timeout_ns: u64) -> Result<bool> {
1358        self.sh.wait_id(id.0, timeout_ns)
1359    }
1360    /// wait for every submission so far (of both queues)
1361    pub fn wait_idle(&self) -> Result<()> {
1362        let v = self.sh.submitted();
1363        let vc = self.sh.lane_submitted();
1364        if self.sh.wait_value(v, 10_000_000_000)? && (vc == 0 || self.sh.wait_id(vc | COMPUTE_ID, 10_000_000_000)?) { Ok(()) } else { Err(GpuError::Timeout) }
1365    }
1366    /// the last submission the GPU has finished
1367    pub fn completed(&self) -> SubmitId {
1368        SubmitId(self.sh.refresh())
1369    }
1370    /// the submission id (of either queue) is done on the GPU (asks the driver; never waits)
1371    pub fn is_done(&self, id: SubmitId) -> bool {
1372        if id.0 & COMPUTE_ID != 0 {
1373            return self.sh.lane_refresh() >= id.0 & !COMPUTE_ID;
1374        }
1375        self.sh.refresh() >= id.0
1376    }
1377    /// bytes of device and host memory the device holds in blocks
1378    pub fn memory_reserved(&self) -> u64 {
1379        self.sh.st.lock().mem.reserved
1380    }
1381}
1382
1383impl Queue {
1384    /// run command buffers in order (after the pending uploads) -> their timeline value
1385    pub fn submit<'a, I: IntoIterator<Item = crate::CommandBuffer<'a>>>(&self, cmds: I) -> Result<SubmitId> {
1386        let mut cbs = Vec::new();
1387        let mut slots = Vec::new();
1388        for c in cmds {
1389            let s = c.into_slot();
1390            cbs.push(s.cb);
1391            slots.push(s);
1392        }
1393        self.sh.submit(&cbs, slots, None, None).map(SubmitId)
1394    }
1395
1396    /// (G5d) run command buffers in order once the submissions `wait` (of either queue; Device::compute_queue) are
1397    /// done: a wait through the timeline, at all commands
1398    pub fn submit_after<'a, I: IntoIterator<Item = crate::CommandBuffer<'a>>>(&self, wait: &[SubmitId], cmds: I) -> Result<SubmitId> {
1399        let (cbs, slots) = split_cmds(cmds);
1400        let after: Vec<u64> = wait.iter().map(|w| w.0).collect();
1401        self.sh.submit_on(false, &cbs, slots, None, &after, None).map(SubmitId)
1402    }
1403
1404    /// submit the pending uploads now
1405    pub fn flush(&self) -> Result<SubmitId> {
1406        self.sh.submit(&[], Vec::new(), None, None).map(SubmitId)
1407    }
1408
1409    /// write data into a buffer at offset before the next submission (staged)
1410    pub fn write_buffer<T: Pod>(&self, buf: &crate::Buffer, offset: u64, data: &[T]) -> Result<()> {
1411        let bytes = crate::bytes_of(data);
1412        let n = bytes.len() as u64;
1413        if offset.checked_add(n).is_none_or(|e| e > buf.size()) {
1414            return Err(GpuError::Invalid("write_buffer: range outside the buffer"));
1415        }
1416        if n == 0 {
1417            return Ok(());
1418        }
1419        let sh = &self.sh;
1420        sh.check()?;
1421        let mut st = sh.st.lock();
1422        let (sbuf, soff, ptr) = sh.stage(&mut st, n)?;
1423        let cb = st.upload.as_ref().map_or(0, |u| u.slot.cb);
1424        // SAFETY: ptr addresses n mapped bytes of the staging chunk; the copy is recorded
1425        // into the upload buffer (recording, guarded by the lock).
1426        unsafe {
1427            core::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr as *mut u8, bytes.len());
1428            let region = vk::BufferCopy { src_offset: soff, dst_offset: offset, size: n };
1429            (sh.f.cmd_copy_buffer)(cb, sbuf, buf.handle(), 1, &region);
1430        }
1431        Ok(())
1432    }
1433
1434    /// write texels into a texture region before the next submission (staged): the region
1435    /// goes from `before` to CopyDst, gets the data, then goes to `after`
1436    pub fn write_texture(&self, dst: &TextureRegion, data: &[u8], layout: DataLayout, before: Layout,
1437                         after: Layout) -> Result<()> {
1438        let t = dst.texture;
1439        let copy = crate::vk_res::copy_region(dst, &layout, data.len() as u64)?;
1440        let need = copy.1;
1441        let sh = &self.sh;
1442        sh.check()?;
1443        let mut st = sh.st.lock();
1444        let (sbuf, soff, ptr) = sh.stage(&mut st, need)?;
1445        let cb = st.upload.as_ref().map_or(0, |u| u.slot.cb);
1446        let mut region = copy.0;
1447        region.buffer_offset = soff;
1448        let range = crate::vk_res::subresources(t, dst.mip, 1, dst.base_layer, dst.layers);
1449        // SAFETY: as in write_buffer; data holds at least `need` bytes after layout.offset
1450        // (copy_region checked it).
1451        unsafe {
1452            core::ptr::copy_nonoverlapping(data.as_ptr().add(layout.offset as usize), ptr as *mut u8, need as usize);
1453            crate::vk_cmd::image_barrier(&sh.f, cb, t.image(), range, before, Layout::CopyDst);
1454            (sh.f.cmd_copy_buffer_to_image)(cb, sbuf, t.image(), vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
1455            crate::vk_cmd::image_barrier(&sh.f, cb, t.image(), range, Layout::CopyDst, after);
1456        }
1457        Ok(())
1458    }
1459
1460    /// write rectangles (x, y, w, h; clipped to the texture, empty ones skipped) of mip 0, layer 0
1461    /// of a 2D texture from `data`, an image of the texture's size in rows of `stride` bytes (a
1462    /// framebuffer), before the next submission; the staged bytes are those of the rectangles.
1463    /// The whole level goes from `before` to CopyDst and then to `after`, once for all of them
1464    pub fn write_texture_rects(&self, t: &crate::Texture, rects: &[[i32; 4]], data: &[u8], stride: u32, before: Layout,
1465                               after: Layout) -> Result<()> {
1466        let f = t.format();
1467        let bb = f.block_bytes() as u64;
1468        if f.is_compressed() || f.is_depth() || t.depth() != 1 {
1469            return Err(GpuError::Invalid("write_texture_rects: an uncompressed 2D colour texture"));
1470        }
1471        let (tw, th) = (t.width() as i32, t.height() as i32);
1472        if (stride as u64) < tw as u64 * bb || (data.len() as u64) < stride as u64 * th as u64 {
1473            return Err(GpuError::Invalid("write_texture_rects: data smaller than the texture"));
1474        }
1475        self.write_texture_rects_at(t, rects, (0, 0), data, stride, before, after)
1476    }
1477
1478    /// write_texture_rects from an image of another size: rectangles (x, y, w, h) of `data`, an
1479    /// image in rows of `stride` bytes, go to (x - origin.0, y - origin.1) of the texture; what
1480    /// lies outside the image or the texture is skipped
1481    #[allow(clippy::too_many_arguments)]
1482    pub fn write_texture_rects_at(&self, t: &crate::Texture, rects: &[[i32; 4]], origin: (i32, i32), data: &[u8], stride: u32, before: Layout,
1483                                  after: Layout) -> Result<()> {
1484        let f = t.format();
1485        let bb = f.block_bytes() as u64;
1486        if f.is_compressed() || f.is_depth() || t.depth() != 1 {
1487            return Err(GpuError::Invalid("write_texture_rects: an uncompressed 2D colour texture"));
1488        }
1489        let (tw, th) = (t.width() as i32, t.height() as i32);
1490        if stride == 0 {
1491            return Err(GpuError::Invalid("write_texture_rects: stride 0"));
1492        }
1493        // the image's size: rows that lie completely in data, as many pixels as a row holds
1494        let (iw, ih) = ((stride as u64 / bb).min(i32::MAX as u64) as i64, (data.len() as u64 / stride as u64).min(i32::MAX as u64) as i64);
1495        let mut regions: Vec<vk::BufferImageCopy> = Vec::with_capacity(rects.len());
1496        let mut total = 0u64;
1497        let sub = crate::vk_res::subresources(t, 0, 1, 0, 1);
1498        let (ox, oy) = (origin.0 as i64, origin.1 as i64);
1499        for r in rects {
1500            // in the texture's coordinates, cut to the texture and the image
1501            let x0 = (r[0] as i64 - ox).max(0).max(-ox);
1502            let y0 = (r[1] as i64 - oy).max(0).max(-oy);
1503            let x1 = (r[0] as i64 + r[2] as i64 - ox).min(tw as i64).min(iw - ox);
1504            let y1 = (r[1] as i64 + r[3] as i64 - oy).min(th as i64).min(ih - oy);
1505            if x1 <= x0 || y1 <= y0 {
1506                continue;
1507            }
1508            let (x0, y0, x1, y1) = (x0 as i32, y0 as i32, x1 as i32, y1 as i32);
1509            let (w, h) = ((x1 - x0) as u32, (y1 - y0) as u32);
1510            regions.push(vk::BufferImageCopy {
1511                buffer_offset: total,
1512                buffer_row_length: w,
1513                buffer_image_height: h,
1514                image_subresource: vk::ImageSubresourceLayers { aspect_mask: sub.aspect_mask, mip_level: 0, base_array_layer: 0, layer_count: 1 },
1515                image_offset: vk::Offset3D { x: x0, y: y0, z: 0 },
1516                image_extent: vk::Extent3D { width: w, height: h, depth: 1 },
1517            });
1518            total += w as u64 * h as u64 * bb;
1519        }
1520        if regions.is_empty() {
1521            return Ok(());
1522        }
1523        let sh = &self.sh;
1524        sh.check()?;
1525        let mut st = sh.st.lock();
1526        let (sbuf, soff, ptr) = sh.stage(&mut st, total)?;
1527        let cb = st.upload.as_ref().map_or(0, |u| u.slot.cb);
1528        for g in regions.iter_mut() {
1529            let (x, y) = ((g.image_offset.x as i64 + ox) as usize, (g.image_offset.y as i64 + oy) as usize);
1530            let (w, h) = (g.image_extent.width as usize, g.image_extent.height as usize);
1531            let row = w * bb as usize;
1532            let mut dst = ptr + g.buffer_offset as usize;
1533            for yy in y..y + h {
1534                let from = yy * stride as usize + x * bb as usize;
1535                // SAFETY: the row lies in data (cut to the image above) and in the staged range
1536                // of `total` bytes, whose rectangles follow each other.
1537                unsafe { core::ptr::copy_nonoverlapping(data.as_ptr().add(from), dst as *mut u8, row) };
1538                dst += row;
1539            }
1540            g.buffer_offset += soff;
1541        }
1542        // SAFETY: the upload buffer is recording (guarded by the lock); the regions lie in the
1543        // texture (clipped above) and the staged range.
1544        unsafe {
1545            crate::vk_cmd::image_barrier(&sh.f, cb, t.image(), sub, before, Layout::CopyDst);
1546            (sh.f.cmd_copy_buffer_to_image)(cb, sbuf, t.image(), vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regions.len() as u32, regions.as_ptr());
1547            crate::vk_cmd::image_barrier(&sh.f, cb, t.image(), sub, Layout::CopyDst, after);
1548        }
1549        Ok(())
1550    }
1551
1552    /// read a buffer's contents at offset (copies through a readback buffer when needed,
1553    /// submits and waits for the GPU: the slow path, for tools and tests)
1554    pub fn read_buffer<T: Pod>(&self, buf: &crate::Buffer, offset: u64, out: &mut [T]) -> Result<()> {
1555        let n = core::mem::size_of_val(out) as u64;
1556        if offset.checked_add(n).is_none_or(|e| e > buf.size()) {
1557            return Err(GpuError::Invalid("read_buffer: range outside the buffer"));
1558        }
1559        if buf.memory() == MemoryKind::Readback {
1560            let id = self.flush()?;
1561            self.sh.wait_value(id.0, u64::MAX)?;
1562            return buf.read(offset, out);
1563        }
1564        let dev = Device { sh: self.sh.clone(), queue: self.clone(), compute: None };
1565        let rb = dev.create_buffer(&crate::BufferDesc { size: n.max(4), usage: crate::BufferUsage::NONE, memory: MemoryKind::Readback })?;
1566        let mut enc = dev.encoder()?;
1567        enc.copy_buffer(buf, offset, &rb, 0, n);
1568        let id = self.submit([enc.finish()?])?;
1569        if !self.sh.wait_value(id.0, 10_000_000_000)? {
1570            return Err(GpuError::Timeout);
1571        }
1572        rb.read(0, out)
1573    }
1574}
1575
1576/// (G5d) the command buffers' handles and slots
1577fn split_cmds<'a, I: IntoIterator<Item = crate::CommandBuffer<'a>>>(cmds: I) -> (Vec<vk::CommandBuffer>, Vec<Slot>) {
1578    let mut cbs = Vec::new();
1579    let mut slots = Vec::new();
1580    for c in cmds {
1581        let s = c.into_slot();
1582        cbs.push(s.cb);
1583        slots.push(s);
1584    }
1585    (cbs, slots)
1586}
1587
1588impl ComputeQueue {
1589    /// (G5d) an encoder of this queue's family: copies, fills, barriers, compute and timestamps (where the family has
1590    /// them); a render pass is Invalid (from finish)
1591    pub fn encoder(&self) -> Result<crate::CommandEncoder<'_>> {
1592        crate::vk_cmd::CommandEncoder::of(&self.sh, true)
1593    }
1594
1595    /// (G5d) run this queue's command buffers in order -> their SubmitId (Device::wait takes it); the pending uploads go
1596    /// first, on the device's queue
1597    pub fn submit<'a, I: IntoIterator<Item = crate::CommandBuffer<'a>>>(&self, cmds: I) -> Result<SubmitId> {
1598        self.submit_after(&[], cmds)
1599    }
1600
1601    /// (G5d) the same once the submissions `wait` (of either queue) are done: a wait through the timeline, at all
1602    /// commands
1603    pub fn submit_after<'a, I: IntoIterator<Item = crate::CommandBuffer<'a>>>(&self, wait: &[SubmitId], cmds: I) -> Result<SubmitId> {
1604        let (cbs, slots) = split_cmds(cmds);
1605        let after: Vec<u64> = wait.iter().map(|w| w.0).collect();
1606        self.sh.submit_on(true, &cbs, slots, None, &after, None).map(SubmitId)
1607    }
1608}