1use std::collections::{HashMap, HashSet};
2use std::path::PathBuf;
3use std::sync::{Arc, Mutex};
4
5use ash::vk;
6use damascene_core::event::{KeyChord, KeyModifiers, LogicalKey, PhysicalKey, Pointer, UiEvent};
7use damascene_core::icons::svg::IconSource;
8use damascene_core::ir::TextAnchor;
9use damascene_core::paint::{IconRunKind, PaintItem, PhysicalScissor, QuadInstance};
10use damascene_core::runtime::{RecordedPaint, RunnerCore, TextRecorder};
11use damascene_core::shader::{ShaderHandle, StockShader, stock_wgsl};
12use damascene_core::state::{AnimationMode, UiState};
13use damascene_core::surface::SurfaceAlpha;
14use damascene_core::text::atlas::RunStyle;
15use damascene_core::theme::Theme;
16use damascene_core::tree::{Color, El, FontWeight, Rect, TextWrap};
17use damascene_core::vector::IconMaterial;
18use gpu_allocator::MemoryLocation;
19use gpu_allocator::vulkan::Allocator;
20use web_time::Instant;
21
22use crate::buffer::GpuBuffer;
23use crate::icon::IconPaint;
24use crate::image::{ImagePaint, ImageRecord};
25use crate::naga_compile::{CompileError, wgsl_to_spirv};
26use crate::pipeline::{FrameUniforms, build_quad_pipeline, build_quad_pipeline_from_spirv};
27use crate::scene::Scene3DPaint;
28use crate::surface::SurfacePaint;
29use crate::text::{TextPaint, TextRunKind};
30
31const INITIAL_INSTANCE_CAPACITY: usize = 256;
32const QUAD_VERTICES: [f32; 8] = [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0];
33
34pub type Result<T> = std::result::Result<T, Error>;
35
36#[derive(Debug)]
38pub enum Error {
39 ShaderCompile(CompileError),
40 Vulkan {
41 op: &'static str,
42 result: vk::Result,
43 },
44 Allocation(gpu_allocator::AllocationError),
45 AllocatorPoisoned,
46 BufferTooSmall {
47 requested: usize,
48 capacity: usize,
49 },
50 UnmappedAllocation,
51 ResourceDestroyed(&'static str),
52 IncompatibleTarget {
53 expected_format: vk::Format,
54 actual_format: vk::Format,
55 expected_sample_count: vk::SampleCountFlags,
56 actual_sample_count: vk::SampleCountFlags,
57 },
58 MissingPipeline(ShaderHandle),
59 PipelineCreationReturnedEmpty {
60 name: String,
61 },
62 Unsupported(&'static str),
63}
64
65impl std::fmt::Display for Error {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 match self {
68 Error::ShaderCompile(err) => err.fmt(f),
69 Error::Vulkan { op, result } => write!(f, "Vulkan error in {op}: {result:?}"),
70 Error::Allocation(err) => err.fmt(f),
71 Error::AllocatorPoisoned => write!(f, "Vulkan allocator mutex was poisoned"),
72 Error::BufferTooSmall {
73 requested,
74 capacity,
75 } => {
76 write!(
77 f,
78 "buffer upload requested {requested} bytes but capacity is {capacity} bytes"
79 )
80 }
81 Error::UnmappedAllocation => write!(f, "GPU allocation is not host-mapped"),
82 Error::ResourceDestroyed(name) => write!(f, "resource was already destroyed: {name}"),
83 Error::IncompatibleTarget {
84 expected_format,
85 actual_format,
86 expected_sample_count,
87 actual_sample_count,
88 } => write!(
89 f,
90 "render target is incompatible: expected {expected_format:?}/{expected_sample_count:?}, got {actual_format:?}/{actual_sample_count:?}"
91 ),
92 Error::MissingPipeline(handle) => {
93 write!(f, "no ash pipeline registered for {handle:?}")
94 }
95 Error::PipelineCreationReturnedEmpty { name } => {
96 write!(
97 f,
98 "Vulkan pipeline creation for `{name}` returned no pipeline"
99 )
100 }
101 Error::Unsupported(message) => write!(f, "{message}"),
102 }
103 }
104}
105
106impl std::error::Error for Error {
107 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
108 match self {
109 Error::ShaderCompile(err) => Some(err),
110 Error::Allocation(err) => Some(err),
111 Error::Unsupported(_) => None,
112 _ => None,
113 }
114 }
115}
116
117impl From<CompileError> for Error {
118 fn from(value: CompileError) -> Self {
119 Error::ShaderCompile(value)
120 }
121}
122
123impl From<vk::Result> for Error {
124 fn from(value: vk::Result) -> Self {
125 Error::Vulkan {
126 op: "vulkan call",
127 result: value,
128 }
129 }
130}
131
132impl From<gpu_allocator::AllocationError> for Error {
133 fn from(value: gpu_allocator::AllocationError) -> Self {
134 Error::Allocation(value)
135 }
136}
137
138pub struct AshContext {
146 pub device: Arc<ash::Device>,
147 pub allocator: Arc<Mutex<Allocator>>,
148 pub queue_family_index: u32,
149 pub max_image_dimension_2d: u32,
157}
158
159impl AshContext {
160 pub fn new(
161 device: Arc<ash::Device>,
162 allocator: Arc<Mutex<Allocator>>,
163 queue_family_index: u32,
164 max_image_dimension_2d: u32,
165 ) -> Self {
166 Self {
167 device,
168 allocator,
169 queue_family_index,
170 max_image_dimension_2d,
171 }
172 }
173}
174
175#[derive(Clone, Copy, Debug)]
177pub struct TargetInfo {
178 pub format: vk::Format,
179 pub sample_count: vk::SampleCountFlags,
180}
181
182impl TargetInfo {
183 pub fn new(format: vk::Format) -> Self {
184 Self {
185 format,
186 sample_count: vk::SampleCountFlags::TYPE_1,
187 }
188 }
189
190 pub fn with_sample_count(mut self, sample_count: vk::SampleCountFlags) -> Self {
191 self.sample_count = sample_count;
192 self
193 }
194}
195
196#[derive(Clone, Copy, Debug)]
198pub struct AshRenderTarget {
199 pub image: vk::Image,
200 pub view: vk::ImageView,
201 pub format: vk::Format,
202 pub extent: vk::Extent2D,
203 pub sample_count: vk::SampleCountFlags,
204 pub initial_layout: vk::ImageLayout,
206 pub final_layout: vk::ImageLayout,
208}
209
210impl AshRenderTarget {
211 pub fn color_attachment(
212 image: vk::Image,
213 view: vk::ImageView,
214 format: vk::Format,
215 extent: vk::Extent2D,
216 ) -> Self {
217 Self {
218 image,
219 view,
220 format,
221 extent,
222 sample_count: vk::SampleCountFlags::TYPE_1,
223 initial_layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
224 final_layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
225 }
226 }
227}
228
229#[derive(Clone, Copy, Debug)]
231pub enum LoadOp {
232 Clear([f32; 4]),
233 Load,
234}
235
236#[derive(Clone, Copy, Debug, Default)]
238pub struct PreparedFrame {
239 pub quad_instances: usize,
240 pub paint_items: usize,
241 pub runs: usize,
242}
243
244pub struct Runner {
246 context: AshContext,
247 target: TargetInfo,
248 frame_buf: GpuBuffer,
249 quad_vbo: GpuBuffer,
250 instance_buf: GpuBuffer,
251 instance_capacity: usize,
252 descriptor_set_layout: vk::DescriptorSetLayout,
253 descriptor_pool: vk::DescriptorPool,
254 descriptor_set: vk::DescriptorSet,
255 pipeline_layout: vk::PipelineLayout,
256 pipelines: HashMap<ShaderHandle, vk::Pipeline>,
257 text_paint: TextPaint,
258 icon_paint: IconPaint,
259 image_paint: ImagePaint,
260 surface_paint: SurfacePaint,
261 scene_paint: Scene3DPaint,
262 backdrop_shaders: HashSet<&'static str>,
263 time_shaders: HashSet<&'static str>,
264 start_time: Instant,
265
266 white_scale: f32,
271 headroom: f32,
277 ref_nits: f32,
278 core: RunnerCore,
279}
280
281impl Runner {
282 pub fn new(context: AshContext, target: TargetInfo) -> Result<Self> {
283 let (frame_buf, quad_vbo, instance_buf) = {
284 let mut allocator = context
285 .allocator
286 .lock()
287 .map_err(|_| Error::AllocatorPoisoned)?;
288 let frame_buf = GpuBuffer::new(
289 &context.device,
290 &mut allocator,
291 "damascene_ash::frame_uniforms",
292 std::mem::size_of::<FrameUniforms>() as vk::DeviceSize,
293 vk::BufferUsageFlags::UNIFORM_BUFFER,
294 MemoryLocation::CpuToGpu,
295 )?;
296 let mut quad_vbo = GpuBuffer::new(
297 &context.device,
298 &mut allocator,
299 "damascene_ash::quad_vbo",
300 std::mem::size_of_val(&QUAD_VERTICES) as vk::DeviceSize,
301 vk::BufferUsageFlags::VERTEX_BUFFER,
302 MemoryLocation::CpuToGpu,
303 )?;
304 quad_vbo.write_bytes(bytemuck::cast_slice(&QUAD_VERTICES))?;
305 let instance_buf = GpuBuffer::new(
306 &context.device,
307 &mut allocator,
308 "damascene_ash::instance_buf",
309 (INITIAL_INSTANCE_CAPACITY * std::mem::size_of::<QuadInstance>()) as vk::DeviceSize,
310 vk::BufferUsageFlags::VERTEX_BUFFER,
311 MemoryLocation::CpuToGpu,
312 )?;
313 (frame_buf, quad_vbo, instance_buf)
314 };
315
316 let descriptor_set_layout = create_frame_descriptor_set_layout(&context.device)?;
317 let descriptor_pool = create_descriptor_pool(&context.device)?;
318 let descriptor_set =
319 allocate_frame_descriptor_set(&context.device, descriptor_pool, descriptor_set_layout)?;
320 update_frame_descriptor_set(&context.device, descriptor_set, frame_buf.buffer);
321 let pipeline_layout = create_pipeline_layout(&context.device, descriptor_set_layout)?;
322 let text_paint = {
323 let mut allocator = context
324 .allocator
325 .lock()
326 .map_err(|_| Error::AllocatorPoisoned)?;
327 TextPaint::new(
328 &context.device,
329 &mut allocator,
330 descriptor_set_layout,
331 target,
332 )?
333 };
334 let icon_paint = {
335 let mut allocator = context
336 .allocator
337 .lock()
338 .map_err(|_| Error::AllocatorPoisoned)?;
339 IconPaint::new(
340 &context.device,
341 &mut allocator,
342 descriptor_set_layout,
343 target,
344 )?
345 };
346 let image_paint = {
347 let mut allocator = context
348 .allocator
349 .lock()
350 .map_err(|_| Error::AllocatorPoisoned)?;
351 ImagePaint::new(
352 &context.device,
353 &mut allocator,
354 descriptor_set_layout,
355 target,
356 context.max_image_dimension_2d,
357 )?
358 };
359 let surface_paint = {
360 let mut allocator = context
361 .allocator
362 .lock()
363 .map_err(|_| Error::AllocatorPoisoned)?;
364 SurfacePaint::new(
365 &context.device,
366 &mut allocator,
367 descriptor_set_layout,
368 target,
369 )?
370 };
371 let scene_paint = {
372 let mut allocator = context
373 .allocator
374 .lock()
375 .map_err(|_| Error::AllocatorPoisoned)?;
376 Scene3DPaint::new(
377 &context.device,
378 &mut allocator,
379 descriptor_set_layout,
380 target,
381 damascene_core::paint::DEFAULT_WORKING_COLOR_SPACE,
382 )?
383 };
384
385 let mut runner = Self {
386 context,
387 target,
388 frame_buf,
389 quad_vbo,
390 instance_buf,
391 instance_capacity: INITIAL_INSTANCE_CAPACITY,
392 descriptor_set_layout,
393 descriptor_pool,
394 descriptor_set,
395 pipeline_layout,
396 pipelines: HashMap::new(),
397 text_paint,
398 icon_paint,
399 image_paint,
400 surface_paint,
401 scene_paint,
402 backdrop_shaders: HashSet::new(),
403 time_shaders: HashSet::new(),
404 start_time: Instant::now(),
405 white_scale: 1.0,
406 headroom: 1.0,
407 ref_nits: damascene_core::color::BT2408_REFERENCE_WHITE_NITS,
408 core: RunnerCore::new(),
409 };
410 runner.register_stock_shaders()?;
411 Ok(runner)
412 }
413
414 pub fn target(&self) -> TargetInfo {
415 self.target
416 }
417
418 pub fn device(&self) -> &ash::Device {
419 &self.context.device
420 }
421
422 pub fn queue_family_index(&self) -> u32 {
423 self.context.queue_family_index
424 }
425
426 pub fn set_surface_size(&mut self, width: u32, height: u32) {
427 self.core.set_surface_size(width, height);
428 }
429
430 pub fn set_working_color_space(&mut self, space: damascene_core::color::ColorSpace) {
442 self.core.set_working_color_space(space);
443 self.text_paint.set_working_color_space(space);
444 self.icon_paint.set_working_color_space(space);
445 self.image_paint.set_working_color_space(space);
446 self.scene_paint.set_working_color_space(space);
447 }
448
449 pub fn working_color_space(&self) -> damascene_core::color::ColorSpace {
451 self.core.working_color_space()
452 }
453
454 pub fn set_white_scale(&mut self, scale: f32) {
461 self.white_scale = scale;
462 }
463
464 pub fn set_output_luminance(&mut self, headroom: f32, reference_nits: f32) {
475 self.headroom = headroom;
476 self.ref_nits = reference_nits;
477 self.image_paint.set_headroom(headroom);
478 }
479
480 pub fn set_theme(&mut self, theme: Theme) {
481 self.icon_paint.set_material(theme.icon_material());
482 self.core.set_theme(theme);
483 }
484
485 pub fn theme(&self) -> &Theme {
486 self.core.theme()
487 }
488
489 pub fn set_icon_material(&mut self, material: IconMaterial) {
491 self.icon_paint.set_material(material);
492 }
493
494 pub fn icon_material(&self) -> IconMaterial {
495 self.icon_paint.material()
496 }
497
498 pub fn register_shader(&mut self, name: &'static str, wgsl: &str) -> Result<()> {
499 self.register_shader_with(name, wgsl, false, false)
500 }
501
502 pub fn register_shader_with(
503 &mut self,
504 name: &'static str,
505 wgsl: &str,
506 samples_backdrop: bool,
507 samples_time: bool,
508 ) -> Result<()> {
509 if samples_backdrop {
510 return Err(Error::Unsupported(
511 "damascene-ash backdrop-sampling custom shaders are not implemented yet",
512 ));
513 }
514 let words = wgsl_to_spirv(name, wgsl)?;
515 let pipeline = build_quad_pipeline_from_spirv(
516 &self.context.device,
517 self.pipeline_layout,
518 self.target,
519 name,
520 &words,
521 )?;
522 if let Some(old) = self.pipelines.insert(ShaderHandle::Custom(name), pipeline) {
523 unsafe {
524 self.context.device.destroy_pipeline(old, None);
525 }
526 }
527 self.backdrop_shaders.remove(name);
528 if samples_time {
529 self.time_shaders.insert(name);
530 } else {
531 self.time_shaders.remove(name);
532 }
533 match damascene_core::paint::slots::introspect_wgsl(wgsl) {
537 Ok(map) => self.core.register_shader_slots(name, map),
538 Err(e) => log::warn!(
539 "damascene-ash: could not introspect shader `{name}` for named uniform \
540 routing ({e}); positional vec_a..vec_e routing still applies"
541 ),
542 }
543 Ok(())
544 }
545
546 pub fn ui_state(&self) -> &UiState {
547 self.core.ui_state()
548 }
549
550 pub fn debug_summary(&self) -> String {
551 self.core.debug_summary()
552 }
553
554 pub fn rect_of_key(&self, key: &str) -> Option<Rect> {
555 self.core.rect_of_key(key)
556 }
557
558 pub fn snapshot_cursor(&self) -> damascene_core::cursor::Cursor {
562 self.core.snapshot_cursor()
563 }
564
565 pub fn prepared_frame(&self) -> PreparedFrame {
566 PreparedFrame {
567 quad_instances: self.core.quad_scratch.len(),
568 paint_items: self.core.paint_items.len(),
569 runs: self.core.runs.len(),
570 }
571 }
572
573 pub fn prepare(
586 &mut self,
587 mut root: El,
588 viewport: Rect,
589 scale_factor: f32,
590 ) -> damascene_core::runtime::PrepareResult {
591 let mut timings = damascene_core::runtime::PrepareTimings::default();
592
593 let ready_depth = self.scene_paint.collect_depth_maps();
598 if !ready_depth.is_empty() {
599 let depth_maps = self.core.ui_state.scene_depth_mut();
600 for (id, map) in ready_depth {
601 depth_maps.insert(id, map);
602 }
603 }
604 self.core
605 .ui_state
606 .scene_depth_mut()
607 .retain(|id, _| self.scene_paint.has_target(id));
608
609 let time_shaders = &self.time_shaders;
610 let damascene_core::runtime::LayoutPrepared {
611 ops,
612 mut needs_redraw,
613 mut next_layout_redraw_in,
614 next_paint_redraw_in,
615 } = self.core.prepare_layout(
616 &mut root,
617 viewport,
618 scale_factor,
619 &mut timings,
620 |handle| matches!(handle, ShaderHandle::Custom(name) if time_shaders.contains(name)),
621 );
622
623 self.text_paint.frame_begin();
624 self.icon_paint.frame_begin();
625 self.image_paint.frame_begin();
626 self.surface_paint.frame_begin();
627 self.scene_paint.frame_begin();
628 let pipelines = &self.pipelines;
629 let backdrop_shaders = &self.backdrop_shaders;
630 {
631 let mut allocator = self
632 .context
633 .allocator
634 .lock()
635 .expect("damascene-ash: allocator mutex poisoned during paint recording");
636 let mut recorder = PaintRecorder {
637 text: &mut self.text_paint,
638 icons: &mut self.icon_paint,
639 images: &mut self.image_paint,
640 surfaces: &mut self.surface_paint,
641 scenes: &mut self.scene_paint,
642 device: &self.context.device,
643 allocator: &mut allocator,
644 };
645 self.core.prepare_paint(
646 &ops,
647 |shader| match shader {
648 ShaderHandle::Stock(StockShader::RoundedRect)
649 | ShaderHandle::Stock(StockShader::Spinner)
650 | ShaderHandle::Stock(StockShader::Skeleton)
651 | ShaderHandle::Stock(StockShader::ProgressIndeterminate) => {
652 pipelines.contains_key(shader)
653 }
654 ShaderHandle::Custom(_) => pipelines.contains_key(shader),
655 _ => false,
656 },
657 |shader| matches!(shader, ShaderHandle::Custom(name) if backdrop_shaders.contains(name)),
658 &mut recorder,
659 scale_factor,
660 &mut timings,
661 );
662 }
663 let t_gpu_start = Instant::now();
664 self.upload_frame_data(viewport, scale_factor)
665 .expect("damascene-ash: failed to upload prepared frame data");
666 timings.gpu_upload = Instant::now() - t_gpu_start;
667
668 self.core.snapshot_owned(root, &mut timings);
669 self.core.last_ops = ops;
670
671 if self.scene_paint.occlusion_unsettled() {
678 needs_redraw = true;
679 next_layout_redraw_in = Some(std::time::Duration::ZERO);
680 }
681
682 let next_redraw_in = match (next_layout_redraw_in, next_paint_redraw_in) {
683 (Some(a), Some(b)) => Some(a.min(b)),
684 (a, b) => a.or(b),
685 };
686
687 damascene_core::runtime::PrepareResult {
688 needs_redraw,
689 next_redraw_in,
690 next_layout_redraw_in,
691 next_paint_redraw_in,
692 timings,
693 }
694 }
695
696 pub fn repaint(
697 &mut self,
698 _viewport: Rect,
699 scale_factor: f32,
700 ) -> damascene_core::runtime::PrepareResult {
701 let mut timings = damascene_core::runtime::PrepareTimings::default();
702 let time_shaders = &self.time_shaders;
703 self.text_paint.frame_begin();
704 self.icon_paint.frame_begin();
705 self.image_paint.frame_begin();
706 self.surface_paint.frame_begin();
707 self.scene_paint.frame_begin();
708 let pipelines = &self.pipelines;
709 let backdrop_shaders = &self.backdrop_shaders;
710 {
711 let mut allocator = self
712 .context
713 .allocator
714 .lock()
715 .expect("damascene-ash: allocator mutex poisoned during paint recording");
716 let mut recorder = PaintRecorder {
717 text: &mut self.text_paint,
718 icons: &mut self.icon_paint,
719 images: &mut self.image_paint,
720 surfaces: &mut self.surface_paint,
721 scenes: &mut self.scene_paint,
722 device: &self.context.device,
723 allocator: &mut allocator,
724 };
725 self.core.prepare_paint_cached(
726 |shader| match shader {
727 ShaderHandle::Stock(StockShader::RoundedRect)
728 | ShaderHandle::Stock(StockShader::Spinner)
729 | ShaderHandle::Stock(StockShader::Skeleton)
730 | ShaderHandle::Stock(StockShader::ProgressIndeterminate) => {
731 pipelines.contains_key(shader)
732 }
733 ShaderHandle::Custom(_) => pipelines.contains_key(shader),
734 _ => false,
735 },
736 |shader| matches!(shader, ShaderHandle::Custom(name) if backdrop_shaders.contains(name)),
737 &mut recorder,
738 scale_factor,
739 &mut timings,
740 );
741 }
742 let next_paint_redraw_in = self.core.scan_continuous_shaders(
743 |handle| matches!(handle, ShaderHandle::Custom(name) if time_shaders.contains(name)),
744 );
745 self.upload_frame_data(_viewport, scale_factor)
746 .expect("damascene-ash: failed to upload repainted frame data");
747 damascene_core::runtime::PrepareResult {
748 needs_redraw: next_paint_redraw_in.is_some(),
749 next_redraw_in: next_paint_redraw_in,
750 next_layout_redraw_in: None,
751 next_paint_redraw_in,
752 timings,
753 }
754 }
755
756 pub unsafe fn draw(&self, _cmd: vk::CommandBuffer) -> Result<()> {
773 unsafe { self.draw_items(_cmd, &self.core.paint_items) }
774 }
775
776 pub unsafe fn record_scene_prepass(&mut self, cmd: vk::CommandBuffer) {
792 if self.scene_paint.has_runs() {
793 unsafe {
794 self.scene_paint.encode_offscreen(&self.context.device, cmd);
795 self.scene_paint
800 .encode_depth_capture(&self.context.device, cmd);
801 }
802 }
803 }
804
805 pub unsafe fn record_uploads(&mut self, cmd: vk::CommandBuffer) -> Result<()> {
819 unsafe {
820 self.text_paint
821 .record_pending_uploads(&self.context.device, cmd)?;
822 self.icon_paint
823 .record_pending_uploads(&self.context.device, cmd)?;
824 self.image_paint
825 .record_pending_uploads(&self.context.device, cmd);
826 }
827 Ok(())
828 }
829
830 pub unsafe fn render(
838 &mut self,
839 _cmd: vk::CommandBuffer,
840 _target: AshRenderTarget,
841 _load: LoadOp,
842 ) -> Result<()> {
843 if _target.format != self.target.format || _target.sample_count != self.target.sample_count
844 {
845 return Err(Error::IncompatibleTarget {
846 expected_format: self.target.format,
847 actual_format: _target.format,
848 expected_sample_count: self.target.sample_count,
849 actual_sample_count: _target.sample_count,
850 });
851 }
852
853 let (load_op, clear_value) = match _load {
854 LoadOp::Clear(color) => (
855 vk::AttachmentLoadOp::CLEAR,
856 vk::ClearValue {
857 color: vk::ClearColorValue { float32: color },
858 },
859 ),
860 LoadOp::Load => (vk::AttachmentLoadOp::LOAD, vk::ClearValue::default()),
861 };
862 unsafe {
863 self.record_uploads(_cmd)?;
864 self.record_scene_prepass(_cmd);
868 transition_color_target(
869 &self.context.device,
870 _cmd,
871 _target.image,
872 _target.initial_layout,
873 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
874 );
875 }
876
877 let color_attachment = vk::RenderingAttachmentInfo::default()
878 .image_view(_target.view)
879 .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
880 .load_op(load_op)
881 .store_op(vk::AttachmentStoreOp::STORE)
882 .clear_value(clear_value);
883 let color_attachments = [color_attachment];
884 let rendering_info = vk::RenderingInfo::default()
885 .render_area(vk::Rect2D {
886 offset: vk::Offset2D { x: 0, y: 0 },
887 extent: _target.extent,
888 })
889 .layer_count(1)
890 .color_attachments(&color_attachments);
891
892 unsafe {
893 self.context
894 .device
895 .cmd_begin_rendering(_cmd, &rendering_info);
896 self.draw_items(_cmd, &self.core.paint_items)?;
897 self.context.device.cmd_end_rendering(_cmd);
898 transition_color_target(
899 &self.context.device,
900 _cmd,
901 _target.image,
902 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
903 _target.final_layout,
904 );
905 }
906 Ok(())
907 }
908
909 pub fn pointer_moved(&mut self, p: Pointer) -> damascene_core::runtime::PointerMove {
910 self.core.pointer_moved(p)
911 }
912
913 pub fn pointer_left(&mut self) -> Vec<UiEvent> {
914 self.core.pointer_left()
915 }
916
917 pub fn pointer_cancelled(&mut self) -> Vec<UiEvent> {
918 self.core.pointer_cancelled()
919 }
920
921 pub fn file_hovered(&mut self, path: PathBuf, x: f32, y: f32) -> Vec<UiEvent> {
922 self.core.file_hovered(path, x, y)
923 }
924
925 pub fn file_hover_cancelled(&mut self) -> Vec<UiEvent> {
926 self.core.file_hover_cancelled()
927 }
928
929 pub fn file_dropped(&mut self, path: PathBuf, x: f32, y: f32) -> Vec<UiEvent> {
930 self.core.file_dropped(path, x, y)
931 }
932
933 pub fn would_press_focus_text_input(&self, x: f32, y: f32) -> bool {
934 self.core.would_press_focus_text_input(x, y)
935 }
936
937 pub fn focused_captures_keys(&self) -> bool {
938 self.core.focused_captures_keys()
939 }
940
941 pub fn pointer_down(&mut self, p: Pointer) -> Vec<UiEvent> {
942 self.core.pointer_down(p)
943 }
944
945 pub fn pointer_up(&mut self, p: Pointer) -> Vec<UiEvent> {
946 self.core.pointer_up(p)
947 }
948
949 pub fn set_modifiers(&mut self, modifiers: KeyModifiers) {
950 self.core.ui_state.set_modifiers(modifiers);
951 }
952
953 pub fn key_down(
954 &mut self,
955 logical: LogicalKey,
956 physical: PhysicalKey,
957 modifiers: KeyModifiers,
958 repeat: bool,
959 ) -> Vec<UiEvent> {
960 self.core.key_down(logical, physical, modifiers, repeat)
961 }
962
963 pub fn text_input(&mut self, text: String) -> Option<UiEvent> {
964 self.core.text_input(text)
965 }
966
967 pub fn set_hotkeys(&mut self, hotkeys: Vec<(KeyChord, String)>) {
968 self.core.set_hotkeys(hotkeys);
969 }
970
971 pub fn set_selection(&mut self, selection: damascene_core::selection::Selection) {
972 self.core.set_selection(selection);
973 }
974
975 pub fn selected_text(&self) -> Option<String> {
976 self.core.selected_text()
977 }
978
979 pub fn selected_text_for(
980 &self,
981 selection: &damascene_core::selection::Selection,
982 ) -> Option<String> {
983 self.core.selected_text_for(selection)
984 }
985
986 pub fn push_toasts(&mut self, specs: Vec<damascene_core::toast::ToastSpec>) {
987 self.core.push_toasts(specs);
988 }
989
990 pub fn dismiss_toast(&mut self, id: u64) {
991 self.core.dismiss_toast(id);
992 }
993
994 pub fn push_focus_requests(&mut self, keys: Vec<String>) {
995 self.core.push_focus_requests(keys);
996 }
997
998 pub fn push_scroll_requests(&mut self, requests: Vec<damascene_core::scroll::ScrollRequest>) {
999 self.core.push_scroll_requests(requests);
1000 }
1001
1002 pub fn push_viewport_requests(
1003 &mut self,
1004 requests: Vec<damascene_core::viewport::ViewportRequest>,
1005 ) {
1006 self.core.push_viewport_requests(requests);
1007 }
1008
1009 pub fn push_plot_requests(&mut self, requests: Vec<damascene_core::plot::PlotRequest>) {
1010 self.core.push_plot_requests(requests);
1011 }
1012
1013 pub fn set_animation_mode(&mut self, mode: AnimationMode) {
1014 self.core.set_animation_mode(mode);
1015 }
1016
1017 pub fn pointer_wheel(&mut self, x: f32, y: f32, dy: f32) -> bool {
1018 self.core.pointer_wheel(x, y, dy)
1019 }
1020
1021 pub fn pointer_wheel_event(&mut self, x: f32, y: f32, dx: f32, dy: f32) -> Option<UiEvent> {
1022 self.core.pointer_wheel_event(x, y, dx, dy)
1023 }
1024
1025 pub fn poll_input(&mut self, now: Instant) -> Vec<UiEvent> {
1026 self.core.poll_input(now)
1027 }
1028
1029 fn upload_frame_data(&mut self, viewport: Rect, scale_factor: f32) -> Result<()> {
1030 self.ensure_instance_capacity(self.core.quad_scratch.len())?;
1031 let frame = FrameUniforms {
1032 viewport: [viewport.w, viewport.h],
1033 time: (Instant::now() - self.start_time).as_secs_f32(),
1034 scale_factor,
1035 white_scale: self.white_scale,
1036 headroom: self.headroom,
1037 ref_nits: self.ref_nits,
1038 _reserved: 0.0,
1039 };
1040 self.frame_buf.write_bytes(bytemuck::bytes_of(&frame))?;
1041 self.instance_buf
1042 .write_bytes(bytemuck::cast_slice(&self.core.quad_scratch))?;
1043 let mut allocator = self
1044 .context
1045 .allocator
1046 .lock()
1047 .map_err(|_| Error::AllocatorPoisoned)?;
1048 self.text_paint
1049 .flush(&self.context.device, &mut allocator)?;
1050 self.icon_paint
1051 .flush(&self.context.device, &mut allocator)?;
1052 self.image_paint
1053 .flush(&self.context.device, &mut allocator)?;
1054 self.surface_paint
1055 .flush(&self.context.device, &mut allocator)?;
1056 self.scene_paint
1057 .flush(&self.context.device, &mut allocator)?;
1058 Ok(())
1059 }
1060
1061 fn ensure_instance_capacity(&mut self, instances: usize) -> Result<()> {
1062 if instances <= self.instance_capacity {
1063 return Ok(());
1064 }
1065 let mut next = self.instance_capacity.max(1);
1066 while next < instances {
1067 next *= 2;
1068 }
1069 let mut allocator = self
1070 .context
1071 .allocator
1072 .lock()
1073 .map_err(|_| Error::AllocatorPoisoned)?;
1074 unsafe {
1075 self.instance_buf
1076 .destroy(&self.context.device, &mut allocator);
1077 }
1078 self.instance_buf = GpuBuffer::new(
1079 &self.context.device,
1080 &mut allocator,
1081 "damascene_ash::instance_buf",
1082 (next * std::mem::size_of::<QuadInstance>()) as vk::DeviceSize,
1083 vk::BufferUsageFlags::VERTEX_BUFFER,
1084 MemoryLocation::CpuToGpu,
1085 )?;
1086 self.instance_capacity = next;
1087 Ok(())
1088 }
1089
1090 unsafe fn draw_items(&self, cmd: vk::CommandBuffer, items: &[PaintItem]) -> Result<()> {
1091 let full = PhysicalScissor {
1092 x: 0,
1093 y: 0,
1094 w: self.core.viewport_px.0,
1095 h: self.core.viewport_px.1,
1096 };
1097 for item in items {
1098 match *item {
1099 PaintItem::QuadRun(index) => {
1100 let run = &self.core.runs[index];
1101 let pipeline = self
1102 .pipelines
1103 .get(&run.handle)
1104 .ok_or(Error::MissingPipeline(run.handle))?;
1105 unsafe {
1106 self.context.device.cmd_bind_pipeline(
1107 cmd,
1108 vk::PipelineBindPoint::GRAPHICS,
1109 *pipeline,
1110 );
1111 self.set_viewport(cmd);
1112 self.set_scissor(cmd, run.scissor, full);
1113 self.context.device.cmd_bind_descriptor_sets(
1114 cmd,
1115 vk::PipelineBindPoint::GRAPHICS,
1116 self.pipeline_layout,
1117 0,
1118 &[self.descriptor_set],
1119 &[],
1120 );
1121 self.context.device.cmd_bind_vertex_buffers(
1122 cmd,
1123 0,
1124 &[self.quad_vbo.buffer, self.instance_buf.buffer],
1125 &[0, 0],
1126 );
1127 self.context
1128 .device
1129 .cmd_draw(cmd, 4, run.count, 0, run.first);
1130 }
1131 }
1132 PaintItem::Text(index) => {
1133 let run = self.text_paint.run(index);
1134 let pipeline = self.text_paint.pipeline_for(run.kind);
1135 let layout = self.text_paint.pipeline_layout_for(run.kind);
1136 unsafe {
1137 self.context.device.cmd_bind_pipeline(
1138 cmd,
1139 vk::PipelineBindPoint::GRAPHICS,
1140 pipeline,
1141 );
1142 self.set_viewport(cmd);
1143 self.set_scissor(cmd, run.scissor, full);
1144 match run.kind {
1145 TextRunKind::Color | TextRunKind::Msdf => {
1146 let page = self.text_paint.page_descriptor(run.kind, run.page);
1147 self.context.device.cmd_bind_descriptor_sets(
1148 cmd,
1149 vk::PipelineBindPoint::GRAPHICS,
1150 layout,
1151 0,
1152 &[self.descriptor_set, page],
1153 &[],
1154 );
1155 }
1156 TextRunKind::Highlight => {
1157 self.context.device.cmd_bind_descriptor_sets(
1158 cmd,
1159 vk::PipelineBindPoint::GRAPHICS,
1160 layout,
1161 0,
1162 &[self.descriptor_set],
1163 &[],
1164 );
1165 }
1166 }
1167 self.context.device.cmd_bind_vertex_buffers(
1168 cmd,
1169 0,
1170 &[
1171 self.quad_vbo.buffer,
1172 self.text_paint.instance_buffer(run.kind),
1173 ],
1174 &[0, 0],
1175 );
1176 self.context
1177 .device
1178 .cmd_draw(cmd, 4, run.count, 0, run.first);
1179 }
1180 }
1181 PaintItem::Image(index) => {
1182 let run = self.image_paint.run(index);
1183 let pipeline = self.image_paint.pipeline();
1184 let layout = self.image_paint.pipeline_layout();
1185 let texture_set = self.image_paint.descriptor_for_run(run);
1186 unsafe {
1187 self.context.device.cmd_bind_pipeline(
1188 cmd,
1189 vk::PipelineBindPoint::GRAPHICS,
1190 pipeline,
1191 );
1192 self.set_viewport(cmd);
1193 self.set_scissor(cmd, run.scissor, full);
1194 self.context.device.cmd_bind_descriptor_sets(
1195 cmd,
1196 vk::PipelineBindPoint::GRAPHICS,
1197 layout,
1198 0,
1199 &[self.descriptor_set, texture_set],
1200 &[],
1201 );
1202 self.context.device.cmd_bind_vertex_buffers(
1203 cmd,
1204 0,
1205 &[self.quad_vbo.buffer, self.image_paint.instance_buffer()],
1206 &[0, 0],
1207 );
1208 self.context
1209 .device
1210 .cmd_draw(cmd, 4, run.count, 0, run.first);
1211 }
1212 }
1213 PaintItem::IconRun(index) | PaintItem::Vector(index) => {
1214 let run = self.icon_paint.run(index);
1215 match run.kind {
1216 IconRunKind::Msdf => {
1217 let page = self.icon_paint.page_descriptor(run.page);
1218 unsafe {
1219 self.context.device.cmd_bind_pipeline(
1220 cmd,
1221 vk::PipelineBindPoint::GRAPHICS,
1222 self.icon_paint.pipeline(),
1223 );
1224 self.set_viewport(cmd);
1225 self.set_scissor(cmd, run.scissor, full);
1226 self.context.device.cmd_bind_descriptor_sets(
1227 cmd,
1228 vk::PipelineBindPoint::GRAPHICS,
1229 self.icon_paint.pipeline_layout(),
1230 0,
1231 &[self.descriptor_set, page],
1232 &[],
1233 );
1234 self.context.device.cmd_bind_vertex_buffers(
1235 cmd,
1236 0,
1237 &[self.quad_vbo.buffer, self.icon_paint.instance_buffer()],
1238 &[0, 0],
1239 );
1240 self.context
1241 .device
1242 .cmd_draw(cmd, 4, run.count, 0, run.first);
1243 }
1244 }
1245 IconRunKind::Tess => unsafe {
1246 self.context.device.cmd_bind_pipeline(
1247 cmd,
1248 vk::PipelineBindPoint::GRAPHICS,
1249 self.icon_paint.tess_pipeline(run.material),
1250 );
1251 self.set_viewport(cmd);
1252 self.set_scissor(cmd, run.scissor, full);
1253 self.context.device.cmd_bind_descriptor_sets(
1254 cmd,
1255 vk::PipelineBindPoint::GRAPHICS,
1256 self.icon_paint.tess_pipeline_layout(),
1257 0,
1258 &[self.descriptor_set],
1259 &[],
1260 );
1261 self.context.device.cmd_bind_vertex_buffers(
1262 cmd,
1263 0,
1264 &[self.icon_paint.tess_vertex_buffer()],
1265 &[0],
1266 );
1267 self.context
1268 .device
1269 .cmd_draw(cmd, run.count, 1, run.first, 0);
1270 },
1271 }
1272 }
1273 PaintItem::Scene3D(index) => {
1274 let run = self.scene_paint.run(index);
1278 let pipeline = self.scene_paint.composite_pipeline();
1279 let layout = self.scene_paint.composite_pipeline_layout();
1280 let texture_set = self.scene_paint.composite_descriptor(run);
1281 unsafe {
1282 self.context.device.cmd_bind_pipeline(
1283 cmd,
1284 vk::PipelineBindPoint::GRAPHICS,
1285 pipeline,
1286 );
1287 self.set_viewport(cmd);
1288 self.set_scissor(cmd, run.scissor, full);
1289 self.context.device.cmd_bind_descriptor_sets(
1290 cmd,
1291 vk::PipelineBindPoint::GRAPHICS,
1292 layout,
1293 0,
1294 &[self.descriptor_set, texture_set],
1295 &[],
1296 );
1297 self.context.device.cmd_bind_vertex_buffers(
1298 cmd,
1299 0,
1300 &[
1301 self.quad_vbo.buffer,
1302 self.scene_paint.composite_instance_buffer(),
1303 ],
1304 &[0, 0],
1305 );
1306 self.context
1307 .device
1308 .cmd_draw(cmd, 4, 1, 0, run.composite_instance);
1309 }
1310 }
1311 PaintItem::BackdropSnapshot => {
1312 return Err(Error::Unsupported(
1313 "damascene-ash backdrop snapshot rendering is not implemented yet",
1314 ));
1315 }
1316 PaintItem::AppTexture(index) => {
1317 let run = self.surface_paint.run(index);
1318 let pipeline = self.surface_paint.pipeline_for(run.alpha);
1319 let layout = self.surface_paint.pipeline_layout();
1320 let texture_set = self.surface_paint.descriptor_for_run(run);
1321 unsafe {
1322 self.context.device.cmd_bind_pipeline(
1323 cmd,
1324 vk::PipelineBindPoint::GRAPHICS,
1325 pipeline,
1326 );
1327 self.set_viewport(cmd);
1328 self.set_scissor(cmd, run.scissor, full);
1329 self.context.device.cmd_bind_descriptor_sets(
1330 cmd,
1331 vk::PipelineBindPoint::GRAPHICS,
1332 layout,
1333 0,
1334 &[self.descriptor_set, texture_set],
1335 &[],
1336 );
1337 self.context.device.cmd_bind_vertex_buffers(
1338 cmd,
1339 0,
1340 &[self.quad_vbo.buffer, self.surface_paint.instance_buffer()],
1341 &[0, 0],
1342 );
1343 self.context
1344 .device
1345 .cmd_draw(cmd, 4, run.count, 0, run.first);
1346 }
1347 }
1348 }
1349 }
1350 Ok(())
1351 }
1352
1353 unsafe fn set_viewport(&self, cmd: vk::CommandBuffer) {
1354 let viewport = vk::Viewport {
1355 x: 0.0,
1356 y: 0.0,
1357 width: self.core.viewport_px.0 as f32,
1358 height: self.core.viewport_px.1 as f32,
1359 min_depth: 0.0,
1360 max_depth: 1.0,
1361 };
1362 unsafe {
1363 self.context.device.cmd_set_viewport(cmd, 0, &[viewport]);
1364 }
1365 }
1366
1367 unsafe fn set_scissor(
1368 &self,
1369 cmd: vk::CommandBuffer,
1370 scissor: Option<PhysicalScissor>,
1371 full: PhysicalScissor,
1372 ) {
1373 let s = scissor.unwrap_or(full);
1374 let rect = vk::Rect2D {
1375 offset: vk::Offset2D {
1376 x: s.x as i32,
1377 y: s.y as i32,
1378 },
1379 extent: vk::Extent2D {
1380 width: s.w.max(1),
1381 height: s.h.max(1),
1382 },
1383 };
1384 unsafe {
1385 self.context.device.cmd_set_scissor(cmd, 0, &[rect]);
1386 }
1387 }
1388
1389 fn register_stock_shaders(&mut self) -> Result<()> {
1390 self.insert_stock_pipeline(StockShader::RoundedRect, stock_wgsl::ROUNDED_RECT)?;
1391 self.insert_stock_pipeline(StockShader::Spinner, stock_wgsl::SPINNER)?;
1392 self.insert_stock_pipeline(StockShader::Skeleton, stock_wgsl::SKELETON)?;
1393 self.insert_stock_pipeline(
1394 StockShader::ProgressIndeterminate,
1395 stock_wgsl::PROGRESS_INDETERMINATE,
1396 )?;
1397 Ok(())
1398 }
1399
1400 fn insert_stock_pipeline(&mut self, shader: StockShader, wgsl: &str) -> Result<()> {
1401 let pipeline = build_quad_pipeline(
1402 &self.context.device,
1403 self.pipeline_layout,
1404 self.target,
1405 shader.name(),
1406 wgsl,
1407 );
1408 self.pipelines
1409 .insert(ShaderHandle::Stock(shader), pipeline?);
1410 Ok(())
1411 }
1412}
1413
1414struct PaintRecorder<'a> {
1415 text: &'a mut TextPaint,
1416 icons: &'a mut IconPaint,
1417 images: &'a mut ImagePaint,
1418 surfaces: &'a mut SurfacePaint,
1419 scenes: &'a mut Scene3DPaint,
1420 device: &'a ash::Device,
1421 allocator: &'a mut Allocator,
1422}
1423
1424impl TextRecorder for PaintRecorder<'_> {
1425 fn record(
1426 &mut self,
1427 rect: Rect,
1428 scissor: Option<PhysicalScissor>,
1429 style: &RunStyle,
1430 text: &str,
1431 size: f32,
1432 line_height: f32,
1433 wrap: TextWrap,
1434 anchor: TextAnchor,
1435 scale_factor: f32,
1436 ) -> std::ops::Range<usize> {
1437 self.text.record(
1438 rect,
1439 scissor,
1440 style,
1441 text,
1442 size,
1443 line_height,
1444 wrap,
1445 anchor,
1446 scale_factor,
1447 )
1448 }
1449
1450 fn record_runs(
1451 &mut self,
1452 rect: Rect,
1453 scissor: Option<PhysicalScissor>,
1454 runs: &[(String, RunStyle)],
1455 size: f32,
1456 line_height: f32,
1457 wrap: TextWrap,
1458 anchor: TextAnchor,
1459 scale_factor: f32,
1460 ) -> std::ops::Range<usize> {
1461 self.text.record_runs(
1462 rect,
1463 scissor,
1464 runs,
1465 size,
1466 line_height,
1467 wrap,
1468 anchor,
1469 scale_factor,
1470 )
1471 }
1472
1473 fn record_image(
1474 &mut self,
1475 rect: Rect,
1476 scissor: Option<PhysicalScissor>,
1477 image: &damascene_core::image::Image,
1478 tint: Option<Color>,
1479 radius: damascene_core::tree::Corners,
1480 _fit: damascene_core::image::ImageFit,
1481 range_limit: damascene_core::image::DynamicRangeLimit,
1482 _scale_factor: f32,
1483 ) -> std::ops::Range<usize> {
1484 self.images
1485 .record(
1486 self.device,
1487 self.allocator,
1488 ImageRecord {
1489 rect,
1490 scissor,
1491 image,
1492 tint,
1493 radius,
1494 range_limit,
1495 },
1496 )
1497 .expect("damascene-ash: failed to record image")
1498 }
1499
1500 fn record_icon(
1501 &mut self,
1502 rect: Rect,
1503 scissor: Option<PhysicalScissor>,
1504 source: &IconSource,
1505 color: Color,
1506 size: f32,
1507 stroke_width: f32,
1508 scale_factor: f32,
1509 ) -> RecordedPaint {
1510 let range = self
1511 .icons
1512 .record(rect, scissor, source, color, stroke_width);
1513 if !range.is_empty() {
1514 return RecordedPaint::Icon(range);
1515 }
1516 record_icon_text_fallback(self.text, rect, scissor, source, color, size, scale_factor)
1517 }
1518
1519 fn record_vector(
1520 &mut self,
1521 rect: Rect,
1522 scissor: Option<PhysicalScissor>,
1523 asset: &damascene_core::vector::VectorAsset,
1524 render_mode: damascene_core::vector::VectorRenderMode,
1525 _scale_factor: f32,
1526 ) -> std::ops::Range<usize> {
1527 self.icons.record_vector(rect, scissor, asset, render_mode)
1528 }
1529
1530 fn record_app_texture(
1531 &mut self,
1532 rect: Rect,
1533 scissor: Option<PhysicalScissor>,
1534 texture: &damascene_core::surface::AppTexture,
1535 alpha: SurfaceAlpha,
1536 transform: damascene_core::affine::Affine2,
1537 _scale_factor: f32,
1538 ) -> std::ops::Range<usize> {
1539 self.surfaces
1540 .record(self.device, rect, scissor, texture, alpha, transform)
1541 .expect("damascene-ash: failed to record app texture")
1542 }
1543
1544 fn record_scene3d(
1545 &mut self,
1546 rect: Rect,
1547 scissor: Option<PhysicalScissor>,
1548 id: &str,
1549 scene: &std::sync::Arc<damascene_core::scene::Scene3DData>,
1550 scale_factor: f32,
1551 ) -> std::ops::Range<usize> {
1552 self.scenes
1553 .record(
1554 self.device,
1555 self.allocator,
1556 rect,
1557 scissor,
1558 id,
1559 scene,
1560 scale_factor,
1561 )
1562 .expect("damascene-ash: failed to record scene")
1563 }
1564}
1565
1566fn record_icon_text_fallback(
1567 text: &mut TextPaint,
1568 rect: Rect,
1569 scissor: Option<PhysicalScissor>,
1570 source: &IconSource,
1571 color: Color,
1572 size: f32,
1573 scale_factor: f32,
1574) -> RecordedPaint {
1575 let glyph = match source {
1576 IconSource::Builtin(name) => name.fallback_glyph(),
1577 IconSource::Custom(_) => "?",
1578 IconSource::UnknownName(_) => damascene_core::tree::IconName::AlertCircle.fallback_glyph(),
1579 };
1580 RecordedPaint::Text(text.record(
1581 rect,
1582 scissor,
1583 &RunStyle::new(FontWeight::Regular, color),
1584 glyph,
1585 size,
1586 damascene_core::text::metrics::line_height(size),
1587 TextWrap::NoWrap,
1588 TextAnchor::Middle,
1589 scale_factor,
1590 ))
1591}
1592
1593impl Drop for Runner {
1594 fn drop(&mut self) {
1595 unsafe {
1596 for (_, pipeline) in self.pipelines.drain() {
1597 self.context.device.destroy_pipeline(pipeline, None);
1598 }
1599 if self.pipeline_layout != vk::PipelineLayout::null() {
1600 self.context
1601 .device
1602 .destroy_pipeline_layout(self.pipeline_layout, None);
1603 }
1604 if self.descriptor_pool != vk::DescriptorPool::null() {
1605 self.context
1606 .device
1607 .destroy_descriptor_pool(self.descriptor_pool, None);
1608 }
1609 if self.descriptor_set_layout != vk::DescriptorSetLayout::null() {
1610 self.context
1611 .device
1612 .destroy_descriptor_set_layout(self.descriptor_set_layout, None);
1613 }
1614 if let Ok(mut allocator) = self.context.allocator.lock() {
1615 self.scene_paint
1616 .destroy(&self.context.device, &mut allocator);
1617 self.text_paint
1618 .destroy(&self.context.device, &mut allocator);
1619 self.icon_paint
1620 .destroy(&self.context.device, &mut allocator);
1621 self.image_paint
1622 .destroy(&self.context.device, &mut allocator);
1623 self.surface_paint
1624 .destroy(&self.context.device, &mut allocator);
1625 self.instance_buf
1626 .destroy(&self.context.device, &mut allocator);
1627 self.quad_vbo.destroy(&self.context.device, &mut allocator);
1628 self.frame_buf.destroy(&self.context.device, &mut allocator);
1629 }
1630 }
1631 }
1632}
1633
1634fn create_frame_descriptor_set_layout(device: &ash::Device) -> Result<vk::DescriptorSetLayout> {
1635 let binding = vk::DescriptorSetLayoutBinding::default()
1636 .binding(0)
1637 .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
1638 .descriptor_count(1)
1639 .stage_flags(vk::ShaderStageFlags::VERTEX | vk::ShaderStageFlags::FRAGMENT);
1640 let bindings = [binding];
1641 let info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&bindings);
1642 unsafe { device.create_descriptor_set_layout(&info, None) }.map_err(|result| Error::Vulkan {
1643 op: "create_descriptor_set_layout",
1644 result,
1645 })
1646}
1647
1648fn create_descriptor_pool(device: &ash::Device) -> Result<vk::DescriptorPool> {
1649 let pool_size = vk::DescriptorPoolSize {
1650 ty: vk::DescriptorType::UNIFORM_BUFFER,
1651 descriptor_count: 1,
1652 };
1653 let pool_sizes = [pool_size];
1654 let info = vk::DescriptorPoolCreateInfo::default()
1655 .max_sets(1)
1656 .pool_sizes(&pool_sizes);
1657 unsafe { device.create_descriptor_pool(&info, None) }.map_err(|result| Error::Vulkan {
1658 op: "create_descriptor_pool",
1659 result,
1660 })
1661}
1662
1663fn allocate_frame_descriptor_set(
1664 device: &ash::Device,
1665 pool: vk::DescriptorPool,
1666 layout: vk::DescriptorSetLayout,
1667) -> Result<vk::DescriptorSet> {
1668 let layouts = [layout];
1669 let info = vk::DescriptorSetAllocateInfo::default()
1670 .descriptor_pool(pool)
1671 .set_layouts(&layouts);
1672 let sets =
1673 unsafe { device.allocate_descriptor_sets(&info) }.map_err(|result| Error::Vulkan {
1674 op: "allocate_descriptor_sets",
1675 result,
1676 })?;
1677 sets.into_iter().next().ok_or(Error::Unsupported(
1678 "descriptor set allocation returned no sets",
1679 ))
1680}
1681
1682fn update_frame_descriptor_set(
1683 device: &ash::Device,
1684 descriptor_set: vk::DescriptorSet,
1685 frame_buffer: vk::Buffer,
1686) {
1687 let buffer_info = vk::DescriptorBufferInfo {
1688 buffer: frame_buffer,
1689 offset: 0,
1690 range: std::mem::size_of::<FrameUniforms>() as vk::DeviceSize,
1691 };
1692 let buffer_infos = [buffer_info];
1693 let write = vk::WriteDescriptorSet::default()
1694 .dst_set(descriptor_set)
1695 .dst_binding(0)
1696 .descriptor_type(vk::DescriptorType::UNIFORM_BUFFER)
1697 .buffer_info(&buffer_infos);
1698 unsafe {
1699 device.update_descriptor_sets(&[write], &[]);
1700 }
1701}
1702
1703fn create_pipeline_layout(
1704 device: &ash::Device,
1705 frame_layout: vk::DescriptorSetLayout,
1706) -> Result<vk::PipelineLayout> {
1707 let set_layouts = [frame_layout];
1708 let info = vk::PipelineLayoutCreateInfo::default().set_layouts(&set_layouts);
1709 unsafe { device.create_pipeline_layout(&info, None) }.map_err(|result| Error::Vulkan {
1710 op: "create_pipeline_layout",
1711 result,
1712 })
1713}
1714
1715unsafe fn transition_color_target(
1716 device: &ash::Device,
1717 cmd: vk::CommandBuffer,
1718 image: vk::Image,
1719 old_layout: vk::ImageLayout,
1720 new_layout: vk::ImageLayout,
1721) {
1722 if old_layout == new_layout {
1723 return;
1724 }
1725
1726 let (src_stage, src_access) = match old_layout {
1727 vk::ImageLayout::UNDEFINED => (
1728 vk::PipelineStageFlags::TOP_OF_PIPE,
1729 vk::AccessFlags::empty(),
1730 ),
1731 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
1732 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1733 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
1734 ),
1735 vk::ImageLayout::PRESENT_SRC_KHR => (
1736 vk::PipelineStageFlags::BOTTOM_OF_PIPE,
1737 vk::AccessFlags::empty(),
1738 ),
1739 _ => (
1740 vk::PipelineStageFlags::ALL_COMMANDS,
1741 vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE,
1742 ),
1743 };
1744 let (dst_stage, dst_access) = match new_layout {
1745 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL => (
1746 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1747 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
1748 ),
1749 vk::ImageLayout::PRESENT_SRC_KHR => (
1750 vk::PipelineStageFlags::BOTTOM_OF_PIPE,
1751 vk::AccessFlags::empty(),
1752 ),
1753 _ => (
1754 vk::PipelineStageFlags::ALL_COMMANDS,
1755 vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE,
1756 ),
1757 };
1758 let barrier = vk::ImageMemoryBarrier::default()
1759 .old_layout(old_layout)
1760 .new_layout(new_layout)
1761 .src_access_mask(src_access)
1762 .dst_access_mask(dst_access)
1763 .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1764 .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
1765 .image(image)
1766 .subresource_range(vk::ImageSubresourceRange {
1767 aspect_mask: vk::ImageAspectFlags::COLOR,
1768 base_mip_level: 0,
1769 level_count: 1,
1770 base_array_layer: 0,
1771 layer_count: 1,
1772 });
1773 unsafe {
1774 device.cmd_pipeline_barrier(
1775 cmd,
1776 src_stage,
1777 dst_stage,
1778 vk::DependencyFlags::empty(),
1779 &[],
1780 &[],
1781 &[barrier],
1782 );
1783 }
1784}