1#![warn(missing_docs)]
3use std::collections::HashMap;
4use std::default::Default;
5use std::sync::Arc;
6
7use bytemuck::{Pod, Zeroable};
8use egui::epaint::{
9 textures::TexturesDelta, ClippedPrimitive, ClippedShape, ImageData, ImageDelta, Primitive,
10};
11use egui::{Color32, Context, Rect, TextureId};
12use vulkano::buffer::{BufferAccess, BufferSlice, BufferUsage, CpuAccessibleBuffer};
13use vulkano::command_buffer::SubpassContents::Inline;
14use vulkano::command_buffer::{
15 AutoCommandBufferBuilder, AutoCommandBufferBuilderContextError, CopyBufferImageError,
16 DrawIndexedError, PrimaryAutoCommandBuffer,
17};
18use vulkano::descriptor_set::{
19 DescriptorSetCreationError, PersistentDescriptorSet, WriteDescriptorSet,
20};
21use vulkano::device::{Device, Queue};
22use vulkano::format::Format;
23use vulkano::image::{
24 ImageCreateFlags, ImageCreationError, ImageDimensions, ImageUsage, StorageImage,
25};
26use vulkano::pipeline::graphics::color_blend::{AttachmentBlend, BlendFactor, ColorBlendState};
27use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
28use vulkano::pipeline::graphics::rasterization::{CullMode, RasterizationState};
29use vulkano::pipeline::graphics::viewport::{Scissor, ViewportState};
30use vulkano::pipeline::graphics::{GraphicsPipeline, GraphicsPipelineCreationError};
31use vulkano::pipeline::Pipeline;
32use vulkano::pipeline::PipelineBindPoint;
33use vulkano::sampler::{
34 Filter, Sampler, SamplerCreateInfo, SamplerCreationError, SamplerMipmapMode,
35};
36mod shaders;
37
38#[derive(Default, Debug, Clone, Copy, Zeroable, Pod)]
39#[repr(C)]
40struct Vertex {
41 pub pos: [f32; 2],
42 pub uv: [f32; 2],
43 pub color: [f32; 4],
44}
45
46impl From<&egui::epaint::Vertex> for Vertex {
47 fn from(v: &egui::epaint::Vertex) -> Self {
48 let convert = {
49 |c: Color32| {
50 [
51 c.r() as f32 / 255.0,
52 c.g() as f32 / 255.0,
53 c.b() as f32 / 255.0,
54 c.a() as f32 / 255.0,
55 ]
56 }
57 };
58
59 Self {
60 pos: [v.pos.x, v.pos.y],
61 uv: [v.uv.x, v.uv.y],
62 color: convert(v.color),
63 }
64 }
65}
66
67vulkano::impl_vertex!(Vertex, pos, uv, color);
68
69use thiserror::Error;
70use vulkano::command_buffer::pool::CommandPoolBuilderAlloc;
71use vulkano::image::view::{ImageView, ImageViewCreationError};
72use vulkano::memory::DeviceMemoryAllocationError;
73use vulkano::pipeline::graphics::vertex_input::BuffersDefinition;
74use vulkano::render_pass::Subpass;
75
76#[derive(Error, Debug)]
77pub enum PainterCreationError {
78 #[error(transparent)]
79 CreatePipelineFailed(#[from] GraphicsPipelineCreationError),
80 #[error(transparent)]
81 CreateSamplerFailed(#[from] SamplerCreationError),
82}
83
84#[derive(Error, Debug)]
85pub enum UpdateTexturesError {
86 #[error(transparent)]
87 CreateImageViewFailed(#[from] ImageViewCreationError),
88 #[error(transparent)]
89 BuildFailed(#[from] DescriptorSetCreationError),
90 #[error(transparent)]
91 Alloc(#[from] DeviceMemoryAllocationError),
92 #[error(transparent)]
93 Copy(#[from] CopyBufferImageError),
94 #[error(transparent)]
95 CreateImage(#[from] ImageCreationError),
96}
97
98#[derive(Error, Debug)]
99pub enum DrawError {
100 #[error(transparent)]
101 UpdateSetFailed(#[from] UpdateTexturesError),
102 #[error(transparent)]
103 NextSubpassFailed(#[from] AutoCommandBufferBuilderContextError),
104 #[error(transparent)]
105 CreateBuffersFailed(#[from] DeviceMemoryAllocationError),
106 #[error(transparent)]
107 DrawIndexedFailed(#[from] DrawIndexedError),
108}
109
110#[must_use = "You must use this to avoid attempting to modify a texture that's still in use"]
111#[derive(PartialEq)]
112pub enum UpdateTexturesResult {
114 Unchanged,
116 Changed,
119}
120
121pub struct Painter {
123 device: Arc<Device>,
124 queue: Arc<Queue>,
125 pub pipeline: Arc<GraphicsPipeline>,
127 pub sampler: Arc<Sampler>,
129 images: HashMap<egui::TextureId, Arc<StorageImage>>,
130 texture_sets: HashMap<egui::TextureId, Arc<PersistentDescriptorSet>>,
131 texture_free_queue: Vec<egui::TextureId>,
132}
133
134impl Painter {
135 pub fn new(
138 device: Arc<Device>,
139 queue: Arc<Queue>,
140 subpass: Subpass,
141 ) -> Result<Self, PainterCreationError> {
142 let pipeline = create_pipeline(device.clone(), subpass.clone())?;
143 let sampler = create_sampler(device.clone())?;
144 Ok(Self {
145 device,
146 queue,
147 pipeline,
148 sampler,
149 images: Default::default(),
150 texture_sets: Default::default(),
151 texture_free_queue: Vec::new(),
152 })
153 }
154
155 fn write_image_delta<P>(
156 &mut self,
157 image: Arc<StorageImage>,
158 delta: &ImageDelta,
159 builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>,
160 ) -> Result<(), UpdateTexturesError>
161 where
162 P: CommandPoolBuilderAlloc,
163 {
164 let image_data = match &delta.image {
165 ImageData::Color(image) => image
166 .pixels
167 .iter()
168 .flat_map(|c| c.to_array())
169 .collect::<Vec<_>>(),
170 ImageData::Font(image) => image
171 .srgba_pixels(1.0)
172 .flat_map(|c| c.to_array())
173 .collect::<Vec<_>>(),
174 };
175
176 let img_buffer = CpuAccessibleBuffer::from_iter(
177 self.device.clone(),
178 BufferUsage::transfer_source(),
179 false,
180 image_data,
181 )?;
182
183 let size = [delta.image.width() as u32, delta.image.height() as u32, 1];
184 let offset = match delta.pos {
185 None => [0, 0, 0],
186 Some(pos) => [pos[0] as u32, pos[1] as u32, 0],
187 };
188
189 builder.copy_buffer_to_image_dimensions(img_buffer, image, offset, size, 0, 1, 0)?;
190 Ok(())
191 }
192
193 pub fn update_textures<P>(
199 &mut self,
200 textures_delta: TexturesDelta,
201 builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>,
202 ) -> Result<UpdateTexturesResult, UpdateTexturesError>
203 where
204 P: CommandPoolBuilderAlloc,
205 {
206 for texture_id in textures_delta.free {
207 self.texture_free_queue.push(texture_id);
208 }
209
210 let mut result = UpdateTexturesResult::Unchanged;
211
212 for (texture_id, delta) in &textures_delta.set {
213 let image = if delta.is_whole() {
214 let image = create_image(self.queue.clone(), &delta.image)?;
215 let layout = &self.pipeline.layout().set_layouts()[0];
216
217 let set = PersistentDescriptorSet::new(
218 layout.clone(),
219 [WriteDescriptorSet::image_view_sampler(
220 0,
221 ImageView::new_default(image.clone())?,
222 self.sampler.clone(),
223 )],
224 )?;
225
226 self.texture_sets.insert(*texture_id, set);
227 self.images.insert(*texture_id, image.clone());
228 image
229 } else {
230 result = UpdateTexturesResult::Changed; self.images[texture_id].clone()
232 };
233 self.write_image_delta(image, delta, builder)?;
234 }
235
236 Ok(result)
237 }
238
239 fn free_textures(&mut self) {
241 for texture_id in &self.texture_free_queue {
242 self.texture_sets.remove(texture_id);
243 self.images.remove(texture_id);
244 }
245
246 self.texture_free_queue.clear();
247 }
248
249 pub fn draw<P>(
251 &mut self,
252 builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>,
253 window_size_points: [f32; 2],
254 egui_ctx: &Context,
255 clipped_shapes: Vec<ClippedShape>,
256 ) -> Result<(), DrawError>
257 where
258 P: CommandPoolBuilderAlloc,
259 {
260 builder
261 .next_subpass(Inline)?
262 .bind_pipeline_graphics(self.pipeline.clone());
263
264 let clipped_primitives: Vec<ClippedPrimitive> = egui_ctx.tessellate(clipped_shapes);
265 let num_meshes = clipped_primitives.len();
266
267 let mut verts = Vec::<Vertex>::with_capacity(num_meshes * 4);
268 let mut indices = Vec::<u32>::with_capacity(num_meshes * 6);
269 let mut clips = Vec::<Rect>::with_capacity(num_meshes);
270 let mut texture_ids = Vec::<TextureId>::with_capacity(num_meshes);
271 let mut offsets = Vec::<(usize, usize)>::with_capacity(num_meshes);
272
273 for cm in clipped_primitives.iter() {
274 let clip = cm.clip_rect;
275 let mesh = match &cm.primitive {
276 Primitive::Mesh(mesh) => mesh,
277 Primitive::Callback(_) => {
278 continue; }
280 };
281
282 if mesh.vertices.len() == 0 || mesh.indices.len() == 0 {
284 continue;
285 }
286
287 offsets.push((verts.len(), indices.len()));
288 texture_ids.push(mesh.texture_id);
289
290 for v in mesh.vertices.iter() {
291 verts.push(v.into());
292 }
293
294 for i in mesh.indices.iter() {
295 indices.push(*i);
296 }
297
298 clips.push(clip);
299 }
300 offsets.push((verts.len(), indices.len()));
301
302 if clips.len() == 0 {
304 return Ok(());
305 }
306
307 let (vertex_buf, index_buf) = self.create_buffers((verts, indices))?;
308 for (idx, clip) in clips.iter().enumerate() {
309 let mut scissors = Vec::with_capacity(1);
310 let o = clip.min;
311 let (w, h) = (clip.width() as u32, clip.height() as u32);
312 scissors.push(Scissor {
313 origin: [(o.x as u32), (o.y as u32)],
314 dimensions: [w, h],
315 });
316 builder.set_scissor(0, scissors);
317
318 let offset = offsets[idx];
319 let end = offsets[idx + 1];
320
321 let vb_slice = BufferSlice::from_typed_buffer_access(vertex_buf.clone())
322 .slice(offset.0 as u64..end.0 as u64)
323 .unwrap();
324 let ib_slice = BufferSlice::from_typed_buffer_access(index_buf.clone())
325 .slice(offset.1 as u64..end.1 as u64)
326 .unwrap();
327
328 let texture_set = self.texture_sets.get(&texture_ids[idx]);
329 if texture_set.is_none() {
330 continue; }
332
333 builder
334 .bind_vertex_buffers(0, vb_slice.clone())
335 .bind_index_buffer(ib_slice.clone())
336 .bind_descriptor_sets(
337 PipelineBindPoint::Graphics,
338 self.pipeline.layout().clone(),
339 0,
340 texture_set.unwrap().clone(),
341 )
342 .push_constants(self.pipeline.layout().clone(), 0, window_size_points)
343 .draw_indexed(ib_slice.len() as u32, 1, 0, 0, 0)?;
344 }
345 self.free_textures();
346 Ok(())
347 }
348
349 fn create_buffers(
351 &self,
352 triangles: (Vec<Vertex>, Vec<u32>),
353 ) -> Result<
354 (
355 Arc<CpuAccessibleBuffer<[Vertex]>>,
356 Arc<CpuAccessibleBuffer<[u32]>>,
357 ),
358 DeviceMemoryAllocationError,
359 > {
360 let vertex_buffer = CpuAccessibleBuffer::from_iter(
361 self.device.clone(),
362 BufferUsage::vertex_buffer(),
363 false,
364 triangles.0.iter().cloned(),
365 )?;
366
367 let index_buffer = CpuAccessibleBuffer::from_iter(
368 self.device.clone(),
369 BufferUsage::index_buffer(),
370 false,
371 triangles.1.iter().cloned(),
372 )?;
373
374 Ok((vertex_buffer, index_buffer))
375 }
376}
377
378fn create_pipeline(
380 device: Arc<Device>,
381 subpass: Subpass,
382) -> Result<Arc<GraphicsPipeline>, GraphicsPipelineCreationError> {
383 let vs = shaders::vs::load(device.clone()).unwrap();
384 let fs = shaders::fs::load(device.clone()).unwrap();
385
386 let mut blend = AttachmentBlend::alpha();
387 blend.color_source = BlendFactor::One;
388
389 let pipeline = GraphicsPipeline::start()
390 .vertex_input_state(BuffersDefinition::new().vertex::<Vertex>())
391 .vertex_shader(vs.entry_point("main").unwrap(), ())
392 .input_assembly_state(InputAssemblyState::new())
393 .viewport_state(ViewportState::viewport_dynamic_scissor_dynamic(1))
394 .fragment_shader(fs.entry_point("main").unwrap(), ())
395 .rasterization_state(RasterizationState::new().cull_mode(CullMode::None))
396 .color_blend_state(ColorBlendState::new(subpass.num_color_attachments()).blend(blend))
397 .render_pass(subpass)
398 .build(device.clone())?;
399 Ok(pipeline)
400}
401
402fn create_sampler(device: Arc<Device>) -> Result<Arc<Sampler>, SamplerCreationError> {
404 Sampler::new(
405 device.clone(),
406 SamplerCreateInfo {
407 mag_filter: Filter::Linear,
408 min_filter: Filter::Linear,
409 mipmap_mode: SamplerMipmapMode::Linear,
410 ..Default::default()
411 },
412 )
413}
414
415fn create_image(
417 queue: Arc<Queue>,
418 texture: &ImageData,
419) -> Result<Arc<StorageImage>, ImageCreationError> {
420 let dimensions = ImageDimensions::Dim2d {
421 width: texture.width() as u32,
422 height: texture.height() as u32,
423 array_layers: 1,
424 };
425
426 let format = Format::R8G8B8A8_SRGB;
427
428 let usage = ImageUsage {
429 transfer_destination: true,
430 sampled: true,
431 storage: false,
432 ..ImageUsage::none()
433 };
434
435 let image = StorageImage::with_usage(
436 queue.device().clone(),
437 dimensions,
438 format,
439 usage,
440 ImageCreateFlags::none(),
441 [queue.family()],
442 )?;
443
444 Ok(image)
445}