1use alloc::vec::Vec;
34use core::marker::PhantomData;
35
36use crate::vk_dev::{Device, Shared, Slot};
37use crate::vk_ffi as vk;
38use crate::vk_res::{PipeInner, copy_region, subresources};
39use crate::*;
40
41const LAYOUT_PRESENT: u32 = if cfg!(jfgui_glibc) { vk::IMAGE_LAYOUT_GENERAL } else { vk::IMAGE_LAYOUT_PRESENT_SRC_KHR };
44
45fn src_of(l: Layout) -> (u32, u32, u32) {
47 let shaders = vk::PIPELINE_STAGE_VERTEX_SHADER_BIT | vk::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT;
48 let tests = vk::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | vk::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
49 let acquire = vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | vk::PIPELINE_STAGE_TRANSFER_BIT | vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT;
50 match l {
51 Layout::Undefined => (vk::IMAGE_LAYOUT_UNDEFINED, acquire, 0),
52 Layout::General => (vk::IMAGE_LAYOUT_GENERAL, vk::PIPELINE_STAGE_ALL_COMMANDS_BIT, vk::ACCESS_MEMORY_WRITE_BIT),
53 Layout::CopySrc => (vk::IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vk::PIPELINE_STAGE_TRANSFER_BIT, 0),
54 Layout::CopyDst => (vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vk::PIPELINE_STAGE_TRANSFER_BIT, vk::ACCESS_TRANSFER_WRITE_BIT),
55 Layout::Sampled => (vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, shaders, 0),
56 Layout::ColorTarget => (vk::IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
57 vk::ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
58 Layout::DepthTarget => (vk::IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, tests, vk::ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT),
59 Layout::DepthRead => (vk::IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, tests | shaders, 0),
60 Layout::Present => (LAYOUT_PRESENT, acquire, 0),
61 }
62}
63
64fn dst_of(l: Layout) -> (u32, u32, u32) {
66 let shaders = vk::PIPELINE_STAGE_VERTEX_SHADER_BIT | vk::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT;
67 let tests = vk::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | vk::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
68 match l {
69 Layout::Undefined => (vk::IMAGE_LAYOUT_UNDEFINED, vk::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0),
70 Layout::General => (vk::IMAGE_LAYOUT_GENERAL, vk::PIPELINE_STAGE_ALL_COMMANDS_BIT,
71 vk::ACCESS_MEMORY_READ_BIT | vk::ACCESS_MEMORY_WRITE_BIT),
72 Layout::CopySrc => (vk::IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vk::PIPELINE_STAGE_TRANSFER_BIT, vk::ACCESS_TRANSFER_READ_BIT),
73 Layout::CopyDst => (vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vk::PIPELINE_STAGE_TRANSFER_BIT, vk::ACCESS_TRANSFER_WRITE_BIT),
74 Layout::Sampled => (vk::IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, shaders, vk::ACCESS_SHADER_READ_BIT),
75 Layout::ColorTarget => (vk::IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
76 vk::ACCESS_COLOR_ATTACHMENT_READ_BIT | vk::ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
77 Layout::DepthTarget => (vk::IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, tests,
78 vk::ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | vk::ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT),
79 Layout::DepthRead => (vk::IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, tests | shaders,
80 vk::ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | vk::ACCESS_SHADER_READ_BIT),
81 Layout::Present => (LAYOUT_PRESENT, vk::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0),
82 }
83}
84
85fn image_barrier_info(image: vk::Handle, range: vk::ImageSubresourceRange, before: Layout, after: Layout)
86 -> (vk::ImageMemoryBarrier, u32, u32) {
87 let (ol, ss, sa) = src_of(before);
88 let (nl, ds, da) = dst_of(after);
89 let b = vk::ImageMemoryBarrier {
90 s_type: vk::STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
91 src_access_mask: sa,
92 dst_access_mask: da,
93 old_layout: ol,
94 new_layout: nl,
95 src_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
96 dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
97 image,
98 subresource_range: range,
99 ..vk::Zero::zero()
100 };
101 (b, ss, ds)
102}
103
104pub(crate) unsafe fn image_barrier(f: &vk::DeviceFns, cb: vk::CommandBuffer, image: vk::Handle, range: vk::ImageSubresourceRange,
109 before: Layout, after: Layout) {
110 let (b, ss, ds) = image_barrier_info(image, range, before, after);
111 unsafe { (f.cmd_pipeline_barrier)(cb, ss, ds, 0, 0, core::ptr::null(), 0, core::ptr::null(), 1, &b) };
113}
114
115fn access_of(a: Access) -> (u32, u32, u32) {
117 let table: [(Access, u32, u32, u32); 12] = [
118 (Access::COPY_READ, vk::PIPELINE_STAGE_TRANSFER_BIT, 0, vk::ACCESS_TRANSFER_READ_BIT),
119 (Access::COPY_WRITE, vk::PIPELINE_STAGE_TRANSFER_BIT, vk::ACCESS_TRANSFER_WRITE_BIT, vk::ACCESS_TRANSFER_WRITE_BIT),
120 (Access::COMPUTE_READ, vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, vk::ACCESS_SHADER_READ_BIT | vk::ACCESS_UNIFORM_READ_BIT),
121 (Access::COMPUTE_WRITE, vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::ACCESS_SHADER_WRITE_BIT,
122 vk::ACCESS_SHADER_READ_BIT | vk::ACCESS_SHADER_WRITE_BIT),
123 (Access::GRAPHICS_READ, vk::PIPELINE_STAGE_VERTEX_SHADER_BIT | vk::PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
124 vk::ACCESS_SHADER_READ_BIT | vk::ACCESS_UNIFORM_READ_BIT),
125 (Access::GRAPHICS_WRITE, vk::PIPELINE_STAGE_VERTEX_SHADER_BIT | vk::PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
126 vk::ACCESS_SHADER_WRITE_BIT, vk::ACCESS_SHADER_READ_BIT | vk::ACCESS_SHADER_WRITE_BIT),
127 (Access::INDIRECT, vk::PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0, vk::ACCESS_INDIRECT_COMMAND_READ_BIT),
128 (Access::INDEX_VERTEX, vk::PIPELINE_STAGE_VERTEX_INPUT_BIT, 0,
129 vk::ACCESS_INDEX_READ_BIT | vk::ACCESS_VERTEX_ATTRIBUTE_READ_BIT),
130 (Access::COLOR_TARGET, vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
131 vk::ACCESS_COLOR_ATTACHMENT_READ_BIT | vk::ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
132 (Access::DEPTH_TARGET, vk::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | vk::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
133 vk::ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
134 vk::ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | vk::ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT),
135 (Access::HOST_READ, vk::PIPELINE_STAGE_HOST_BIT, 0, vk::ACCESS_HOST_READ_BIT),
136 (Access::HOST_WRITE, vk::PIPELINE_STAGE_HOST_BIT, vk::ACCESS_HOST_WRITE_BIT, vk::ACCESS_HOST_WRITE_BIT),
137 ];
138 let mut out = (0, 0, 0);
139 for (flag, st, sa, da) in table {
140 if a.intersects(flag) {
141 out.0 |= st;
142 out.1 |= sa;
143 out.2 |= da;
144 }
145 }
146 out
147}
148
149struct EndBarrier {
151 image: vk::Handle,
152 range: vk::ImageSubresourceRange,
153 from: Layout,
154 to: Layout,
155}
156
157struct Pass {
158 colors: [u32; COLOR_TARGETS_MAX],
159 n: usize,
160 depth: u32,
161 end: Vec<EndBarrier>,
162}
163
164pub struct CommandEncoder<'a> {
166 sh: &'a Shared,
167 slot: Option<Slot>,
168 cb: vk::CommandBuffer,
169 err: Option<GpuError>,
170 pass: Option<Pass>,
171 compute: Option<&'a PipeInner>,
172 graphics: Option<&'a PipeInner>,
173 index: bool,
174 _borrow: PhantomData<&'a ()>,
175}
176
177pub struct CommandBuffer<'a> {
179 sh: &'a Shared,
180 slot: Option<Slot>,
181}
182
183impl CommandBuffer<'_> {
184 pub(crate) fn into_slot(mut self) -> Slot {
185 self.slot.take().unwrap_or_else(|| unreachable!())
186 }
187}
188
189impl Drop for CommandBuffer<'_> {
190 fn drop(&mut self) {
191 if let Some(s) = self.slot.take() {
192 self.sh.give_slot(s);
193 }
194 }
195}
196
197impl Drop for CommandEncoder<'_> {
198 fn drop(&mut self) {
199 if let Some(s) = self.slot.take() {
200 self.sh.give_slot(s);
203 }
204 }
205}
206
207impl Device {
208 pub fn encoder(&self) -> Result<CommandEncoder<'_>> {
210 let slot = self.sh.take_slot()?;
211 Ok(CommandEncoder {
212 sh: &self.sh,
213 cb: slot.cb,
214 slot: Some(slot),
215 err: None,
216 pass: None,
217 compute: None,
218 graphics: None,
219 index: false,
220 _borrow: PhantomData,
221 })
222 }
223}
224
225impl<'a> CommandEncoder<'a> {
226 #[inline]
228 fn ok(&mut self, cond: bool, what: &'static str) -> Option<()> {
229 if self.err.is_some() {
230 return None;
231 }
232 if !cond {
233 self.err = Some(GpuError::Invalid(what));
234 return None;
235 }
236 Some(())
237 }
238
239 #[inline]
240 fn outside(&mut self, what: &'static str) -> Option<()> {
241 let c = self.pass.is_none();
242 self.ok(c, what)
243 }
244
245 fn buf_range(&mut self, b: &Buffer, off: u64, n: u64, what: &'static str) -> Option<()> {
246 let c = off.checked_add(n).is_some_and(|e| e <= b.size());
247 self.ok(c, what)
248 }
249
250 pub fn copy_buffer(&mut self, src: &'a Buffer, src_off: u64, dst: &'a Buffer, dst_off: u64, size: u64) {
253 if self.outside("copy_buffer inside a render pass").is_none()
254 || self.buf_range(src, src_off, size, "copy_buffer: source range").is_none()
255 || self.buf_range(dst, dst_off, size, "copy_buffer: destination range").is_none()
256 || size == 0
257 {
258 return;
259 }
260 let r = vk::BufferCopy { src_offset: src_off, dst_offset: dst_off, size };
261 unsafe { (self.sh.f.cmd_copy_buffer)(self.cb, src.handle(), dst.handle(), 1, &r) };
263 }
264
265 pub fn fill_buffer(&mut self, buf: &'a Buffer, offset: u64, size: u64, value: u32) {
267 if self.outside("fill_buffer inside a render pass").is_none()
268 || self.ok(offset % 4 == 0 && size % 4 == 0 && size > 0, "fill_buffer: offset and size multiples of 4").is_none()
269 || self.buf_range(buf, offset, size, "fill_buffer: range").is_none()
270 {
271 return;
272 }
273 unsafe { (self.sh.f.cmd_fill_buffer)(self.cb, buf.handle(), offset, size, value) };
275 }
276
277 pub fn copy_buffer_to_texture(&mut self, src: &'a Buffer, layout: DataLayout, dst: TextureRegion<'a>) {
279 if self.outside("copy inside a render pass").is_none() {
280 return;
281 }
282 let c = copy_region(&dst, &layout, src.size());
283 let ok = c.is_ok() && dst.texture.usage().contains(TextureUsage::COPY_DST);
284 if self.ok(ok, "copy_buffer_to_texture: region, data layout or COPY_DST usage").is_none() {
285 return;
286 }
287 let Ok((r, _)) = c else { return };
288 unsafe {
290 (self.sh.f.cmd_copy_buffer_to_image)(self.cb, src.handle(), dst.texture.image(),
291 vk::IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &r)
292 };
293 }
294
295 pub fn copy_texture_to_buffer(&mut self, src: TextureRegion<'a>, dst: &'a Buffer, layout: DataLayout) {
297 if self.outside("copy inside a render pass").is_none() {
298 return;
299 }
300 let c = copy_region(&src, &layout, dst.size());
301 let ok = c.is_ok() && src.texture.usage().contains(TextureUsage::COPY_SRC);
302 if self.ok(ok, "copy_texture_to_buffer: region, data layout or COPY_SRC usage").is_none() {
303 return;
304 }
305 let Ok((r, _)) = c else { return };
306 unsafe {
308 (self.sh.f.cmd_copy_image_to_buffer)(self.cb, src.texture.image(), vk::IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
309 dst.handle(), 1, &r)
310 };
311 }
312
313 pub fn barrier(&mut self, before: Access, after: Access) {
317 if self.outside("barrier inside a render pass").is_none() {
318 return;
319 }
320 let (ss, sa, _) = access_of(before);
321 let (ds, _, da) = access_of(after);
322 let mb = vk::MemoryBarrier { s_type: vk::STRUCTURE_TYPE_MEMORY_BARRIER, src_access_mask: sa, dst_access_mask: da,
323 ..vk::Zero::zero() };
324 let ss = if ss == 0 { vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT } else { ss };
325 let ds = if ds == 0 { vk::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT } else { ds };
326 unsafe { (self.sh.f.cmd_pipeline_barrier)(self.cb, ss, ds, 0, 1, &mb, 0, core::ptr::null(), 0, core::ptr::null()) };
328 }
329
330 pub fn texture_barrier(&mut self, tex: &'a Texture, before: Layout, after: Layout) {
332 self.texture_barrier_range(tex, 0, tex.mips(), 0, tex.layers(), before, after);
333 }
334
335 #[allow(clippy::too_many_arguments)]
337 pub fn texture_barrier_range(&mut self, tex: &'a Texture, base_mip: u32, mips: u32, base_layer: u32, layers: u32,
338 before: Layout, after: Layout) {
339 if self.outside("texture_barrier inside a render pass").is_none()
340 || self.ok(mips > 0 && layers > 0 && base_mip + mips <= tex.mips() && base_layer + layers <= tex.layers(),
341 "texture_barrier: range outside the texture").is_none()
342 || self.ok(after != Layout::Undefined, "texture_barrier: Undefined is no destination").is_none()
343 {
344 return;
345 }
346 let range = subresources(tex, base_mip, mips, base_layer, layers);
347 unsafe { image_barrier(&self.sh.f, self.cb, tex.image(), range, before, after) };
349 }
350
351 pub fn set_compute_pipeline(&mut self, p: &'a ComputePipeline) {
354 if self.outside("set_compute_pipeline inside a render pass").is_none() {
355 return;
356 }
357 self.compute = Some(&p.0);
358 unsafe { (self.sh.f.cmd_bind_pipeline)(self.cb, vk::PIPELINE_BIND_POINT_COMPUTE, p.0.h) };
360 }
361
362 fn current(&mut self) -> Option<(&'a PipeInner, u32)> {
365 let (p, bp) = if self.pass.is_some() {
366 (self.graphics, vk::PIPELINE_BIND_POINT_GRAPHICS)
367 } else {
368 (self.compute, vk::PIPELINE_BIND_POINT_COMPUTE)
369 };
370 self.ok(p.is_some(), "bind group / push constants before a pipeline")?;
371 p.map(|p| (p, bp))
372 }
373
374 pub fn set_bind_group(&mut self, index: u32, group: &'a BindGroup) {
376 let Some((p, bp)) = self.current() else { return };
377 let sets = &p.layout.0.sets;
378 let fits = sets.get(index as usize).is_some_and(|&l| l == group.0.layout);
379 if self.ok(fits, "set_bind_group: the group's layout differs from the pipeline layout's").is_none() {
380 return;
381 }
382 unsafe {
384 (self.sh.f.cmd_bind_descriptor_sets)(self.cb, bp, p.layout.0.h, index, 1, &group.0.set, 0, core::ptr::null())
385 };
386 }
387
388 pub fn push_constants<T: Pod>(&mut self, offset: u32, data: &[T]) {
390 let Some((p, _)) = self.current() else { return };
391 let bytes = bytes_of(data);
392 let n = bytes.len() as u32;
393 let fits = offset % 4 == 0 && n % 4 == 0 && n > 0 && offset + n <= p.layout.0.push;
394 if self.ok(fits, "push_constants: range outside the layout's (multiples of 4)").is_none() {
395 return;
396 }
397 unsafe {
399 (self.sh.f.cmd_push_constants)(self.cb, p.layout.0.h, vk::SHADER_STAGE_ALL, offset, n,
400 bytes.as_ptr() as *const core::ffi::c_void)
401 };
402 }
403
404 pub fn dispatch(&mut self, x: u32, y: u32, z: u32) {
405 let m = self.sh.limits.max_compute_workgroups;
406 let ok = self.pass.is_none() && self.compute.is_some() && x <= m[0] && y <= m[1] && z <= m[2];
407 if self.ok(ok, "dispatch: no compute pipeline, inside a pass or too many workgroups").is_none() {
408 return;
409 }
410 unsafe { (self.sh.f.cmd_dispatch)(self.cb, x, y, z) };
412 }
413
414 pub fn dispatch_indirect(&mut self, buf: &'a Buffer, offset: u64) {
416 let ok = self.pass.is_none() && self.compute.is_some() && buf.usage().contains(BufferUsage::INDIRECT) && offset % 4 == 0
417 && offset + 12 <= buf.size();
418 if self.ok(ok, "dispatch_indirect: pipeline, INDIRECT usage, offset or range").is_none() {
419 return;
420 }
421 unsafe { (self.sh.f.cmd_dispatch_indirect)(self.cb, buf.handle(), offset) };
423 }
424
425 pub fn begin_render(&mut self, desc: &RenderPassDesc<'_, 'a>) {
428 if self.outside("begin_render inside a render pass").is_none() {
429 return;
430 }
431 let n = desc.color.len();
432 if self.ok(n <= self.sh.limits.max_color_targets as usize && (n > 0 || desc.depth.is_some()),
433 "begin_render: no attachments or too many").is_none()
434 {
435 return;
436 }
437 let first = desc.color.first().map(|c| c.view).or(desc.depth.map(|d| d.view));
438 let Some(first) = first else { return };
439 let (w, h) = first.size();
440 let single = |v: &TextureView| v.0.mips == 1 && v.0.layers == 1 && v.size() == (w, h);
441 let colors_ok = desc.color.iter().all(|c| {
442 single(c.view) && c.view.usage().contains(TextureUsage::COLOR_TARGET) && c.after != Layout::Undefined
443 });
444 let depth_ok = desc.depth.is_none_or(|d| {
445 single(d.view) && d.view.usage().contains(TextureUsage::DEPTH_TARGET) && d.after != Layout::Undefined
446 });
447 if self.ok(colors_ok && depth_ok, "begin_render: attachments (one mip and layer, equal sizes, usage)").is_none() {
448 return;
449 }
450 let mut barriers: Vec<vk::ImageMemoryBarrier> = Vec::with_capacity(n + 1);
451 let (mut ss, mut ds) = (0, 0);
452 let mut end = Vec::new();
453 let mut atts: Vec<vk::RenderingAttachmentInfo> = Vec::with_capacity(n);
454 let mut colors = [0u32; COLOR_TARGETS_MAX];
455 let load = |l: Load| match l {
456 Load::Keep => vk::ATTACHMENT_LOAD_OP_LOAD,
457 Load::Clear => vk::ATTACHMENT_LOAD_OP_CLEAR,
458 Load::Discard => vk::ATTACHMENT_LOAD_OP_DONT_CARE,
459 };
460 let store = |s: bool| if s { vk::ATTACHMENT_STORE_OP_STORE } else { vk::ATTACHMENT_STORE_OP_DONT_CARE };
461 for (i, c) in desc.color.iter().enumerate() {
462 let v = &c.view.0;
463 let range = vk::ImageSubresourceRange { aspect_mask: v.tex.aspect_all, base_mip_level: v.base_mip, level_count: 1,
464 base_array_layer: v.base_layer, layer_count: 1 };
465 let (b, s, d) = image_barrier_info(v.tex.image, range, c.before, Layout::ColorTarget);
466 barriers.push(b);
467 ss |= s;
468 ds |= d;
469 if c.after != Layout::ColorTarget {
470 end.push(EndBarrier { image: v.tex.image, range, from: Layout::ColorTarget, to: c.after });
471 }
472 colors[i] = crate::vk_res::vk_format(c.view.format());
473 atts.push(vk::RenderingAttachmentInfo {
474 s_type: vk::STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
475 image_view: v.h,
476 image_layout: vk::IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
477 load_op: load(c.load),
478 store_op: store(c.store),
479 clear_value: c.clear.map(f32::to_bits),
480 ..vk::Zero::zero()
481 });
482 }
483 let mut datt: vk::RenderingAttachmentInfo = vk::Zero::zero();
484 let mut depth = vk::FORMAT_UNDEFINED;
485 if let Some(d) = desc.depth {
486 let v = &d.view.0;
487 let range = vk::ImageSubresourceRange { aspect_mask: v.tex.aspect_all, base_mip_level: v.base_mip, level_count: 1,
488 base_array_layer: v.base_layer, layer_count: 1 };
489 let (b, s, dd) = image_barrier_info(v.tex.image, range, d.before, Layout::DepthTarget);
490 barriers.push(b);
491 ss |= s;
492 ds |= dd;
493 if d.after != Layout::DepthTarget {
494 end.push(EndBarrier { image: v.tex.image, range, from: Layout::DepthTarget, to: d.after });
495 }
496 depth = crate::vk_res::vk_format(d.view.format());
497 datt = vk::RenderingAttachmentInfo {
498 s_type: vk::STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
499 image_view: v.h,
500 image_layout: vk::IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
501 load_op: load(d.load),
502 store_op: store(d.store),
503 clear_value: [d.clear.to_bits(), 0, 0, 0],
504 ..vk::Zero::zero()
505 };
506 }
507 let has_stencil = desc.depth.is_some_and(|d| d.view.format().has_stencil());
508 let ri = vk::RenderingInfo {
509 s_type: vk::STRUCTURE_TYPE_RENDERING_INFO,
510 render_area: vk::Rect2D { offset: vk::Offset2D { x: 0, y: 0 }, extent: vk::Extent2D { width: w, height: h } },
511 layer_count: 1,
512 color_attachment_count: n as u32,
513 p_color_attachments: atts.as_ptr(),
514 p_depth_attachment: if desc.depth.is_some() { &datt } else { core::ptr::null() },
515 p_stencil_attachment: if has_stencil { &datt } else { core::ptr::null() },
516 ..vk::Zero::zero()
517 };
518 let vp = vk::Viewport { x: 0.0, y: h as f32, width: w as f32, height: -(h as f32), min_depth: 0.0, max_depth: 1.0 };
519 let sc = vk::Rect2D { offset: vk::Offset2D { x: 0, y: 0 }, extent: vk::Extent2D { width: w, height: h } };
520 unsafe {
523 (self.sh.f.cmd_pipeline_barrier)(self.cb, ss, ds, 0, 0, core::ptr::null(), 0, core::ptr::null(),
524 barriers.len() as u32, barriers.as_ptr());
525 (self.sh.f.cmd_begin_rendering)(self.cb, &ri);
526 (self.sh.f.cmd_set_viewport)(self.cb, 0, 1, &vp);
527 (self.sh.f.cmd_set_scissor)(self.cb, 0, 1, &sc);
528 }
529 self.pass = Some(Pass { colors, n, depth, end });
530 self.graphics = None;
531 self.index = false;
532 }
533
534 pub fn end_render(&mut self) {
535 let Some(pass) = self.pass.take() else {
536 self.ok(false, "end_render without begin_render");
537 return;
538 };
539 unsafe { (self.sh.f.cmd_end_rendering)(self.cb) };
541 if pass.end.is_empty() {
542 return;
543 }
544 let (mut ss, mut ds) = (0, 0);
545 let barriers: Vec<vk::ImageMemoryBarrier> = pass
546 .end
547 .iter()
548 .map(|e| {
549 let (b, s, d) = image_barrier_info(e.image, e.range, e.from, e.to);
550 ss |= s;
551 ds |= d;
552 b
553 })
554 .collect();
555 unsafe {
557 (self.sh.f.cmd_pipeline_barrier)(self.cb, ss, ds, 0, 0, core::ptr::null(), 0, core::ptr::null(),
558 barriers.len() as u32, barriers.as_ptr())
559 };
560 }
561
562 pub fn set_render_pipeline(&mut self, p: &'a RenderPipeline) {
563 let fits = self.pass.as_ref().is_some_and(|pass| {
564 pass.n == p.0.ncolor && pass.colors[..pass.n] == p.0.colors[..pass.n] && pass.depth == p.0.depth
565 });
566 if self.ok(fits, "set_render_pipeline: outside a pass or target formats differ").is_none() {
567 return;
568 }
569 self.graphics = Some(&p.0);
570 unsafe { (self.sh.f.cmd_bind_pipeline)(self.cb, vk::PIPELINE_BIND_POINT_GRAPHICS, p.0.h) };
572 }
573
574 pub fn set_viewport(&mut self, x: f32, y: f32, w: f32, h: f32, min_depth: f32, max_depth: f32) {
576 if self.ok(self.pass.is_some() && w > 0.0 && h > 0.0, "set_viewport: outside a pass or empty").is_none() {
577 return;
578 }
579 let vp = vk::Viewport { x, y: y + h, width: w, height: -h, min_depth, max_depth };
580 unsafe { (self.sh.f.cmd_set_viewport)(self.cb, 0, 1, &vp) };
582 }
583
584 pub fn set_scissor(&mut self, x: u32, y: u32, w: u32, h: u32) {
585 if self.ok(self.pass.is_some(), "set_scissor outside a pass").is_none() {
586 return;
587 }
588 let r = vk::Rect2D { offset: vk::Offset2D { x: x as i32, y: y as i32 }, extent: vk::Extent2D { width: w, height: h } };
589 unsafe { (self.sh.f.cmd_set_scissor)(self.cb, 0, 1, &r) };
591 }
592
593 pub fn set_vertex_buffer(&mut self, slot: u32, buf: &'a Buffer, offset: u64) {
594 let ok = self.pass.is_some() && buf.usage().contains(BufferUsage::VERTEX) && offset < buf.size()
595 && slot < self.sh.limits.max_vertex_buffers;
596 if self.ok(ok, "set_vertex_buffer: outside a pass, VERTEX usage or offset").is_none() {
597 return;
598 }
599 unsafe { (self.sh.f.cmd_bind_vertex_buffers)(self.cb, slot, 1, &buf.handle(), &offset) };
601 }
602
603 pub fn set_index_buffer(&mut self, buf: &'a Buffer, offset: u64, format: IndexFormat) {
604 let size = if format == IndexFormat::U16 { 2 } else { 4 };
605 let ok = self.pass.is_some() && buf.usage().contains(BufferUsage::INDEX) && offset % size == 0 && offset < buf.size();
606 if self.ok(ok, "set_index_buffer: outside a pass, INDEX usage or offset").is_none() {
607 return;
608 }
609 let t = if format == IndexFormat::U16 { vk::INDEX_TYPE_UINT16 } else { vk::INDEX_TYPE_UINT32 };
610 unsafe { (self.sh.f.cmd_bind_index_buffer)(self.cb, buf.handle(), offset, t) };
612 self.index = true;
613 }
614
615 #[inline]
616 fn can_draw(&mut self, indexed: bool) -> Option<()> {
617 let ok = self.pass.is_some() && self.graphics.is_some() && (!indexed || self.index);
618 self.ok(ok, "draw: outside a pass, without a render pipeline or index buffer")
619 }
620
621 pub fn draw(&mut self, vertices: u32, instances: u32, first_vertex: u32, first_instance: u32) {
622 if self.can_draw(false).is_some() {
623 unsafe { (self.sh.f.cmd_draw)(self.cb, vertices, instances, first_vertex, first_instance) };
625 }
626 }
627
628 pub fn draw_indexed(&mut self, indices: u32, instances: u32, first_index: u32, base_vertex: i32, first_instance: u32) {
629 if self.can_draw(true).is_some() {
630 unsafe { (self.sh.f.cmd_draw_indexed)(self.cb, indices, instances, first_index, base_vertex, first_instance) };
632 }
633 }
634
635 fn indirect_ok(&mut self, buf: &Buffer, offset: u64, bytes: u64) -> Option<()> {
636 let ok = buf.usage().contains(BufferUsage::INDIRECT) && offset % 4 == 0 && offset.checked_add(bytes).is_some_and(|e| e <= buf.size());
637 self.ok(ok, "indirect: INDIRECT usage, offset (multiple of 4) or range")
638 }
639
640 pub fn draw_indirect(&mut self, buf: &'a Buffer, offset: u64) {
642 if self.can_draw(false).is_some() && self.indirect_ok(buf, offset, 16).is_some() {
643 unsafe { (self.sh.f.cmd_draw_indirect)(self.cb, buf.handle(), offset, 1, 16) };
645 }
646 }
647
648 pub fn draw_indexed_indirect(&mut self, buf: &'a Buffer, offset: u64) {
651 if self.can_draw(true).is_some() && self.indirect_ok(buf, offset, 20).is_some() {
652 unsafe { (self.sh.f.cmd_draw_indexed_indirect)(self.cb, buf.handle(), offset, 1, 20) };
654 }
655 }
656
657 pub fn draw_indexed_indirect_multi(&mut self, buf: &'a Buffer, offset: u64, count: u32, stride: u32) {
659 let ok = self.sh.features.multi_draw_indirect && stride >= 20 && stride % 4 == 0 && count > 0
660 && count <= self.sh.limits.max_draw_indirect_count;
661 if self.ok(ok, "draw_indexed_indirect_multi: needs multi_draw_indirect, stride >= 20").is_none()
662 || self.can_draw(true).is_none()
663 || self.indirect_ok(buf, offset, (count as u64 - 1) * stride as u64 + 20).is_none()
664 {
665 return;
666 }
667 unsafe { (self.sh.f.cmd_draw_indexed_indirect)(self.cb, buf.handle(), offset, count, stride) };
669 }
670
671 pub fn draw_indexed_indirect_count(&mut self, buf: &'a Buffer, offset: u64, count_buf: &'a Buffer, count_offset: u64,
674 max_count: u32, stride: u32) {
675 let f = self.sh.f.cmd_draw_indexed_indirect_count;
676 let ok = self.sh.features.draw_indirect_count && f.is_some() && stride >= 20 && stride % 4 == 0 && max_count > 0;
677 if self.ok(ok, "draw_indexed_indirect_count: needs level Voll, stride >= 20").is_none()
678 || self.can_draw(true).is_none()
679 || self.indirect_ok(buf, offset, (max_count as u64 - 1) * stride as u64 + 20).is_none()
680 || self.indirect_ok(count_buf, count_offset, 4).is_none()
681 {
682 return;
683 }
684 let Some(f) = f else { return };
685 unsafe { f(self.cb, buf.handle(), offset, count_buf.handle(), count_offset, max_count, stride) };
687 }
688
689 pub fn reset_timestamps(&mut self, ts: &'a Timestamps, first: u32, n: u32) {
693 let ok = self.pass.is_none() && n > 0 && first.checked_add(n).is_some_and(|e| e <= ts.0.count);
694 if self.ok(ok, "reset_timestamps: inside a pass or range").is_none() {
695 return;
696 }
697 unsafe { (self.sh.f.cmd_reset_query_pool)(self.cb, ts.0.pool, first, n) };
699 }
700
701 pub fn timestamp(&mut self, ts: &'a Timestamps, i: u32) {
703 if self.ok(i < ts.0.count, "timestamp: index outside the set").is_none() {
704 return;
705 }
706 unsafe { (self.sh.f.cmd_write_timestamp)(self.cb, vk::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, ts.0.pool, i) };
708 }
709
710 pub fn finish(mut self) -> Result<CommandBuffer<'a>> {
714 if self.pass.is_some() {
715 self.ok(false, "finish inside a render pass");
716 }
717 if let Some(e) = self.err {
718 return Err(e); }
720 let slot = self.slot.take().unwrap_or_else(|| unreachable!());
721 let r = unsafe { (self.sh.f.end_command_buffer)(slot.cb) };
723 if let Err(e) = self.sh.vk(r) {
724 self.sh.give_slot(slot);
725 return Err(e);
726 }
727 Ok(CommandBuffer { sh: self.sh, slot: Some(slot) })
728 }
729}