1use crate::device::VulkanContext;
9use crate::resource::{backend_err, transition, vk_format, VulkanTexture};
10use ash::vk;
11use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme};
12use gpu_allocator::MemoryLocation;
13use impeller_hal::{
14 Batch, BlendMode, ClipRole, Error, Extent2D, PassDescriptor, Result, TextureDescriptor,
15 TextureUsage,
16};
17use std::collections::HashMap;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub(crate) struct RenderPassKey {
23 pub(crate) format: vk::Format,
24 pub(crate) clears: bool,
25 pub(crate) samples: u32,
28 pub(crate) presents: bool,
33 pub(crate) stencil: Option<vk::Format>,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48pub(crate) struct PipelineKey {
49 pub(crate) format: vk::Format,
50 pub(crate) blend: BlendMode,
51 pub(crate) samples: u32,
54 pub(crate) stencil: Option<vk::Format>,
56 pub(crate) program: Option<u32>,
63 pub(crate) role: ClipRole,
69}
70
71#[derive(Default)]
77pub(crate) struct PipelineCache {
78 pub(crate) layout: Option<vk::PipelineLayout>,
79 pub(crate) render_passes: HashMap<RenderPassKey, vk::RenderPass>,
80 pub(crate) pipelines: HashMap<PipelineKey, vk::Pipeline>,
81}
82
83impl PipelineCache {
84 pub(crate) fn render_pass(&self, key: RenderPassKey) -> Option<vk::RenderPass> {
85 self.render_passes.get(&key).copied()
86 }
87
88 pub(crate) fn pipeline(&self, key: PipelineKey) -> Option<vk::Pipeline> {
89 self.pipelines.get(&key).copied()
90 }
91
92 pub(crate) fn layout(&self) -> Option<vk::PipelineLayout> {
93 self.layout
94 }
95
96 pub(crate) fn destroy(&mut self, device: &ash::Device) {
97 unsafe {
99 for pipeline in self.pipelines.values() {
100 device.destroy_pipeline(*pipeline, None);
101 }
102 for pass in self.render_passes.values() {
103 device.destroy_render_pass(*pass, None);
104 }
105 if let Some(layout) = self.layout {
106 device.destroy_pipeline_layout(layout, None);
107 }
108 }
109 self.pipelines.clear();
110 self.render_passes.clear();
111 self.layout = None;
112 }
113}
114
115impl VulkanContext {
116 pub fn draw_indexed(
121 &mut self,
122 target: &mut VulkanTexture,
123 vertices: &[[f32; 2]],
124 indices: &[u32],
125 material: impeller_hal::Material,
126 blend: BlendMode,
127 clear: Option<[f32; 4]>,
128 ) -> Result<()> {
129 let mut batch = Batch::new();
130 batch.push(vertices, indices, material, blend)?;
131 self.submit_batch(
132 target,
133 &batch,
134 PassDescriptor {
135 clear,
136 samples: 1,
137 viewport: None,
138 },
139 )
140 }
141
142 pub fn submit_batch(
144 &mut self,
145 target: &mut VulkanTexture,
146 batch: &Batch,
147 pass: PassDescriptor,
148 ) -> Result<()> {
149 self.submit_batch_textured(target, batch, pass, &[])
150 }
151
152 pub fn submit_batch_textured(
155 &mut self,
156 target: &mut VulkanTexture,
157 batch: &Batch,
158 pass: PassDescriptor,
159 textures: &[&VulkanTexture],
160 ) -> Result<()> {
161 let format = vk_format(target.format());
162 let clear = pass.clear;
163
164 self.capabilities().check_blend_modes(batch)?;
165 if !pass.samples.is_power_of_two() {
166 return Err(Error::Unsupported("sample count is not a power of two"));
167 }
168 if !self.capabilities().sample_counts.supports(pass.samples) {
169 return Err(Error::Unsupported("sample count not supported by device"));
170 }
171 if pass.is_multisampled() && clear.is_none() {
172 return Err(Error::Unsupported(
178 "a multisampled pass must clear: give the pass a clear color, \
179 or draw it at one sample. Preserving would need a \
180 resolved-to-multisample copy, which this technique has no \
181 reverse of",
182 ));
183 }
184
185 if batch.is_empty() {
188 return match clear {
189 Some(c) => self.clear_texture(target, c),
190 None => Ok(()),
191 };
192 }
193
194 let stencil_format = self.stencil_format_for(batch)?;
195 let pass_key = RenderPassKey {
196 format,
197 clears: clear.is_some(),
198 samples: pass.samples,
199 presents: false,
200 stencil: stencil_format,
201 };
202 let render_pass = self.ensure_render_pass(pass_key)?;
203 for draw in batch.draws() {
204 self.ensure_pipeline(
205 PipelineKey {
206 format,
207 program: draw.material.program(),
208 blend: draw.blend,
209 samples: pass.samples,
210 stencil: stencil_format,
211 role: draw.stencil.role,
212 },
213 render_pass,
214 )?;
215 }
216
217 let bindings = crate::sampling::build(self, batch, textures)?;
218
219 let device = self.raw_device().clone();
220 let vertex_buffer = self.upload(
221 cast_bytes(batch.vertices()),
222 vk::BufferUsageFlags::VERTEX_BUFFER,
223 )?;
224 let index_buffer = self.upload(
225 cast_bytes(batch.indices()),
226 vk::BufferUsageFlags::INDEX_BUFFER,
227 )?;
228 let (material_buffer, materials) = crate::materials::build(self, batch, target.format())?;
229
230 let result = self.record_batch(
231 &device,
232 target,
233 format,
234 render_pass,
235 batch,
236 vertex_buffer.buffer,
237 index_buffer.buffer,
238 pass,
239 stencil_format,
240 &bindings,
241 &materials,
242 textures,
243 );
244
245 self.release(vertex_buffer);
246 self.release(index_buffer);
247 self.release(material_buffer);
248 materials.destroy(&device);
249 bindings.destroy(self);
250 result
251 }
252
253 #[allow(clippy::too_many_arguments)]
254 fn record_batch(
255 &mut self,
256 device: &ash::Device,
257 target: &mut VulkanTexture,
258 format: vk::Format,
259 render_pass: vk::RenderPass,
260 batch: &Batch,
261 vertex_buffer: vk::Buffer,
262 index_buffer: vk::Buffer,
263 pass: PassDescriptor,
264 stencil_format: Option<vk::Format>,
265 bindings: &crate::sampling::Bindings,
266 materials: &crate::materials::Materials,
267 sampled: &[&VulkanTexture],
268 ) -> Result<()> {
269 let layout = self.pipeline_cache().layout().expect("ensured above");
270 let extent = target.extent();
271 let clear = pass.clear;
272
273 let view_info = vk::ImageViewCreateInfo::default()
274 .image(target.raw_image())
275 .view_type(vk::ImageViewType::TYPE_2D)
276 .format(format)
277 .subresource_range(
278 vk::ImageSubresourceRange::default()
279 .aspect_mask(vk::ImageAspectFlags::COLOR)
280 .level_count(1)
281 .layer_count(1),
282 );
283 let view = unsafe { device.create_image_view(&view_info, None) }
284 .map_err(|e| backend_err("create_image_view", e))?;
285
286 let multisample = if pass.is_multisampled() {
291 match self.create_multisample_buffer(target, pass.samples) {
292 Ok(ms) => Some(ms),
293 Err(e) => {
294 unsafe { device.destroy_image_view(view, None) };
295 return Err(e);
296 }
297 }
298 } else {
299 None
300 };
301
302 let stencil = match stencil_format {
305 Some(format) => match crate::stencil::create(self, extent, pass.samples, format) {
306 Ok(buffer) => Some(buffer),
307 Err(e) => {
308 unsafe { device.destroy_image_view(view, None) };
309 if let Some((tex, ms_view)) = multisample {
310 unsafe { device.destroy_image_view(ms_view, None) };
311 self.destroy_texture(tex);
312 }
313 return Err(e);
314 }
315 },
316 None => None,
317 };
318
319 let mut attachments: Vec<vk::ImageView> = match &multisample {
320 Some((_, ms_view)) => vec![*ms_view, view],
323 None => vec![view],
324 };
325 if let Some(stencil) = &stencil {
326 attachments.push(stencil.view);
327 }
328 let fb_info = vk::FramebufferCreateInfo::default()
329 .render_pass(render_pass)
330 .attachments(&attachments)
331 .width(extent.width)
332 .height(extent.height)
333 .layers(1);
334 let framebuffer = match unsafe { device.create_framebuffer(&fb_info, None) } {
335 Ok(fb) => fb,
336 Err(e) => {
337 unsafe { device.destroy_image_view(view, None) };
338 if let Some((tex, ms_view)) = multisample {
339 unsafe { device.destroy_image_view(ms_view, None) };
340 self.destroy_texture(tex);
341 }
342 if let Some(stencil) = stencil {
343 stencil.destroy(self);
344 }
345 return Err(backend_err("create_framebuffer", e));
346 }
347 };
348
349 let previous_layout = target.layout();
350 let outcome = (|| -> Result<()> {
351 let cmd = self.begin_one_shot()?;
352
353 crate::sampling::transition_for_sampling(device, cmd, sampled);
357
358 if clear.is_none() {
359 transition(
363 device,
364 cmd,
365 target.raw_image(),
366 previous_layout,
367 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
368 );
369 }
370
371 let mut clear_values = vec![vk::ClearValue {
376 color: vk::ClearColorValue {
377 float32: clear.unwrap_or([0.0; 4]),
378 },
379 }];
380 clear_values.resize(attachments.len(), vk::ClearValue::default());
381 let clear_values = &clear_values[..];
382 let area = vk::Rect2D {
383 offset: vk::Offset2D { x: 0, y: 0 },
384 extent: vk::Extent2D {
385 width: extent.width,
386 height: extent.height,
387 },
388 };
389 let begin = vk::RenderPassBeginInfo::default()
390 .render_pass(render_pass)
391 .framebuffer(framebuffer)
392 .render_area(area)
393 .clear_values(clear_values);
394
395 let viewport = pass_viewport(pass, extent);
396
397 unsafe {
398 device.cmd_begin_render_pass(cmd, &begin, vk::SubpassContents::INLINE);
399 device.cmd_set_viewport(cmd, 0, &[viewport]);
400 device.cmd_set_scissor(cmd, 0, &[area]);
401 device.cmd_bind_vertex_buffers(cmd, 0, &[vertex_buffer], &[0]);
402 device.cmd_bind_index_buffer(cmd, index_buffer, 0, vk::IndexType::UINT32);
403
404 let recording = PassRecording {
405 device,
406 cmd,
407 layout,
408 area,
409 bindings,
410 materials,
411 };
412 let mut state = RecordedState::at_pass_start(area);
413 for (index, draw) in batch.draws().iter().enumerate() {
414 let pipeline = self
415 .pipeline_cache()
416 .pipeline(PipelineKey {
417 format,
418 program: draw.material.program(),
419 blend: draw.blend,
420 samples: pass.samples,
421 stencil: stencil_format,
422 role: draw.stencil.role,
423 })
424 .expect("ensured above");
425 record_draw(&recording, draw, index, pipeline, &mut state);
426 }
427
428 device.cmd_end_render_pass(cmd);
429 }
430
431 self.submit_one_shot(cmd)
432 })();
433
434 unsafe {
437 device.destroy_framebuffer(framebuffer, None);
438 device.destroy_image_view(view, None);
439 }
440 if let Some((tex, ms_view)) = multisample {
441 unsafe { device.destroy_image_view(ms_view, None) };
442 self.destroy_texture(tex);
443 }
444 if let Some(stencil) = stencil {
445 stencil.destroy(self);
446 }
447
448 if outcome.is_ok() {
449 target.set_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
453 }
454 outcome
455 }
456
457 fn stencil_format_for(&self, batch: &Batch) -> Result<Option<vk::Format>> {
466 if !batch.uses_stencil() {
467 return Ok(None);
468 }
469 batch.check_clip_depth()?;
470 crate::stencil::stencil_format(self).map(Some)
471 }
472
473 fn create_multisample_buffer(
474 &mut self,
475 target: &VulkanTexture,
476 samples: u32,
477 ) -> Result<(VulkanTexture, vk::ImageView)> {
478 let desc = TextureDescriptor {
479 extent: target.extent(),
480 format: target.format(),
481 usage: TextureUsage {
482 render_target: true,
483 ..TextureUsage::default()
484 },
485 sample_count: samples,
486 mip_levels: 1,
489 #[cfg(unix)]
490 external: None,
491 };
492 let texture = self.create_texture(&desc)?;
493
494 let device = self.raw_device().clone();
495 let info = vk::ImageViewCreateInfo::default()
496 .image(texture.raw_image())
497 .view_type(vk::ImageViewType::TYPE_2D)
498 .format(vk_format(target.format()))
499 .subresource_range(
500 vk::ImageSubresourceRange::default()
501 .aspect_mask(vk::ImageAspectFlags::COLOR)
502 .level_count(1)
503 .layer_count(1),
504 );
505 match unsafe { device.create_image_view(&info, None) } {
506 Ok(view) => Ok((texture, view)),
507 Err(e) => {
508 self.destroy_texture(texture);
509 Err(backend_err("create_image_view", e))
510 }
511 }
512 }
513
514 pub fn submit_batch_deferred(
526 &mut self,
527 target: &mut VulkanTexture,
528 batch: &Batch,
529 pass: PassDescriptor,
530 ) -> Result<crate::fence::VulkanFence> {
531 self.submit_batch_deferred_synchronized(target, batch, pass, &[], Default::default())
532 }
533
534 pub fn submit_batch_deferred_synchronized(
547 &mut self,
548 target: &mut VulkanTexture,
549 batch: &Batch,
550 pass: PassDescriptor,
551 textures: &[&VulkanTexture],
552 sync: crate::device::FrameSync<'_>,
553 ) -> Result<crate::fence::VulkanFence> {
554 if pass.is_multisampled() {
555 return Err(Error::Unsupported(
559 "deferred submission of a multisampled pass",
560 ));
561 }
562 let format = vk_format(target.format());
563 if batch.is_empty() {
564 return Err(Error::Unsupported("deferred submission of an empty batch"));
565 }
566
567 let stencil_format = self.stencil_format_for(batch)?;
568 let pass_key = RenderPassKey {
569 format,
570 clears: pass.clear.is_some(),
571 samples: 1,
572 presents: sync.presents,
573 stencil: stencil_format,
574 };
575 let render_pass = self.ensure_render_pass(pass_key)?;
576 for draw in batch.draws() {
577 self.ensure_pipeline(
578 PipelineKey {
579 format,
580 program: draw.material.program(),
581 blend: draw.blend,
582 samples: 1,
583 stencil: stencil_format,
584 role: draw.stencil.role,
585 },
586 render_pass,
587 )?;
588 }
589
590 let device = self.raw_device().clone();
591 let vertex_buffer = self.upload(
592 cast_bytes(batch.vertices()),
593 vk::BufferUsageFlags::VERTEX_BUFFER,
594 )?;
595 let index_buffer = self.upload(
596 cast_bytes(batch.indices()),
597 vk::BufferUsageFlags::INDEX_BUFFER,
598 )?;
599
600 let result = self.record_deferred(
601 &device,
602 target,
603 format,
604 render_pass,
605 batch,
606 vertex_buffer.buffer,
607 index_buffer.buffer,
608 pass,
609 stencil_format,
610 textures,
611 sync,
612 );
613
614 match result {
618 Ok(mut fence) => {
619 fence.retained.push(vertex_buffer);
622 fence.retained.push(index_buffer);
623 Ok(fence)
624 }
625 Err(e) => {
626 self.release(vertex_buffer);
627 self.release(index_buffer);
628 Err(e)
629 }
630 }
631 }
632
633 pub fn retire_fence(&mut self, mut fence: crate::fence::VulkanFence) {
635 fence.retire();
638 for buffer in std::mem::take(&mut fence.retained) {
639 self.release(buffer);
640 }
641 if let Some(bindings) = fence.bindings.take() {
643 bindings.destroy(self);
644 }
645 if let Some(materials) = fence.materials.take() {
646 materials.destroy(&self.raw_device().clone());
647 }
648 }
649
650 #[allow(clippy::too_many_arguments)]
651 fn record_deferred(
652 &mut self,
653 device: &ash::Device,
654 target: &mut VulkanTexture,
655 format: vk::Format,
656 render_pass: vk::RenderPass,
657 batch: &Batch,
658 vertex_buffer: vk::Buffer,
659 index_buffer: vk::Buffer,
660 pass: PassDescriptor,
661 stencil_format: Option<vk::Format>,
662 textures: &[&VulkanTexture],
663 sync: crate::device::FrameSync<'_>,
664 ) -> Result<crate::fence::VulkanFence> {
665 let bindings = crate::sampling::build(self, batch, textures)?;
670 let (material_buffer, materials) = crate::materials::build(self, batch, target.format())?;
671 let layout = self.pipeline_cache().layout().expect("ensured above");
672 let extent = target.extent();
673
674 let view_info = vk::ImageViewCreateInfo::default()
675 .image(target.raw_image())
676 .view_type(vk::ImageViewType::TYPE_2D)
677 .format(format)
678 .subresource_range(
679 vk::ImageSubresourceRange::default()
680 .aspect_mask(vk::ImageAspectFlags::COLOR)
681 .level_count(1)
682 .layer_count(1),
683 );
684 let view = unsafe { device.create_image_view(&view_info, None) }
685 .map_err(|e| backend_err("create_image_view", e))?;
686
687 let attachments = [view];
688 let fb_info = vk::FramebufferCreateInfo::default()
689 .render_pass(render_pass)
690 .attachments(&attachments)
691 .width(extent.width)
692 .height(extent.height)
693 .layers(1);
694 let framebuffer = match unsafe { device.create_framebuffer(&fb_info, None) } {
695 Ok(fb) => fb,
696 Err(e) => {
697 unsafe { device.destroy_image_view(view, None) };
698 return Err(backend_err("create_framebuffer", e));
699 }
700 };
701
702 let pipelines: Vec<vk::Pipeline> = batch
706 .draws()
707 .iter()
708 .map(|draw| {
709 self.pipeline_cache()
710 .pipeline(PipelineKey {
711 format,
712 program: draw.material.program(),
713 blend: draw.blend,
714 samples: 1,
715 stencil: stencil_format,
716 role: draw.stencil.role,
717 })
718 .expect("ensured above")
719 })
720 .collect();
721
722 let previous_layout = target.layout();
723 let cmd = self.begin_one_shot()?;
724
725 unsafe {
728 crate::sampling::transition_for_sampling(device, cmd, textures);
732
733 if pass.clear.is_none() {
734 transition(
735 device,
736 cmd,
737 target.raw_image(),
738 previous_layout,
739 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
740 );
741 }
742
743 let clear_values = [vk::ClearValue {
744 color: vk::ClearColorValue {
745 float32: pass.clear.unwrap_or([0.0; 4]),
746 },
747 }];
748 let area = vk::Rect2D {
749 offset: vk::Offset2D { x: 0, y: 0 },
750 extent: vk::Extent2D {
751 width: extent.width,
752 height: extent.height,
753 },
754 };
755 let begin = vk::RenderPassBeginInfo::default()
756 .render_pass(render_pass)
757 .framebuffer(framebuffer)
758 .render_area(area)
759 .clear_values(&clear_values);
760 let viewport = pass_viewport(pass, extent);
761
762 device.cmd_begin_render_pass(cmd, &begin, vk::SubpassContents::INLINE);
763 device.cmd_set_viewport(cmd, 0, &[viewport]);
764 device.cmd_set_scissor(cmd, 0, &[area]);
765 device.cmd_bind_vertex_buffers(cmd, 0, &[vertex_buffer], &[0]);
766 device.cmd_bind_index_buffer(cmd, index_buffer, 0, vk::IndexType::UINT32);
767
768 let recording = PassRecording {
769 device,
770 cmd,
771 layout,
772 area,
773 bindings: &bindings,
774 materials: &materials,
775 };
776 let mut state = RecordedState::at_pass_start(area);
777 for (index, (draw, pipeline)) in batch.draws().iter().zip(&pipelines).enumerate() {
778 record_draw(&recording, draw, index, *pipeline, &mut state);
779 }
780 device.cmd_end_render_pass(cmd);
781 }
782
783 let mut fence = self.submit_exportable(cmd, framebuffer, view, sync)?;
784 fence.bindings = Some(bindings);
785 fence.materials = Some(materials);
786 fence.retained.push(material_buffer);
787 target.set_layout(if sync.presents {
788 vk::ImageLayout::PRESENT_SRC_KHR
789 } else {
790 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL
791 });
792 Ok(fence)
793 }
794
795 fn ensure_render_pass(&mut self, key: RenderPassKey) -> Result<vk::RenderPass> {
796 if let Some(pass) = self.pipeline_cache().render_pass(key) {
797 return Ok(pass);
798 }
799 let device = self.raw_device().clone();
800 let pass = build_render_pass(&device, key)?;
801 self.pipeline_cache_mut().render_passes.insert(key, pass);
802 Ok(pass)
803 }
804
805 fn ensure_pipeline(&mut self, key: PipelineKey, render_pass: vk::RenderPass) -> Result<()> {
806 if self.pipeline_cache().pipeline(key).is_some() {
807 return Ok(());
808 }
809 let device = self.raw_device().clone();
810 let layout = match self.pipeline_cache().layout() {
811 Some(l) => l,
812 None => {
813 let descriptors = self.descriptor_layout()?;
817 let materials = self.material_layout()?;
818 let l = build_pipeline_layout(&device, descriptors, materials)?;
819 self.pipeline_cache_mut().layout = Some(l);
820 l
821 }
822 };
823 let fragment = match key.program {
826 Some(id) => Some(self.runtime_program(id)?.to_vec()),
827 None => None,
828 };
829 let pipeline = build_pipeline(&device, key, render_pass, layout, fragment.as_deref())?;
830 self.pipeline_cache_mut().pipelines.insert(key, pipeline);
831 Ok(())
832 }
833
834 pub(crate) fn upload(
835 &mut self,
836 bytes: &[u8],
837 usage: vk::BufferUsageFlags,
838 ) -> Result<StagedBuffer> {
839 let device = self.raw_device().clone();
840 let info = vk::BufferCreateInfo::default()
841 .size(bytes.len().max(1) as u64)
842 .usage(usage)
843 .sharing_mode(vk::SharingMode::EXCLUSIVE);
844 let buffer = unsafe { device.create_buffer(&info, None) }
845 .map_err(|e| backend_err("create_buffer", e))?;
846
847 let requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
848 let mut allocation = self
849 .allocator_mut()
850 .allocate(&AllocationCreateDesc {
851 name: "upload",
852 requirements,
853 location: MemoryLocation::CpuToGpu,
861 linear: true,
862 allocation_scheme: AllocationScheme::GpuAllocatorManaged,
863 })
864 .map_err(|e| {
865 unsafe { device.destroy_buffer(buffer, None) };
866 Error::Backend {
867 backend: "vulkan",
868 detail: format!("upload allocation: {e}"),
869 }
870 })?;
871
872 unsafe { device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset()) }
873 .map_err(|e| backend_err("bind_buffer_memory", e))?;
874
875 let slice = allocation.mapped_slice_mut().ok_or(Error::Backend {
876 backend: "vulkan",
877 detail: "upload allocation was not host-visible".into(),
878 })?;
879 slice[..bytes.len()].copy_from_slice(bytes);
880
881 Ok(StagedBuffer { buffer, allocation })
882 }
883
884 pub(crate) fn release(&mut self, staged: StagedBuffer) {
885 let _ = self.allocator_mut().free(staged.allocation);
886 unsafe { self.raw_device().destroy_buffer(staged.buffer, None) };
889 }
890}
891
892pub(crate) struct StagedBuffer {
893 pub(crate) buffer: vk::Buffer,
894 pub(crate) allocation: Allocation,
895}
896
897fn pass_viewport(pass: PassDescriptor, extent: Extent2D) -> vk::Viewport {
911 let (offset, size) = match pass.viewport {
912 Some(v) => (v.offset, v.extent),
913 None => ([0.0, 0.0], extent),
914 };
915 vk::Viewport {
916 x: offset[0],
917 y: offset[1],
918 width: size.width as f32,
919 height: size.height as f32,
920 min_depth: 0.0,
921 max_depth: 1.0,
922 }
923}
924
925fn build_render_pass(device: &ash::Device, key: RenderPassKey) -> Result<vk::RenderPass> {
926 let (load_op, initial_layout) = if key.clears {
930 (vk::AttachmentLoadOp::CLEAR, vk::ImageLayout::UNDEFINED)
931 } else {
932 (
933 vk::AttachmentLoadOp::LOAD,
934 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
935 )
936 };
937 let color = vk::AttachmentDescription::default()
938 .format(key.format)
939 .samples(sample_flags(key.samples))
940 .load_op(load_op)
941 .store_op(if key.samples > 1 {
946 vk::AttachmentStoreOp::DONT_CARE
947 } else {
948 vk::AttachmentStoreOp::STORE
949 })
950 .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
951 .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
952 .initial_layout(initial_layout)
953 .final_layout(final_layout(key, false));
954
955 let resolve = vk::AttachmentDescription::default()
959 .format(key.format)
960 .samples(vk::SampleCountFlags::TYPE_1)
961 .load_op(vk::AttachmentLoadOp::DONT_CARE)
962 .store_op(vk::AttachmentStoreOp::STORE)
963 .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
964 .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
965 .initial_layout(vk::ImageLayout::UNDEFINED)
966 .final_layout(final_layout(key, true));
967
968 let stencil = key.stencil.map(|format| {
975 vk::AttachmentDescription::default()
976 .format(format)
977 .samples(sample_flags(key.samples))
978 .load_op(vk::AttachmentLoadOp::DONT_CARE)
979 .store_op(vk::AttachmentStoreOp::DONT_CARE)
980 .stencil_load_op(vk::AttachmentLoadOp::CLEAR)
981 .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
982 .initial_layout(vk::ImageLayout::UNDEFINED)
983 .final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
984 });
985
986 let mut attachments: Vec<vk::AttachmentDescription> = if key.samples > 1 {
987 vec![color, resolve]
988 } else {
989 vec![color]
990 };
991 let stencil_index = attachments.len() as u32;
994 if let Some(stencil) = stencil {
995 attachments.push(stencil);
996 }
997
998 let color_refs = [vk::AttachmentReference::default()
999 .attachment(0)
1000 .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)];
1001 let resolve_refs = [vk::AttachmentReference::default()
1002 .attachment(1)
1003 .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)];
1004 let stencil_ref = vk::AttachmentReference::default()
1005 .attachment(stencil_index)
1006 .layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
1007
1008 let mut subpass = vk::SubpassDescription::default()
1009 .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
1010 .color_attachments(&color_refs);
1011 if key.samples > 1 {
1012 subpass = subpass.resolve_attachments(&resolve_refs);
1013 }
1014 if key.stencil.is_some() {
1015 subpass = subpass.depth_stencil_attachment(&stencil_ref);
1016 }
1017 let subpasses = [subpass];
1018 let attachment_stages = vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
1041 | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
1042 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
1043 let attachment_writes = vk::AccessFlags::COLOR_ATTACHMENT_WRITE
1044 | vk::AccessFlags::COLOR_ATTACHMENT_READ
1045 | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE
1046 | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ;
1047 let dependencies = [
1048 vk::SubpassDependency::default()
1049 .src_subpass(vk::SUBPASS_EXTERNAL)
1050 .dst_subpass(0)
1051 .src_stage_mask(attachment_stages)
1052 .dst_stage_mask(attachment_stages)
1053 .src_access_mask(vk::AccessFlags::empty())
1054 .dst_access_mask(attachment_writes),
1055 vk::SubpassDependency::default()
1056 .src_subpass(0)
1057 .dst_subpass(vk::SUBPASS_EXTERNAL)
1058 .src_stage_mask(attachment_stages)
1059 .dst_stage_mask(vk::PipelineStageFlags::FRAGMENT_SHADER)
1060 .src_access_mask(
1061 vk::AccessFlags::COLOR_ATTACHMENT_WRITE
1062 | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
1063 )
1064 .dst_access_mask(vk::AccessFlags::SHADER_READ),
1065 ];
1066 let info = vk::RenderPassCreateInfo::default()
1067 .attachments(&attachments)
1068 .subpasses(&subpasses)
1069 .dependencies(&dependencies);
1070
1071 unsafe { device.create_render_pass(&info, None) }
1072 .map_err(|e| backend_err("create_render_pass", e))
1073}
1074
1075fn final_layout(key: RenderPassKey, is_resolve: bool) -> vk::ImageLayout {
1082 let read_by_the_caller = is_resolve || key.samples == 1;
1083 if key.presents && read_by_the_caller {
1084 vk::ImageLayout::PRESENT_SRC_KHR
1085 } else {
1086 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL
1087 }
1088}
1089
1090pub(crate) fn sample_flags(count: u32) -> vk::SampleCountFlags {
1091 match count {
1092 2 => vk::SampleCountFlags::TYPE_2,
1093 4 => vk::SampleCountFlags::TYPE_4,
1094 8 => vk::SampleCountFlags::TYPE_8,
1095 16 => vk::SampleCountFlags::TYPE_16,
1096 _ => vk::SampleCountFlags::TYPE_1,
1097 }
1098}
1099
1100fn build_pipeline_layout(
1101 device: &ash::Device,
1102 descriptor_layout: vk::DescriptorSetLayout,
1103 material_layout: vk::DescriptorSetLayout,
1104) -> Result<vk::PipelineLayout> {
1105 let set_layouts = [descriptor_layout, material_layout];
1114 let info = vk::PipelineLayoutCreateInfo::default().set_layouts(&set_layouts);
1115 unsafe { device.create_pipeline_layout(&info, None) }
1116 .map_err(|e| backend_err("create_pipeline_layout", e))
1117}
1118
1119fn build_pipeline(
1120 device: &ash::Device,
1121 key: PipelineKey,
1122 render_pass: vk::RenderPass,
1123 layout: vk::PipelineLayout,
1124 fragment: Option<&[u32]>,
1125) -> Result<vk::Pipeline> {
1126 let module_info = vk::ShaderModuleCreateInfo::default().code(impeller_shaders::SOLID_SPV);
1131 let module = unsafe { device.create_shader_module(&module_info, None) }
1132 .map_err(|e| backend_err("create_shader_module", e))?;
1133 let fragment_module = match fragment {
1134 Some(code) => {
1135 let info = vk::ShaderModuleCreateInfo::default().code(code);
1136 Some(
1137 unsafe { device.create_shader_module(&info, None) }
1138 .map_err(|e| backend_err("create_shader_module", e))?,
1139 )
1140 }
1141 None => None,
1142 };
1143 let fs_module = fragment_module.unwrap_or(module);
1144
1145 let vs_name = c"vs_main";
1146 let fs_name = c"fs_main";
1147 let stages = [
1148 vk::PipelineShaderStageCreateInfo::default()
1149 .stage(vk::ShaderStageFlags::VERTEX)
1150 .module(module)
1151 .name(vs_name),
1152 vk::PipelineShaderStageCreateInfo::default()
1153 .stage(vk::ShaderStageFlags::FRAGMENT)
1154 .module(fs_module)
1155 .name(fs_name),
1156 ];
1157
1158 let bindings = [vk::VertexInputBindingDescription::default()
1159 .binding(0)
1160 .stride(std::mem::size_of::<impeller_hal::Vertex>() as u32)
1161 .input_rate(vk::VertexInputRate::VERTEX)];
1162 let attributes = [
1168 vk::VertexInputAttributeDescription::default()
1169 .location(0)
1170 .binding(0)
1171 .format(vk::Format::R32G32B32_SFLOAT)
1172 .offset(std::mem::offset_of!(impeller_hal::Vertex, position) as u32),
1173 vk::VertexInputAttributeDescription::default()
1174 .location(1)
1175 .binding(0)
1176 .format(vk::Format::R32G32_SFLOAT)
1177 .offset(std::mem::offset_of!(impeller_hal::Vertex, uv) as u32),
1178 vk::VertexInputAttributeDescription::default()
1179 .location(2)
1180 .binding(0)
1181 .format(vk::Format::R32G32B32A32_SFLOAT)
1182 .offset(std::mem::offset_of!(impeller_hal::Vertex, color) as u32),
1183 ];
1184 let vertex_input = vk::PipelineVertexInputStateCreateInfo::default()
1185 .vertex_binding_descriptions(&bindings)
1186 .vertex_attribute_descriptions(&attributes);
1187
1188 let assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
1189 .topology(vk::PrimitiveTopology::TRIANGLE_LIST);
1190 let viewport_state = vk::PipelineViewportStateCreateInfo::default()
1191 .viewport_count(1)
1192 .scissor_count(1);
1193 let raster = vk::PipelineRasterizationStateCreateInfo::default()
1194 .cull_mode(vk::CullModeFlags::NONE)
1198 .front_face(vk::FrontFace::COUNTER_CLOCKWISE)
1199 .polygon_mode(vk::PolygonMode::FILL)
1200 .line_width(1.0);
1201 let multisample = vk::PipelineMultisampleStateCreateInfo::default()
1202 .rasterization_samples(sample_flags(key.samples));
1203
1204 let attachment = vk::PipelineColorBlendAttachmentState::default().color_write_mask(
1214 if key.role.writes_color() {
1215 vk::ColorComponentFlags::RGBA
1216 } else {
1217 vk::ColorComponentFlags::empty()
1218 },
1219 );
1220 let blend_attachments = [match key.blend.factors() {
1221 _ if key.blend.is_plain_write() => {
1222 attachment.blend_enable(false)
1225 }
1226 Some(factors) => {
1227 let src = vk_blend_factor(factors.src);
1228 let dst = vk_blend_factor(factors.dst);
1229 attachment
1230 .blend_enable(true)
1231 .src_color_blend_factor(src)
1232 .dst_color_blend_factor(dst)
1233 .color_blend_op(vk::BlendOp::ADD)
1234 .src_alpha_blend_factor(src)
1239 .dst_alpha_blend_factor(dst)
1240 .alpha_blend_op(vk::BlendOp::ADD)
1241 }
1242 None => attachment
1247 .blend_enable(true)
1248 .src_color_blend_factor(vk::BlendFactor::ONE)
1249 .dst_color_blend_factor(vk::BlendFactor::ZERO)
1250 .color_blend_op(vk_advanced_blend_op(key.blend))
1251 .src_alpha_blend_factor(vk::BlendFactor::ONE)
1252 .dst_alpha_blend_factor(vk::BlendFactor::ZERO)
1253 .alpha_blend_op(vk_advanced_blend_op(key.blend)),
1254 }];
1255 let mut blend =
1256 vk::PipelineColorBlendStateCreateInfo::default().attachments(&blend_attachments);
1257
1258 let mut advanced = vk::PipelineColorBlendAdvancedStateCreateInfoEXT::default()
1264 .src_premultiplied(true)
1265 .dst_premultiplied(true)
1266 .blend_overlap(vk::BlendOverlapEXT::UNCORRELATED);
1267 if key.blend.is_advanced() {
1268 blend = blend.push_next(&mut advanced);
1269 }
1270
1271 let stencil_op = vk::StencilOpState::default()
1275 .compare_op(vk::CompareOp::EQUAL)
1276 .fail_op(vk::StencilOp::KEEP)
1277 .depth_fail_op(vk::StencilOp::KEEP)
1278 .pass_op(match key.role {
1279 ClipRole::Content => vk::StencilOp::KEEP,
1280 ClipRole::Narrow => vk::StencilOp::INCREMENT_AND_CLAMP,
1286 ClipRole::Widen => vk::StencilOp::DECREMENT_AND_CLAMP,
1287 })
1288 .compare_mask(0xff)
1289 .write_mask(if key.role.writes_stencil() { 0xff } else { 0 });
1290 let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default()
1291 .stencil_test_enable(key.stencil.is_some())
1292 .front(stencil_op)
1293 .back(stencil_op);
1294
1295 let dynamic_states = [
1296 vk::DynamicState::VIEWPORT,
1297 vk::DynamicState::SCISSOR,
1298 vk::DynamicState::STENCIL_REFERENCE,
1299 ];
1300 let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
1301
1302 let info = vk::GraphicsPipelineCreateInfo::default()
1303 .stages(&stages)
1304 .vertex_input_state(&vertex_input)
1305 .input_assembly_state(&assembly)
1306 .viewport_state(&viewport_state)
1307 .rasterization_state(&raster)
1308 .multisample_state(&multisample)
1309 .depth_stencil_state(&depth_stencil)
1310 .color_blend_state(&blend)
1311 .dynamic_state(&dynamic)
1312 .layout(layout)
1313 .render_pass(render_pass)
1314 .subpass(0);
1315
1316 let created =
1317 unsafe { device.create_graphics_pipelines(vk::PipelineCache::null(), &[info], None) };
1318
1319 unsafe { device.destroy_shader_module(module, None) };
1321 if let Some(fragment) = fragment_module {
1322 unsafe { device.destroy_shader_module(fragment, None) };
1323 }
1324
1325 match created {
1326 Ok(pipelines) => Ok(pipelines[0]),
1327 Err((_, e)) => Err(backend_err("create_graphics_pipelines", e)),
1328 }
1329}
1330
1331struct PassRecording<'a> {
1333 device: &'a ash::Device,
1334 cmd: vk::CommandBuffer,
1335 layout: vk::PipelineLayout,
1336 area: vk::Rect2D,
1338 bindings: &'a crate::sampling::Bindings,
1339 materials: &'a crate::materials::Materials,
1340}
1341
1342struct RecordedState {
1347 pipeline: Option<vk::Pipeline>,
1348 descriptor_set: Option<vk::DescriptorSet>,
1349 scissor: vk::Rect2D,
1350 stencil_reference: Option<u32>,
1353}
1354
1355impl RecordedState {
1356 fn at_pass_start(area: vk::Rect2D) -> Self {
1359 Self {
1360 pipeline: None,
1361 descriptor_set: None,
1362 scissor: area,
1363 stencil_reference: None,
1364 }
1365 }
1366}
1367
1368unsafe fn record_draw(
1380 pass: &PassRecording,
1381 draw: &impeller_hal::BatchDraw,
1382 index: usize,
1383 pipeline: vk::Pipeline,
1384 state: &mut RecordedState,
1385) {
1386 let PassRecording {
1387 device,
1388 cmd,
1389 layout,
1390 area,
1391 bindings,
1392 materials,
1393 } = *pass;
1394 unsafe {
1395 if state.pipeline != Some(pipeline) {
1396 device.cmd_bind_pipeline(cmd, vk::PipelineBindPoint::GRAPHICS, pipeline);
1397 state.pipeline = Some(pipeline);
1398 }
1399
1400 let wanted = match draw.clip {
1407 Some(clip) => {
1408 let clip = clip.clamped_to(impeller_hal::Extent2D::new(
1409 area.extent.width,
1410 area.extent.height,
1411 ));
1412 vk::Rect2D {
1413 offset: vk::Offset2D {
1414 x: clip.x as i32,
1415 y: clip.y as i32,
1416 },
1417 extent: vk::Extent2D {
1418 width: clip.width,
1419 height: clip.height,
1420 },
1421 }
1422 }
1423 None => area,
1424 };
1425 if state.scissor != wanted {
1426 device.cmd_set_scissor(cmd, 0, &[wanted]);
1427 state.scissor = wanted;
1428 }
1429
1430 let set = bindings.set_for(draw.material.texture_slots());
1435 if state.descriptor_set != Some(set) {
1436 device.cmd_bind_descriptor_sets(
1437 cmd,
1438 vk::PipelineBindPoint::GRAPHICS,
1439 layout,
1440 0,
1441 &[set],
1442 &[],
1443 );
1444 state.descriptor_set = Some(set);
1445 }
1446
1447 let reference = draw.stencil.reference;
1453 if state.stencil_reference != Some(reference) {
1454 device.cmd_set_stencil_reference(cmd, vk::StencilFaceFlags::FRONT_AND_BACK, reference);
1455 state.stencil_reference = Some(reference);
1456 }
1457
1458 device.cmd_bind_descriptor_sets(
1464 cmd,
1465 vk::PipelineBindPoint::GRAPHICS,
1466 layout,
1467 1,
1468 &[materials.set()],
1469 &[materials.offset(index)],
1470 );
1471 device.cmd_draw_indexed(cmd, draw.index_count, 1, draw.first_index, 0, 0);
1472 }
1473}
1474
1475fn vk_advanced_blend_op(mode: impeller_hal::BlendMode) -> vk::BlendOp {
1484 use impeller_hal::BlendMode;
1485 match mode {
1486 BlendMode::Multiply => vk::BlendOp::MULTIPLY_EXT,
1487 BlendMode::Screen => vk::BlendOp::SCREEN_EXT,
1488 BlendMode::Overlay => vk::BlendOp::OVERLAY_EXT,
1489 BlendMode::Darken => vk::BlendOp::DARKEN_EXT,
1490 BlendMode::Lighten => vk::BlendOp::LIGHTEN_EXT,
1491 BlendMode::ColorDodge => vk::BlendOp::COLORDODGE_EXT,
1492 BlendMode::ColorBurn => vk::BlendOp::COLORBURN_EXT,
1493 BlendMode::HardLight => vk::BlendOp::HARDLIGHT_EXT,
1494 BlendMode::SoftLight => vk::BlendOp::SOFTLIGHT_EXT,
1495 BlendMode::Difference => vk::BlendOp::DIFFERENCE_EXT,
1496 BlendMode::Exclusion => vk::BlendOp::EXCLUSION_EXT,
1497 BlendMode::Hue => vk::BlendOp::HSL_HUE_EXT,
1501 BlendMode::Saturation => vk::BlendOp::HSL_SATURATION_EXT,
1502 BlendMode::Color => vk::BlendOp::HSL_COLOR_EXT,
1503 BlendMode::Luminosity => vk::BlendOp::HSL_LUMINOSITY_EXT,
1504 other => unreachable!("{other} is not an advanced blend mode"),
1505 }
1506}
1507
1508fn vk_blend_factor(factor: impeller_hal::BlendFactor) -> vk::BlendFactor {
1510 use impeller_hal::BlendFactor;
1511 match factor {
1512 BlendFactor::Zero => vk::BlendFactor::ZERO,
1513 BlendFactor::One => vk::BlendFactor::ONE,
1514 BlendFactor::SrcAlpha => vk::BlendFactor::SRC_ALPHA,
1515 BlendFactor::OneMinusSrcAlpha => vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
1516 BlendFactor::DstAlpha => vk::BlendFactor::DST_ALPHA,
1517 BlendFactor::OneMinusDstAlpha => vk::BlendFactor::ONE_MINUS_DST_ALPHA,
1518 BlendFactor::DstColor => vk::BlendFactor::DST_COLOR,
1519 }
1520}
1521
1522pub(crate) fn cast_bytes<T>(slice: &[T]) -> &[u8] {
1524 unsafe { std::slice::from_raw_parts(slice.as_ptr() as *const u8, std::mem::size_of_val(slice)) }
1528}