1use egui::{epaint::Primitive, PaintCallbackInfo};
4use fxhash::FxHashMap;
5use std::{borrow::Cow, num::NonZeroU32};
6use type_map::concurrent::TypeMap;
7use wgpu::util::DeviceExt as _;
8use wgpu::{self, TextureViewDescriptor};
9
10const EGUI_WGSL: &str = r#"
11// Vertex shader bindings
12
13struct VertexOutput {
14 @location(0) tex_coord: vec2<f32>,
15 @location(1) color: vec4<f32>,
16 @builtin(position) position: vec4<f32>,
17};
18
19struct Locals {
20 screen_size: vec2<f32>,
21 // Uniform buffers need to be at least 16 bytes in WebGL.
22 // See https://github.com/gfx-rs/wgpu/issues/2072
23 _padding: vec2<u32>,
24};
25@group(0) @binding(0) var<uniform> r_locals: Locals;
26
27// 0-1 from 0-255
28fn linear_from_srgb(srgb: vec3<f32>) -> vec3<f32> {
29 let cutoff = srgb < vec3<f32>(10.31475);
30 let lower = srgb / vec3<f32>(3294.6);
31 let higher = pow((srgb + vec3<f32>(14.025)) / vec3<f32>(269.025), vec3<f32>(2.4));
32 return select(higher, lower, cutoff);
33}
34
35// [u8; 4] SRGB as u32 -> [r, g, b, a]
36fn unpack_color(color: u32) -> vec4<f32> {
37 return vec4<f32>(
38 f32(color & 255u),
39 f32((color >> 8u) & 255u),
40 f32((color >> 16u) & 255u),
41 f32((color >> 24u) & 255u),
42 );
43}
44
45fn position_from_screen(screen_pos: vec2<f32>) -> vec4<f32> {
46 return vec4<f32>(
47 2.0 * screen_pos.x / r_locals.screen_size.x - 1.0,
48 1.0 - 2.0 * screen_pos.y / r_locals.screen_size.y,
49 0.0,
50 1.0,
51 );
52}
53
54@vertex
55fn vs_main(
56 @location(0) a_pos: vec2<f32>,
57 @location(1) a_tex_coord: vec2<f32>,
58 @location(2) a_color: u32,
59) -> VertexOutput {
60 var out: VertexOutput;
61 out.tex_coord = a_tex_coord;
62 let color = unpack_color(a_color);
63 out.color = vec4<f32>(linear_from_srgb(color.rgb), color.a / 255.0);
64 out.position = position_from_screen(a_pos);
65 return out;
66}
67
68@vertex
69fn vs_conv_main(
70 @location(0) a_pos: vec2<f32>,
71 @location(1) a_tex_coord: vec2<f32>,
72 @location(2) a_color: u32,
73) -> VertexOutput {
74 var out: VertexOutput;
75 out.tex_coord = a_tex_coord;
76 let color = unpack_color(a_color);
77 out.color = vec4<f32>(color.rgba / 255.0);
78 out.position = position_from_screen(a_pos);
79 return out;
80}
81
82// Fragment shader bindings
83
84@group(1) @binding(0) var r_tex_color: texture_2d<f32>;
85@group(1) @binding(1) var r_tex_sampler: sampler;
86
87@fragment
88fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
89 return in.color * textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
90}
91"#;
92
93const IDX_BUF: &str = "egui_index_buffer";
94const VTX_BUF: &str = "egui_vertex_buffer";
95const UNI_BUF: &str = "egui_uniform_buffer";
96const WG_RPAS: &str = "wgpu render pass";
97const GRP_LAB: &str = "egui_pass";
98
99pub struct CallbackFn {
120 prepare: Box<PrepareCallback>,
121 paint: Box<PaintCallback>,
122}
123
124type PrepareCallback = dyn Fn(&wgpu::Device, &wgpu::Queue, &mut TypeMap) + Sync + Send;
125
126type PaintCallback =
127 dyn for<'a, 'b> Fn(PaintCallbackInfo, &'a mut wgpu::RenderPass<'b>, &'b TypeMap) + Sync + Send;
128
129impl Default for CallbackFn {
130 fn default() -> Self {
131 CallbackFn {
132 prepare: Box::new(|_, _, _| ()),
133 paint: Box::new(|_, _, _| ()),
134 }
135 }
136}
137
138#[allow(dead_code)]
139impl CallbackFn {
140 pub fn new() -> Self {
141 Self::default()
142 }
143
144 pub fn prepare<F>(mut self, prepare: F) -> Self
146 where
147 F: Fn(&wgpu::Device, &wgpu::Queue, &mut TypeMap) + Sync + Send + 'static,
148 {
149 self.prepare = Box::new(prepare) as _;
150 self
151 }
152
153 pub fn paint<F>(mut self, paint: F) -> Self
155 where
156 F: for<'a, 'b> Fn(PaintCallbackInfo, &'a mut wgpu::RenderPass<'b>, &'b TypeMap)
157 + Sync
158 + Send
159 + 'static,
160 {
161 self.paint = Box::new(paint) as _;
162 self
163 }
164}
165
166#[derive(Debug)]
168enum BufferType {
169 Uniform,
170 Index,
171 Vertex,
172}
173
174pub struct ScreenDescriptor {
176 pub size_in_pixels: [u32; 2],
178
179 pub pixels_per_point: f32,
181}
182
183impl ScreenDescriptor {
184 pub fn screen_size_in_points(&self) -> [f32; 2] {
186 [
187 self.size_in_pixels[0] as f32 / self.pixels_per_point,
188 self.size_in_pixels[1] as f32 / self.pixels_per_point,
189 ]
190 }
191}
192
193#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
195#[repr(C)]
196struct UniformBuffer {
197 screen_size_in_points: [f32; 2],
198 _padding: [u32; 2],
201}
202
203#[derive(Debug)]
205struct SizedBuffer {
206 buffer: wgpu::Buffer,
207 size: usize,
209}
210
211pub struct RenderPass<'a> {
213 render_pipeline: wgpu::RenderPipeline,
214 index_buffers: Vec<SizedBuffer>,
215 vertex_buffers: Vec<SizedBuffer>,
216 uniform_buffer: SizedBuffer,
217 uniform_bind_group: wgpu::BindGroup,
218 texture_bind_group_layout: wgpu::BindGroupLayout,
219 textures: FxHashMap<egui::TextureId, (Option<wgpu::Texture>, wgpu::BindGroup)>,
223 next_user_texture_id: u64,
224 pub paint_callback_resources: TypeMap,
227 sampler: wgpu::Sampler,
228 texture_size: wgpu::Extent3d,
229 pub tex_view_desc: TextureViewDescriptor<'a>,
230}
231
232impl<'a> RenderPass<'a> {
233 pub fn new(
237 device: &wgpu::Device,
238 texture_format: wgpu::TextureFormat,
239 msaa_samples: u32,
240 ) -> Self {
241 let shader = wgpu::ShaderModuleDescriptor {
242 label: Some("egui_shader"),
243 source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(EGUI_WGSL)),
244 };
245 let module = device.create_shader_module(shader);
246
247 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
248 label: Some(UNI_BUF),
249 contents: bytemuck::cast_slice(&[UniformBuffer {
250 screen_size_in_points: [0.0, 0.0],
251 _padding: Default::default(),
252 }]),
253 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
254 });
255 let uniform_buffer = SizedBuffer {
256 buffer: uniform_buffer,
257 size: std::mem::size_of::<UniformBuffer>(),
258 };
259
260 let uniform_bind_group_layout =
261 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
262 label: Some("egui_uniform_bind_group_layout"),
263 entries: &[wgpu::BindGroupLayoutEntry {
264 binding: 0,
265 visibility: wgpu::ShaderStages::VERTEX,
266 ty: wgpu::BindingType::Buffer {
267 has_dynamic_offset: false,
268 min_binding_size: None,
269 ty: wgpu::BufferBindingType::Uniform,
270 },
271 count: None,
272 }],
273 });
274
275 let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
276 label: Some("egui_uniform_bind_group"),
277 layout: &uniform_bind_group_layout,
278 entries: &[wgpu::BindGroupEntry {
279 binding: 0,
280 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
281 buffer: &uniform_buffer.buffer,
282 offset: 0,
283 size: None,
284 }),
285 }],
286 });
287
288 let texture_bind_group_layout =
289 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
290 label: Some("egui_texture_bind_group_layout"),
291 entries: &[
292 wgpu::BindGroupLayoutEntry {
293 binding: 0,
294 visibility: wgpu::ShaderStages::FRAGMENT,
295 ty: wgpu::BindingType::Texture {
296 multisampled: false,
297 sample_type: wgpu::TextureSampleType::Float { filterable: true },
298 view_dimension: wgpu::TextureViewDimension::D2,
299 },
300 count: None,
301 },
302 wgpu::BindGroupLayoutEntry {
303 binding: 1,
304 visibility: wgpu::ShaderStages::FRAGMENT,
305 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
306 count: None,
307 },
308 ],
309 });
310
311 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
312 label: Some("egui_pipeline_layout"),
313 bind_group_layouts: &[&uniform_bind_group_layout, &texture_bind_group_layout],
314 push_constant_ranges: &[],
315 });
316
317 let mut multisample = wgpu::MultisampleState::default();
318 multisample.count = msaa_samples;
319
320 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
321 label: Some("egui_pipeline"),
322 layout: Some(&pipeline_layout),
323 vertex: wgpu::VertexState {
324 entry_point: if texture_format.describe().srgb {
325 "vs_main"
326 } else {
327 "vs_conv_main"
328 },
329 module: &module,
330 buffers: &[wgpu::VertexBufferLayout {
331 array_stride: 5 * 4,
332 step_mode: wgpu::VertexStepMode::Vertex,
333 attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Uint32],
337 }],
338 },
339 primitive: wgpu::PrimitiveState {
340 topology: wgpu::PrimitiveTopology::TriangleList,
341 unclipped_depth: false,
342 conservative: false,
343 cull_mode: None,
344 front_face: wgpu::FrontFace::default(),
345 polygon_mode: wgpu::PolygonMode::default(),
346 strip_index_format: None,
347 },
348 depth_stencil: None,
349 multisample,
350
351 fragment: Some(wgpu::FragmentState {
352 module: &module,
353 entry_point: "fs_main",
354 targets: &[Some(wgpu::ColorTargetState {
355 format: texture_format,
356 blend: Some(wgpu::BlendState {
357 color: wgpu::BlendComponent {
358 src_factor: wgpu::BlendFactor::One,
359 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
360 operation: wgpu::BlendOperation::Add,
361 },
362 alpha: wgpu::BlendComponent {
363 src_factor: wgpu::BlendFactor::OneMinusDstAlpha,
364 dst_factor: wgpu::BlendFactor::One,
365 operation: wgpu::BlendOperation::Add,
366 },
367 }),
368 write_mask: wgpu::ColorWrites::ALL,
369 })],
370 }),
371 multiview: None,
372 });
373
374 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
375 label: None,
376 mag_filter: wgpu::FilterMode::Linear,
377 min_filter: wgpu::FilterMode::Nearest,
378 ..Default::default()
379 });
380
381 let texture_size = wgpu::Extent3d {
382 width: 200,
383 height: 200,
384 depth_or_array_layers: 1,
385 };
386
387 Self {
388 render_pipeline,
389 vertex_buffers: Vec::with_capacity(64),
390 index_buffers: Vec::with_capacity(64),
391 uniform_buffer,
392 uniform_bind_group,
393 texture_bind_group_layout,
394 textures: FxHashMap::default(),
395 next_user_texture_id: 0,
396 paint_callback_resources: TypeMap::default(),
397 sampler,
398 texture_size,
399 tex_view_desc: TextureViewDescriptor::default(),
400 }
401 }
402
403 pub fn execute(
405 &self,
406 encoder: &mut wgpu::CommandEncoder,
407 color_attachment: &wgpu::TextureView,
408 paint_jobs: Vec<egui::epaint::ClippedPrimitive>,
409 screen_descriptor: &ScreenDescriptor,
410 clear_color: Option<wgpu::Color>,
411 ) {
412 let load_operation = if let Some(color) = clear_color {
413 wgpu::LoadOp::Clear(color)
414 } else {
415 wgpu::LoadOp::Load
416 };
417
418 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
419 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
420 view: color_attachment,
421 resolve_target: None,
422 ops: wgpu::Operations {
423 load: load_operation,
424 store: true,
425 },
426 })],
427 depth_stencil_attachment: None,
428 label: Some(WG_RPAS),
429 });
430 rpass.push_debug_group(GRP_LAB);
431
432 self.execute_with_renderpass(&mut rpass, paint_jobs, screen_descriptor);
433
434 rpass.pop_debug_group();
435 }
436
437 pub fn execute_with_renderpass<'rpass>(
439 &'rpass self,
440 rpass: &mut wgpu::RenderPass<'rpass>,
441 paint_jobs: Vec<egui::epaint::ClippedPrimitive>,
442 screen_descriptor: &ScreenDescriptor,
443 ) {
444 let pixels_per_point = screen_descriptor.pixels_per_point;
445 let size_in_pixels = screen_descriptor.size_in_pixels;
446
447 let mut needs_reset = true;
450
451 let mut index_buffers = self.index_buffers.iter();
452 let mut vertex_buffers = self.vertex_buffers.iter();
453
454 for egui::ClippedPrimitive {
455 clip_rect,
456 primitive,
457 } in paint_jobs
458 {
459 if needs_reset {
460 rpass.set_viewport(
461 0.0,
462 0.0,
463 size_in_pixels[0] as f32,
464 size_in_pixels[1] as f32,
465 0.0,
466 1.0,
467 );
468 rpass.set_pipeline(&self.render_pipeline);
469 rpass.set_bind_group(0, &self.uniform_bind_group, &[]);
470 needs_reset = false;
471 }
472
473 {
474 let rect = ScissorRect::new(&clip_rect, pixels_per_point, size_in_pixels);
475
476 if rect.width == 0 || rect.height == 0 {
477 if let Primitive::Mesh(_) = primitive {
479 index_buffers.next().unwrap();
481 vertex_buffers.next().unwrap();
482 }
483 continue;
484 }
485
486 rpass.set_scissor_rect(rect.x, rect.y, rect.width, rect.height);
487 }
488
489 match primitive {
490 Primitive::Mesh(mesh) => {
491 let index_buffer = index_buffers.next().unwrap();
492 let vertex_buffer = vertex_buffers.next().unwrap();
493
494 if let Some((_texture, bind_group)) = self.textures.get(&mesh.texture_id) {
495 rpass.set_bind_group(1, bind_group, &[]);
496 rpass.set_index_buffer(
497 index_buffer.buffer.slice(..),
498 wgpu::IndexFormat::Uint32,
499 );
500 rpass.set_vertex_buffer(0, vertex_buffer.buffer.slice(..));
501 rpass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
502 } else {
503 eprintln!("Missing texture: {:?}", mesh.texture_id);
504 }
505 }
506 Primitive::Callback(callback) => {
507 let cbfn = if let Some(c) = callback.callback.downcast_ref::<CallbackFn>() {
508 c
509 } else {
510 continue;
512 };
513
514 if callback.rect.is_positive() {
515 needs_reset = true;
516
517 {
518 let rect_min_x = pixels_per_point * callback.rect.min.x;
521 let rect_min_y = pixels_per_point * callback.rect.min.y;
522 let rect_max_x = pixels_per_point * callback.rect.max.x;
523 let rect_max_y = pixels_per_point * callback.rect.max.y;
524
525 let rect_min_x = rect_min_x.round();
526 let rect_min_y = rect_min_y.round();
527 let rect_max_x = rect_max_x.round();
528 let rect_max_y = rect_max_y.round();
529
530 rpass.set_viewport(
531 rect_min_x,
532 rect_min_y,
533 rect_max_x - rect_min_x,
534 rect_max_y - rect_min_y,
535 0.0,
536 1.0,
537 );
538 }
539
540 (cbfn.paint)(
541 PaintCallbackInfo {
542 viewport: callback.rect,
543 clip_rect,
544 pixels_per_point,
545 screen_size_px: size_in_pixels,
546 },
547 rpass,
548 &self.paint_callback_resources,
549 );
550 }
551 }
552 }
553 }
554
555 rpass.set_scissor_rect(0, 0, size_in_pixels[0], size_in_pixels[1]);
556 }
557
558 pub fn update_texture(
560 &mut self,
561 device: &wgpu::Device,
562 queue: &wgpu::Queue,
563 id: egui::TextureId,
564 image_delta: &egui::epaint::ImageDelta,
565 ) {
566 let width = image_delta.image.width() as u32;
567 let height = image_delta.image.height() as u32;
568
569 self.texture_size.width = width;
570 self.texture_size.height = height;
571
572 let data_color32 = match &image_delta.image {
573 egui::ImageData::Color(image) => {
574 assert_eq!(
575 width as usize * height as usize,
576 image.pixels.len(),
577 "Mismatch between texture size and texel count"
578 );
579 Cow::Borrowed(&image.pixels)
580 }
581 egui::ImageData::Font(image) => {
582 assert_eq!(
583 width as usize * height as usize,
584 image.pixels.len(),
585 "Mismatch between texture size and texel count"
586 );
587 Cow::Owned(image.srgba_pixels(1.0).collect::<Vec<_>>())
588 }
589 };
590
591 let queue_write_data_to_texture = |texture, origin| {
592 queue.write_texture(
593 wgpu::ImageCopyTexture {
594 texture,
595 mip_level: 0,
596 origin,
597 aspect: wgpu::TextureAspect::All,
598 },
599 bytemuck::cast_slice(data_color32.as_slice()),
600 wgpu::ImageDataLayout {
601 offset: 0,
602 bytes_per_row: NonZeroU32::new(4 * width),
603 rows_per_image: NonZeroU32::new(height),
604 },
605 self.texture_size,
606 );
607 };
608
609 if let Some(pos) = image_delta.pos {
610 let (texture, _bind_group) = self
612 .textures
613 .get(&id)
614 .expect("Tried to update a texture that has not been allocated yet.");
615 let origin = wgpu::Origin3d {
616 x: pos[0] as u32,
617 y: pos[1] as u32,
618 z: 0,
619 };
620 queue_write_data_to_texture(
621 texture.as_ref().expect("Tried to update user texture."),
622 origin,
623 );
624 } else {
625 let texture = device.create_texture(&wgpu::TextureDescriptor {
627 label: None,
628 size: self.texture_size,
629 mip_level_count: 1,
630 sample_count: 1,
631 dimension: wgpu::TextureDimension::D2,
632 format: wgpu::TextureFormat::Rgba8UnormSrgb,
633 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
634 });
635
636 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
637 label: None,
638 layout: &self.texture_bind_group_layout,
639 entries: &[
640 wgpu::BindGroupEntry {
641 binding: 0,
642 resource: wgpu::BindingResource::TextureView(
643 &texture.create_view(&self.tex_view_desc),
644 ),
645 },
646 wgpu::BindGroupEntry {
647 binding: 1,
648 resource: wgpu::BindingResource::Sampler(&self.sampler),
649 },
650 ],
651 });
652
653 let origin = wgpu::Origin3d::ZERO;
654 queue_write_data_to_texture(&texture, origin);
655 self.textures.insert(id, (Some(texture), bind_group));
656 };
657 }
658
659 pub fn free_texture(&mut self, id: &egui::TextureId) {
660 self.textures.remove(id);
661 }
662
663 pub fn texture(
669 &self,
670 id: &egui::TextureId,
671 ) -> Option<&(Option<wgpu::Texture>, wgpu::BindGroup)> {
672 self.textures.get(id)
673 }
674
675 pub fn register_native_texture(
682 &mut self,
683 device: &wgpu::Device,
684 texture: &wgpu::TextureView,
685 texture_filter: wgpu::FilterMode,
686 ) -> egui::TextureId {
687 self.register_native_texture_with_sampler_options(
688 device,
689 texture,
690 wgpu::SamplerDescriptor {
691 label: Some(
692 format!(
693 "egui_user_image_{}_texture_sampler",
694 self.next_user_texture_id
695 )
696 .as_str(),
697 ),
698 mag_filter: texture_filter,
699 min_filter: texture_filter,
700 ..Default::default()
701 },
702 )
703 }
704
705 #[allow(clippy::needless_pass_by_value)] pub fn register_native_texture_with_sampler_options(
716 &mut self,
717 device: &wgpu::Device,
718 texture: &wgpu::TextureView,
719 sampler_descriptor: wgpu::SamplerDescriptor<'_>,
720 ) -> egui::TextureId {
721 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
722 compare: None,
723 ..sampler_descriptor
724 });
725
726 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
727 label: Some(
728 format!(
729 "egui_user_image_{}_texture_bind_group",
730 self.next_user_texture_id
731 )
732 .as_str(),
733 ),
734 layout: &self.texture_bind_group_layout,
735 entries: &[
736 wgpu::BindGroupEntry {
737 binding: 0,
738 resource: wgpu::BindingResource::TextureView(texture),
739 },
740 wgpu::BindGroupEntry {
741 binding: 1,
742 resource: wgpu::BindingResource::Sampler(&sampler),
743 },
744 ],
745 });
746
747 let id = egui::TextureId::User(self.next_user_texture_id);
748 self.textures.insert(id, (None, bind_group));
749 self.next_user_texture_id += 1;
750
751 id
752 }
753
754 pub fn update_buffers(
757 &mut self,
758 device: &wgpu::Device,
759 queue: &wgpu::Queue,
760 paint_jobs: &[egui::epaint::ClippedPrimitive],
761 screen_descriptor: &ScreenDescriptor,
762 ) {
763 let screen_size_in_points = screen_descriptor.screen_size_in_points();
764
765 self.update_buffer(
766 device,
767 queue,
768 &BufferType::Uniform,
769 0,
770 bytemuck::cast_slice(&[UniformBuffer {
771 screen_size_in_points,
772 _padding: Default::default(),
773 }]),
774 );
775
776 let mut mesh_idx = 0;
777 for egui::ClippedPrimitive { primitive, .. } in paint_jobs.iter() {
778 match primitive {
779 Primitive::Mesh(mesh) => {
780 {
781 let data: &[u8] = bytemuck::cast_slice(&mesh.indices);
782 if mesh_idx < self.index_buffers.len() {
783 self.update_buffer(device, queue, &BufferType::Index, mesh_idx, data);
784 } else {
785 let buffer =
786 device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
787 label: Some(IDX_BUF),
788 contents: data,
789 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
790 });
791 self.index_buffers.push(SizedBuffer {
792 buffer,
793 size: data.len(),
794 });
795 }
796 }
797
798 let data: &[u8] = bytemuck::cast_slice(&mesh.vertices);
799 if mesh_idx < self.vertex_buffers.len() {
800 self.update_buffer(device, queue, &BufferType::Vertex, mesh_idx, data);
801 } else {
802 let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
803 label: Some(VTX_BUF),
804 contents: data,
805 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
806 });
807
808 self.vertex_buffers.push(SizedBuffer {
809 buffer,
810 size: data.len(),
811 });
812 }
813
814 mesh_idx += 1;
815 }
816 Primitive::Callback(callback) => {
817 let cbfn = if let Some(c) = callback.callback.downcast_ref::<CallbackFn>() {
818 c
819 } else {
820 eprintln!("Unknown paint callback: expected `egui_gpu::CallbackFn`");
821 continue;
822 };
823
824 (cbfn.prepare)(device, queue, &mut self.paint_callback_resources);
825 }
826 }
827 }
828 }
829
830 fn update_buffer(
832 &mut self,
833 device: &wgpu::Device,
834 queue: &wgpu::Queue,
835 buffer_type: &BufferType,
836 index: usize,
837 data: &[u8],
838 ) {
839 let (buffer, storage, label) = match buffer_type {
840 BufferType::Index => (
841 &mut self.index_buffers[index],
842 wgpu::BufferUsages::INDEX,
843 IDX_BUF,
844 ),
845 BufferType::Vertex => (
846 &mut self.vertex_buffers[index],
847 wgpu::BufferUsages::VERTEX,
848 VTX_BUF,
849 ),
850 BufferType::Uniform => (
851 &mut self.uniform_buffer,
852 wgpu::BufferUsages::UNIFORM,
853 UNI_BUF,
854 ),
855 };
856
857 if data.len() > buffer.size {
858 buffer.size = data.len();
859 buffer.buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
860 label: Some(label),
861 contents: bytemuck::cast_slice(data),
862 usage: storage | wgpu::BufferUsages::COPY_DST,
863 });
864 } else {
865 queue.write_buffer(&buffer.buffer, 0, data);
866 }
867 }
868}
869
870struct ScissorRect {
872 x: u32,
873 y: u32,
874 width: u32,
875 height: u32,
876}
877
878impl ScissorRect {
879 fn new(clip_rect: &egui::Rect, pixels_per_point: f32, target_size: [u32; 2]) -> Self {
880 let clip_min_x = (pixels_per_point * clip_rect.min.x).round() as u32;
882 let clip_min_y = (pixels_per_point * clip_rect.min.y).round() as u32;
883 let clip_max_x = (pixels_per_point * clip_rect.max.x).round() as u32;
884 let clip_max_y = (pixels_per_point * clip_rect.max.y).round() as u32;
885
886 let clip_min_x = clip_min_x.clamp(0, target_size[0]);
888 let clip_min_y = clip_min_y.clamp(0, target_size[1]);
889 let clip_max_x = clip_max_x.clamp(clip_min_x, target_size[0]);
890 let clip_max_y = clip_max_y.clamp(clip_min_y, target_size[1]);
891
892 ScissorRect {
893 x: clip_min_x,
894 y: clip_min_y,
895 width: clip_max_x - clip_min_x,
896 height: clip_max_y - clip_min_y,
897 }
898 }
899}