1use std::any::Any;
8use std::collections::HashMap;
9use std::ffi::CString;
10use std::ops::Range;
11use std::sync::Arc;
12
13use ash::vk;
14use bytemuck::{Pod, Zeroable};
15use damascene_core::affine::Affine2;
16use damascene_core::paint::PhysicalScissor;
17use damascene_core::shader::stock_wgsl;
18use damascene_core::surface::{
19 AppTexture, AppTextureBackend, AppTextureId, SurfaceAlpha, SurfaceFormat, next_app_texture_id,
20};
21use damascene_core::tree::Rect;
22use gpu_allocator::MemoryLocation;
23use gpu_allocator::vulkan::Allocator;
24
25use crate::buffer::GpuBuffer;
26use crate::naga_compile::wgsl_to_spirv;
27use crate::runner::{Error, Result, TargetInfo};
28
29const INITIAL_SURFACE_INSTANCE_CAPACITY: usize = 16;
30
31#[repr(C)]
32#[derive(Copy, Clone, Pod, Zeroable, Debug)]
33struct SurfaceInstance {
34 rect: [f32; 4],
35 matrix: [f32; 4],
36 translation: [f32; 2],
37}
38
39pub(crate) struct SurfaceRun {
40 pub texture_idx: usize,
41 pub scissor: Option<PhysicalScissor>,
42 pub alpha: SurfaceAlpha,
43 pub first: u32,
44 pub count: u32,
45}
46
47struct CachedDescriptor {
48 descriptor_set: vk::DescriptorSet,
49 last_used_frame: u64,
50}
51
52pub(crate) struct SurfacePaint {
53 instances: Vec<SurfaceInstance>,
54 instance_buf: GpuBuffer,
55 instance_capacity: usize,
56 runs: Vec<SurfaceRun>,
57 texture_set_layout: vk::DescriptorSetLayout,
58 pipeline_layout: vk::PipelineLayout,
59 pipeline_premul: vk::Pipeline,
60 pipeline_straight: vk::Pipeline,
61 pipeline_opaque: vk::Pipeline,
62 descriptor_pool: vk::DescriptorPool,
63 sampler: vk::Sampler,
64 cache: HashMap<u64, CachedDescriptor>,
65 bind_lookup: Vec<u64>,
66 frame_counter: u64,
67}
68
69impl SurfacePaint {
70 pub(crate) fn new(
71 device: &ash::Device,
72 allocator: &mut Allocator,
73 frame_set_layout: vk::DescriptorSetLayout,
74 target: TargetInfo,
75 ) -> Result<Self> {
76 let texture_set_layout = create_texture_set_layout(device)?;
77 let layouts = [frame_set_layout, texture_set_layout];
78 let pipeline_layout = create_pipeline_layout(device, &layouts)?;
79 let pipeline_premul = build_surface_pipeline(
80 device,
81 pipeline_layout,
82 target,
83 "stock::surface::premul",
84 "fs_premul",
85 SurfaceAlpha::Premultiplied,
86 )?;
87 let pipeline_straight = build_surface_pipeline(
88 device,
89 pipeline_layout,
90 target,
91 "stock::surface::straight",
92 "fs_straight",
93 SurfaceAlpha::Straight,
94 )?;
95 let pipeline_opaque = build_surface_pipeline(
96 device,
97 pipeline_layout,
98 target,
99 "stock::surface::opaque",
100 "fs_opaque",
101 SurfaceAlpha::Opaque,
102 )?;
103 let pool_sizes = [
104 vk::DescriptorPoolSize {
105 ty: vk::DescriptorType::SAMPLED_IMAGE,
106 descriptor_count: 256,
107 },
108 vk::DescriptorPoolSize {
109 ty: vk::DescriptorType::SAMPLER,
110 descriptor_count: 256,
111 },
112 ];
113 let pool_info = vk::DescriptorPoolCreateInfo::default()
114 .flags(vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET)
115 .max_sets(256)
116 .pool_sizes(&pool_sizes);
117 let descriptor_pool = unsafe { device.create_descriptor_pool(&pool_info, None) }?;
118 let sampler_info = vk::SamplerCreateInfo::default()
119 .mag_filter(vk::Filter::LINEAR)
120 .min_filter(vk::Filter::LINEAR)
121 .mipmap_mode(vk::SamplerMipmapMode::LINEAR)
122 .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE)
123 .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE)
124 .address_mode_w(vk::SamplerAddressMode::CLAMP_TO_EDGE);
125 let sampler = unsafe { device.create_sampler(&sampler_info, None) }?;
126 let instance_capacity = INITIAL_SURFACE_INSTANCE_CAPACITY;
127 let instance_buf = GpuBuffer::new(
128 device,
129 allocator,
130 "damascene_ash::surface_instances",
131 (instance_capacity * std::mem::size_of::<SurfaceInstance>()) as vk::DeviceSize,
132 vk::BufferUsageFlags::VERTEX_BUFFER,
133 MemoryLocation::CpuToGpu,
134 )?;
135 Ok(Self {
136 instances: Vec::new(),
137 instance_buf,
138 instance_capacity,
139 runs: Vec::new(),
140 texture_set_layout,
141 pipeline_layout,
142 pipeline_premul,
143 pipeline_straight,
144 pipeline_opaque,
145 descriptor_pool,
146 sampler,
147 cache: HashMap::new(),
148 bind_lookup: Vec::new(),
149 frame_counter: 0,
150 })
151 }
152
153 pub(crate) fn frame_begin(&mut self) {
154 self.instances.clear();
155 self.runs.clear();
156 self.bind_lookup.clear();
157 self.frame_counter = self.frame_counter.wrapping_add(1);
158 }
159
160 pub(crate) fn record(
161 &mut self,
162 device: &ash::Device,
163 rect: Rect,
164 scissor: Option<PhysicalScissor>,
165 texture: &AppTexture,
166 alpha: SurfaceAlpha,
167 transform: Affine2,
168 ) -> Result<Range<usize>> {
169 let start = self.runs.len();
170 if rect.w <= 0.0 || rect.h <= 0.0 {
171 return Ok(start..start);
172 }
173 let texture_idx = self.ensure_descriptor(device, texture)?;
174 let first = self.instances.len() as u32;
175 self.instances.push(SurfaceInstance {
176 rect: [rect.x, rect.y, rect.w, rect.h],
177 matrix: [transform.a, transform.b, transform.c, transform.d],
178 translation: [transform.tx, transform.ty],
179 });
180 self.runs.push(SurfaceRun {
181 texture_idx,
182 scissor,
183 alpha,
184 first,
185 count: 1,
186 });
187 Ok(start..self.runs.len())
188 }
189
190 pub(crate) fn flush(&mut self, device: &ash::Device, allocator: &mut Allocator) -> Result<()> {
191 let mut stale = Vec::new();
192 self.cache.retain(|_, cached| {
193 let keep = cached.last_used_frame == self.frame_counter;
194 if !keep {
195 stale.push(cached.descriptor_set);
196 }
197 keep
198 });
199 if !stale.is_empty() {
200 unsafe {
201 device.free_descriptor_sets(self.descriptor_pool, &stale)?;
202 }
203 }
204 self.ensure_instance_capacity(device, allocator)?;
205 self.instance_buf
206 .write_bytes(bytemuck::cast_slice(&self.instances))?;
207 Ok(())
208 }
209
210 fn ensure_descriptor(&mut self, device: &ash::Device, texture: &AppTexture) -> Result<usize> {
211 let backend = texture
212 .backend()
213 .as_any()
214 .downcast_ref::<AshAppTexture>()
215 .unwrap_or_else(|| {
216 panic!(
217 "damascene-ash expected AshAppTexture, got {}",
218 texture.backend_name()
219 )
220 });
221 let id = backend.id.0;
222 if !self.cache.contains_key(&id) {
223 let cached = self.create_descriptor(device, backend)?;
224 self.cache.insert(id, cached);
225 }
226 self.cache
227 .get_mut(&id)
228 .expect("just inserted")
229 .last_used_frame = self.frame_counter;
230 if let Some(idx) = self.bind_lookup.iter().position(|&h| h == id) {
231 Ok(idx)
232 } else {
233 self.bind_lookup.push(id);
234 Ok(self.bind_lookup.len() - 1)
235 }
236 }
237
238 fn create_descriptor(
239 &self,
240 device: &ash::Device,
241 texture: &AshAppTexture,
242 ) -> Result<CachedDescriptor> {
243 let set_layouts = [self.texture_set_layout];
244 let allocate_info = vk::DescriptorSetAllocateInfo::default()
245 .descriptor_pool(self.descriptor_pool)
246 .set_layouts(&set_layouts);
247 let descriptor_set = unsafe { device.allocate_descriptor_sets(&allocate_info) }?[0];
248 let image_info = vk::DescriptorImageInfo::default()
249 .image_view(texture.view)
250 .image_layout(texture.image_layout);
251 let sampler_info = vk::DescriptorImageInfo::default().sampler(self.sampler);
252 let writes = [
253 vk::WriteDescriptorSet::default()
254 .dst_set(descriptor_set)
255 .dst_binding(0)
256 .descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
257 .image_info(std::slice::from_ref(&image_info)),
258 vk::WriteDescriptorSet::default()
259 .dst_set(descriptor_set)
260 .dst_binding(1)
261 .descriptor_type(vk::DescriptorType::SAMPLER)
262 .image_info(std::slice::from_ref(&sampler_info)),
263 ];
264 unsafe {
265 device.update_descriptor_sets(&writes, &[]);
266 }
267 Ok(CachedDescriptor {
268 descriptor_set,
269 last_used_frame: self.frame_counter,
270 })
271 }
272
273 fn ensure_instance_capacity(
274 &mut self,
275 device: &ash::Device,
276 allocator: &mut Allocator,
277 ) -> Result<()> {
278 if self.instances.len() <= self.instance_capacity {
279 return Ok(());
280 }
281 let mut next = self.instance_capacity.max(1);
282 while next < self.instances.len() {
283 next *= 2;
284 }
285 unsafe {
286 self.instance_buf.destroy(device, allocator);
287 }
288 self.instance_buf = GpuBuffer::new(
289 device,
290 allocator,
291 "damascene_ash::surface_instances",
292 (next * std::mem::size_of::<SurfaceInstance>()) as vk::DeviceSize,
293 vk::BufferUsageFlags::VERTEX_BUFFER,
294 MemoryLocation::CpuToGpu,
295 )?;
296 self.instance_capacity = next;
297 Ok(())
298 }
299
300 pub(crate) fn run(&self, index: usize) -> &SurfaceRun {
301 &self.runs[index]
302 }
303
304 pub(crate) fn pipeline_for(&self, alpha: SurfaceAlpha) -> vk::Pipeline {
305 match alpha {
306 SurfaceAlpha::Premultiplied => self.pipeline_premul,
307 SurfaceAlpha::Straight => self.pipeline_straight,
308 SurfaceAlpha::Opaque => self.pipeline_opaque,
309 }
310 }
311
312 pub(crate) fn pipeline_layout(&self) -> vk::PipelineLayout {
313 self.pipeline_layout
314 }
315
316 pub(crate) fn instance_buffer(&self) -> vk::Buffer {
317 self.instance_buf.buffer
318 }
319
320 pub(crate) fn descriptor_for_run(&self, run: &SurfaceRun) -> vk::DescriptorSet {
321 let id = self.bind_lookup[run.texture_idx];
322 self.cache
323 .get(&id)
324 .expect("cache entry alive for frame")
325 .descriptor_set
326 }
327
328 pub(crate) unsafe fn destroy(&mut self, device: &ash::Device, allocator: &mut Allocator) {
329 unsafe {
330 self.instance_buf.destroy(device, allocator);
331 device.destroy_pipeline(self.pipeline_premul, None);
332 device.destroy_pipeline(self.pipeline_straight, None);
333 device.destroy_pipeline(self.pipeline_opaque, None);
334 device.destroy_pipeline_layout(self.pipeline_layout, None);
335 device.destroy_sampler(self.sampler, None);
336 device.destroy_descriptor_pool(self.descriptor_pool, None);
337 device.destroy_descriptor_set_layout(self.texture_set_layout, None);
338 }
339 }
340}
341
342#[derive(Clone, Copy, Debug)]
350pub struct AshAppTexture {
351 pub image: vk::Image,
352 pub view: vk::ImageView,
353 pub image_layout: vk::ImageLayout,
354 id: AppTextureId,
355 size: (u32, u32),
356 format: SurfaceFormat,
357}
358
359pub fn app_texture(
369 image: vk::Image,
370 view: vk::ImageView,
371 format: vk::Format,
372 extent: vk::Extent2D,
373) -> AppTexture {
374 app_texture_with_layout(
375 image,
376 view,
377 format,
378 extent,
379 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
380 )
381}
382
383pub fn app_texture_with_layout(
390 image: vk::Image,
391 view: vk::ImageView,
392 format: vk::Format,
393 extent: vk::Extent2D,
394 image_layout: vk::ImageLayout,
395) -> AppTexture {
396 let surface_format = match format {
397 vk::Format::R8G8B8A8_SRGB => SurfaceFormat::Rgba8UnormSrgb,
398 vk::Format::B8G8R8A8_SRGB => SurfaceFormat::Bgra8UnormSrgb,
399 vk::Format::R8G8B8A8_UNORM => SurfaceFormat::Rgba8Unorm,
400 vk::Format::R16G16B16A16_SFLOAT => SurfaceFormat::Rgba16Float,
401 _ => panic!("unsupported damascene-ash app texture format: {format:?}"),
402 };
403 assert!(
404 extent.width > 0 && extent.height > 0,
405 "damascene-ash app textures must have non-zero extent"
406 );
407 AppTexture::from_backend(Arc::new(AshAppTexture {
408 image,
409 view,
410 image_layout,
411 id: next_app_texture_id(),
412 size: (extent.width, extent.height),
413 format: surface_format,
414 }))
415}
416
417impl AppTextureBackend for AshAppTexture {
418 fn id(&self) -> AppTextureId {
419 self.id
420 }
421
422 fn size_px(&self) -> (u32, u32) {
423 self.size
424 }
425
426 fn format(&self) -> SurfaceFormat {
427 self.format
428 }
429
430 fn as_any(&self) -> &dyn Any {
431 self
432 }
433}
434
435fn create_texture_set_layout(device: &ash::Device) -> Result<vk::DescriptorSetLayout> {
436 let bindings = [
437 vk::DescriptorSetLayoutBinding::default()
438 .binding(0)
439 .descriptor_type(vk::DescriptorType::SAMPLED_IMAGE)
440 .descriptor_count(1)
441 .stage_flags(vk::ShaderStageFlags::FRAGMENT),
442 vk::DescriptorSetLayoutBinding::default()
443 .binding(1)
444 .descriptor_type(vk::DescriptorType::SAMPLER)
445 .descriptor_count(1)
446 .stage_flags(vk::ShaderStageFlags::FRAGMENT),
447 ];
448 let info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&bindings);
449 unsafe { device.create_descriptor_set_layout(&info, None) }.map_err(Into::into)
450}
451
452fn create_pipeline_layout(
453 device: &ash::Device,
454 layouts: &[vk::DescriptorSetLayout],
455) -> Result<vk::PipelineLayout> {
456 let info = vk::PipelineLayoutCreateInfo::default().set_layouts(layouts);
457 unsafe { device.create_pipeline_layout(&info, None) }.map_err(Into::into)
458}
459
460fn build_surface_pipeline(
461 device: &ash::Device,
462 pipeline_layout: vk::PipelineLayout,
463 target: TargetInfo,
464 name: &str,
465 fragment_entry: &str,
466 alpha: SurfaceAlpha,
467) -> Result<vk::Pipeline> {
468 let words = wgsl_to_spirv(name, stock_wgsl::SURFACE)?;
469 let shader_info = vk::ShaderModuleCreateInfo::default().code(&words);
470 let shader = unsafe { device.create_shader_module(&shader_info, None) }?;
471 let result = build_pipeline_with_module(
472 device,
473 pipeline_layout,
474 target,
475 shader,
476 name,
477 fragment_entry,
478 alpha,
479 );
480 unsafe {
481 device.destroy_shader_module(shader, None);
482 }
483 result
484}
485
486fn build_pipeline_with_module(
487 device: &ash::Device,
488 pipeline_layout: vk::PipelineLayout,
489 target: TargetInfo,
490 shader: vk::ShaderModule,
491 name: &str,
492 fragment_entry: &str,
493 alpha: SurfaceAlpha,
494) -> Result<vk::Pipeline> {
495 let vs_main = CString::new("vs_main").expect("static string has no nul");
496 let fs_main = CString::new(fragment_entry).expect("static fragment entry has no nul");
497 let stages = [
498 vk::PipelineShaderStageCreateInfo::default()
499 .stage(vk::ShaderStageFlags::VERTEX)
500 .module(shader)
501 .name(&vs_main),
502 vk::PipelineShaderStageCreateInfo::default()
503 .stage(vk::ShaderStageFlags::FRAGMENT)
504 .module(shader)
505 .name(&fs_main),
506 ];
507 let bindings = [
508 vk::VertexInputBindingDescription {
509 binding: 0,
510 stride: (2 * std::mem::size_of::<f32>()) as u32,
511 input_rate: vk::VertexInputRate::VERTEX,
512 },
513 vk::VertexInputBindingDescription {
514 binding: 1,
515 stride: std::mem::size_of::<SurfaceInstance>() as u32,
516 input_rate: vk::VertexInputRate::INSTANCE,
517 },
518 ];
519 let attrs = [
520 attr(0, 0, 0, vk::Format::R32G32_SFLOAT),
521 attr(1, 1, 0, vk::Format::R32G32B32A32_SFLOAT),
522 attr(2, 1, 16, vk::Format::R32G32B32A32_SFLOAT),
523 attr(3, 1, 32, vk::Format::R32G32_SFLOAT),
524 ];
525 let vertex_input = vk::PipelineVertexInputStateCreateInfo::default()
526 .vertex_binding_descriptions(&bindings)
527 .vertex_attribute_descriptions(&attrs);
528 let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
529 .topology(vk::PrimitiveTopology::TRIANGLE_STRIP);
530 let viewport_state = vk::PipelineViewportStateCreateInfo::default()
531 .viewport_count(1)
532 .scissor_count(1);
533 let rasterization = vk::PipelineRasterizationStateCreateInfo::default()
534 .polygon_mode(vk::PolygonMode::FILL)
535 .cull_mode(vk::CullModeFlags::empty())
536 .front_face(vk::FrontFace::COUNTER_CLOCKWISE)
537 .line_width(1.0);
538 let multisample = vk::PipelineMultisampleStateCreateInfo::default()
539 .rasterization_samples(target.sample_count)
540 .sample_shading_enable(target.sample_count != vk::SampleCountFlags::TYPE_1)
541 .min_sample_shading(1.0);
542 let blend_attachment = blend_attachment(alpha);
543 let blend_attachments = [blend_attachment];
544 let color_blend =
545 vk::PipelineColorBlendStateCreateInfo::default().attachments(&blend_attachments);
546 let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR];
547 let dynamic_state =
548 vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
549 let color_formats = [target.format];
550 let mut rendering =
551 vk::PipelineRenderingCreateInfo::default().color_attachment_formats(&color_formats);
552 let info = vk::GraphicsPipelineCreateInfo::default()
553 .stages(&stages)
554 .vertex_input_state(&vertex_input)
555 .input_assembly_state(&input_assembly)
556 .viewport_state(&viewport_state)
557 .rasterization_state(&rasterization)
558 .multisample_state(&multisample)
559 .color_blend_state(&color_blend)
560 .dynamic_state(&dynamic_state)
561 .layout(pipeline_layout)
562 .push_next(&mut rendering);
563 let pipelines =
564 unsafe { device.create_graphics_pipelines(vk::PipelineCache::null(), &[info], None) }
565 .map_err(|(_pipelines, err)| Error::Vulkan {
566 op: "create_graphics_pipelines",
567 result: err,
568 })?;
569 pipelines
570 .into_iter()
571 .next()
572 .ok_or(Error::PipelineCreationReturnedEmpty {
573 name: name.to_string(),
574 })
575}
576
577fn blend_attachment(alpha: SurfaceAlpha) -> vk::PipelineColorBlendAttachmentState {
578 let (src, dst) = match alpha {
579 SurfaceAlpha::Premultiplied | SurfaceAlpha::Straight => {
580 (vk::BlendFactor::ONE, vk::BlendFactor::ONE_MINUS_SRC_ALPHA)
581 }
582 SurfaceAlpha::Opaque => (vk::BlendFactor::ONE, vk::BlendFactor::ZERO),
583 };
584 vk::PipelineColorBlendAttachmentState::default()
585 .blend_enable(true)
586 .src_color_blend_factor(src)
587 .dst_color_blend_factor(dst)
588 .color_blend_op(vk::BlendOp::ADD)
589 .src_alpha_blend_factor(src)
590 .dst_alpha_blend_factor(dst)
591 .alpha_blend_op(vk::BlendOp::ADD)
592 .color_write_mask(
593 vk::ColorComponentFlags::R
594 | vk::ColorComponentFlags::G
595 | vk::ColorComponentFlags::B
596 | vk::ColorComponentFlags::A,
597 )
598}
599
600fn attr(
601 location: u32,
602 binding: u32,
603 offset: u32,
604 format: vk::Format,
605) -> vk::VertexInputAttributeDescription {
606 vk::VertexInputAttributeDescription {
607 location,
608 binding,
609 format,
610 offset,
611 }
612}