1#![allow(unsafe_code)]
2
3use std::{borrow::Cow, num::NonZeroU64, ops::Range};
4
5use ahash::HashMap;
6use epaint::{emath::NumExt, PaintCallbackInfo, Primitive, Vertex};
7
8use wgpu::util::DeviceExt as _;
9
10#[cfg(not(all(
12 target_arch = "wasm32",
13 not(feature = "fragile-send-sync-non-atomic-wasm"),
14)))]
15pub type CallbackResources = type_map::concurrent::TypeMap;
17#[cfg(all(
18 target_arch = "wasm32",
19 not(feature = "fragile-send-sync-non-atomic-wasm"),
20))]
21pub type CallbackResources = type_map::TypeMap;
23
24pub struct Callback(Box<dyn CallbackTrait>);
30
31impl Callback {
32 pub fn new_paint_callback(
34 rect: epaint::emath::Rect,
35 callback: impl CallbackTrait + 'static,
36 ) -> epaint::PaintCallback {
37 epaint::PaintCallback {
38 rect,
39 callback: std::sync::Arc::new(Self(Box::new(callback))),
40 }
41 }
42}
43
44pub trait CallbackTrait: Send + Sync {
89 fn prepare(
90 &self,
91 _device: &wgpu::Device,
92 _queue: &wgpu::Queue,
93 _screen_descriptor: &ScreenDescriptor,
94 _egui_encoder: &mut wgpu::CommandEncoder,
95 _callback_resources: &mut CallbackResources,
96 ) -> Vec<wgpu::CommandBuffer> {
97 Vec::new()
98 }
99
100 fn finish_prepare(
102 &self,
103 _device: &wgpu::Device,
104 _queue: &wgpu::Queue,
105 _egui_encoder: &mut wgpu::CommandEncoder,
106 _callback_resources: &mut CallbackResources,
107 ) -> Vec<wgpu::CommandBuffer> {
108 Vec::new()
109 }
110
111 fn paint(
116 &self,
117 info: PaintCallbackInfo,
118 render_pass: &mut wgpu::RenderPass<'static>,
119 callback_resources: &CallbackResources,
120 );
121}
122
123pub struct ScreenDescriptor {
125 pub size_in_pixels: [u32; 2],
127
128 pub pixels_per_point: f32,
130}
131
132impl ScreenDescriptor {
133 fn screen_size_in_points(&self) -> [f32; 2] {
135 [
136 self.size_in_pixels[0] as f32 / self.pixels_per_point,
137 self.size_in_pixels[1] as f32 / self.pixels_per_point,
138 ]
139 }
140}
141
142#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
144#[repr(C)]
145struct UniformBuffer {
146 screen_size_in_points: [f32; 2],
147 dithering: u32,
148 _padding: u32,
151}
152
153impl PartialEq for UniformBuffer {
154 fn eq(&self, other: &Self) -> bool {
155 self.screen_size_in_points == other.screen_size_in_points
156 && self.dithering == other.dithering
157 }
158}
159
160struct SlicedBuffer {
161 buffer: wgpu::Buffer,
162 slices: Vec<Range<usize>>,
163 capacity: wgpu::BufferAddress,
164}
165
166pub struct Texture {
167 pub texture: Option<wgpu::Texture>,
169
170 pub bind_group: wgpu::BindGroup,
172
173 pub options: Option<epaint::textures::TextureOptions>,
176}
177
178pub struct Renderer {
180 pipeline: wgpu::RenderPipeline,
181
182 index_buffer: SlicedBuffer,
183 vertex_buffer: SlicedBuffer,
184
185 uniform_buffer: wgpu::Buffer,
186 previous_uniform_buffer_content: UniformBuffer,
187 uniform_bind_group: wgpu::BindGroup,
188 texture_bind_group_layout: wgpu::BindGroupLayout,
189
190 textures: HashMap<epaint::TextureId, Texture>,
194 next_user_texture_id: u64,
195 samplers: HashMap<epaint::textures::TextureOptions, wgpu::Sampler>,
196
197 dithering: bool,
198
199 pub callback_resources: CallbackResources,
203}
204
205impl Renderer {
206 pub fn new(
211 device: &wgpu::Device,
212 output_color_format: wgpu::TextureFormat,
213 output_depth_format: Option<wgpu::TextureFormat>,
214 msaa_samples: u32,
215 dithering: bool,
216 ) -> Self {
217 profiling::function_scope!();
218
219 let shader = wgpu::ShaderModuleDescriptor {
220 label: Some("egui"),
221 source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("egui.wgsl"))),
222 };
223 let module = {
224 profiling::scope!("create_shader_module");
225 device.create_shader_module(shader)
226 };
227
228 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
229 label: Some("egui_uniform_buffer"),
230 contents: bytemuck::cast_slice(&[UniformBuffer {
231 screen_size_in_points: [0.0, 0.0],
232 dithering: u32::from(dithering),
233 _padding: Default::default(),
234 }]),
235 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
236 });
237
238 let uniform_bind_group_layout = {
239 profiling::scope!("create_bind_group_layout");
240 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
241 label: Some("egui_uniform_bind_group_layout"),
242 entries: &[wgpu::BindGroupLayoutEntry {
243 binding: 0,
244 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
245 ty: wgpu::BindingType::Buffer {
246 has_dynamic_offset: false,
247 min_binding_size: NonZeroU64::new(std::mem::size_of::<UniformBuffer>() as _),
248 ty: wgpu::BufferBindingType::Uniform,
249 },
250 count: None,
251 }],
252 })
253 };
254
255 let uniform_bind_group = {
256 profiling::scope!("create_bind_group");
257 device.create_bind_group(&wgpu::BindGroupDescriptor {
258 label: Some("egui_uniform_bind_group"),
259 layout: &uniform_bind_group_layout,
260 entries: &[wgpu::BindGroupEntry {
261 binding: 0,
262 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
263 buffer: &uniform_buffer,
264 offset: 0,
265 size: None,
266 }),
267 }],
268 })
269 };
270
271 let texture_bind_group_layout = {
272 profiling::scope!("create_bind_group_layout");
273 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
274 label: Some("egui_texture_bind_group_layout"),
275 entries: &[
276 wgpu::BindGroupLayoutEntry {
277 binding: 0,
278 visibility: wgpu::ShaderStages::FRAGMENT,
279 ty: wgpu::BindingType::Texture {
280 multisampled: false,
281 sample_type: wgpu::TextureSampleType::Float { filterable: true },
282 view_dimension: wgpu::TextureViewDimension::D2,
283 },
284 count: None,
285 },
286 wgpu::BindGroupLayoutEntry {
287 binding: 1,
288 visibility: wgpu::ShaderStages::FRAGMENT,
289 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
290 count: None,
291 },
292 ],
293 })
294 };
295
296 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
297 label: Some("egui_pipeline_layout"),
298 bind_group_layouts: &[&uniform_bind_group_layout, &texture_bind_group_layout],
299 push_constant_ranges: &[],
300 });
301
302 let depth_stencil = output_depth_format.map(|format| wgpu::DepthStencilState {
303 format,
304 depth_write_enabled: false,
305 depth_compare: wgpu::CompareFunction::Always,
306 stencil: wgpu::StencilState::default(),
307 bias: wgpu::DepthBiasState::default(),
308 });
309
310 let pipeline = {
311 profiling::scope!("create_render_pipeline");
312 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
313 label: Some("egui_pipeline"),
314 layout: Some(&pipeline_layout),
315 vertex: wgpu::VertexState {
316 entry_point: Some("vs_main"),
317 module: &module,
318 buffers: &[wgpu::VertexBufferLayout {
319 array_stride: 5 * 4,
320 step_mode: wgpu::VertexStepMode::Vertex,
321 attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Uint32],
325 }],
326 compilation_options: wgpu::PipelineCompilationOptions::default()
327 },
328 primitive: wgpu::PrimitiveState {
329 topology: wgpu::PrimitiveTopology::TriangleList,
330 unclipped_depth: false,
331 conservative: false,
332 cull_mode: None,
333 front_face: wgpu::FrontFace::default(),
334 polygon_mode: wgpu::PolygonMode::default(),
335 strip_index_format: None,
336 },
337 depth_stencil,
338 multisample: wgpu::MultisampleState {
339 alpha_to_coverage_enabled: false,
340 count: msaa_samples,
341 mask: !0,
342 },
343
344 fragment: Some(wgpu::FragmentState {
345 module: &module,
346 entry_point: Some(if output_color_format.is_srgb() {
347 log::warn!("Detected a linear (sRGBA aware) framebuffer {:?}. egui prefers Rgba8Unorm or Bgra8Unorm", output_color_format);
348 "fs_main_linear_framebuffer"
349 } else {
350 "fs_main_gamma_framebuffer" }),
352 targets: &[Some(wgpu::ColorTargetState {
353 format: output_color_format,
354 blend: Some(wgpu::BlendState {
355 color: wgpu::BlendComponent {
356 src_factor: wgpu::BlendFactor::One,
357 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
358 operation: wgpu::BlendOperation::Add,
359 },
360 alpha: wgpu::BlendComponent {
361 src_factor: wgpu::BlendFactor::OneMinusDstAlpha,
362 dst_factor: wgpu::BlendFactor::One,
363 operation: wgpu::BlendOperation::Add,
364 },
365 }),
366 write_mask: wgpu::ColorWrites::ALL,
367 })],
368 compilation_options: wgpu::PipelineCompilationOptions::default()
369 }),
370 multiview: None,
371 cache: None,
372 }
373 )
374 };
375
376 const VERTEX_BUFFER_START_CAPACITY: wgpu::BufferAddress =
377 (std::mem::size_of::<Vertex>() * 1024) as _;
378 const INDEX_BUFFER_START_CAPACITY: wgpu::BufferAddress =
379 (std::mem::size_of::<u32>() * 1024 * 3) as _;
380
381 Self {
382 pipeline,
383 vertex_buffer: SlicedBuffer {
384 buffer: create_vertex_buffer(device, VERTEX_BUFFER_START_CAPACITY),
385 slices: Vec::with_capacity(64),
386 capacity: VERTEX_BUFFER_START_CAPACITY,
387 },
388 index_buffer: SlicedBuffer {
389 buffer: create_index_buffer(device, INDEX_BUFFER_START_CAPACITY),
390 slices: Vec::with_capacity(64),
391 capacity: INDEX_BUFFER_START_CAPACITY,
392 },
393 uniform_buffer,
394 previous_uniform_buffer_content: UniformBuffer {
396 screen_size_in_points: [0.0, 0.0],
397 dithering: 0,
398 _padding: 0,
399 },
400 uniform_bind_group,
401 texture_bind_group_layout,
402 textures: HashMap::default(),
403 next_user_texture_id: 0,
404 samplers: HashMap::default(),
405 dithering,
406 callback_resources: CallbackResources::default(),
407 }
408 }
409
410 pub fn render(
418 &self,
419 render_pass: &mut wgpu::RenderPass<'static>,
420 paint_jobs: &[epaint::ClippedPrimitive],
421 screen_descriptor: &ScreenDescriptor,
422 ) {
423 profiling::function_scope!();
424
425 let pixels_per_point = screen_descriptor.pixels_per_point;
426 let size_in_pixels = screen_descriptor.size_in_pixels;
427
428 let mut needs_reset = true;
431
432 let mut index_buffer_slices = self.index_buffer.slices.iter();
433 let mut vertex_buffer_slices = self.vertex_buffer.slices.iter();
434
435 for epaint::ClippedPrimitive {
436 clip_rect,
437 primitive,
438 } in paint_jobs
439 {
440 if needs_reset {
441 render_pass.set_viewport(
442 0.0,
443 0.0,
444 size_in_pixels[0] as f32,
445 size_in_pixels[1] as f32,
446 0.0,
447 1.0,
448 );
449 render_pass.set_pipeline(&self.pipeline);
450 render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
451 needs_reset = false;
452 }
453
454 {
455 let rect = ScissorRect::new(clip_rect, pixels_per_point, size_in_pixels);
456
457 if rect.width == 0 || rect.height == 0 {
458 if let Primitive::Mesh(_) = primitive {
460 index_buffer_slices.next().unwrap();
462 vertex_buffer_slices.next().unwrap();
463 }
464 continue;
465 }
466
467 render_pass.set_scissor_rect(rect.x, rect.y, rect.width, rect.height);
468 }
469
470 match primitive {
471 Primitive::Mesh(mesh) => {
472 let index_buffer_slice = index_buffer_slices.next().unwrap();
473 let vertex_buffer_slice = vertex_buffer_slices.next().unwrap();
474
475 if let Some(Texture { bind_group, .. }) = self.textures.get(&mesh.texture_id) {
476 render_pass.set_bind_group(1, bind_group, &[]);
477 render_pass.set_index_buffer(
478 self.index_buffer.buffer.slice(
479 index_buffer_slice.start as u64..index_buffer_slice.end as u64,
480 ),
481 wgpu::IndexFormat::Uint32,
482 );
483 render_pass.set_vertex_buffer(
484 0,
485 self.vertex_buffer.buffer.slice(
486 vertex_buffer_slice.start as u64..vertex_buffer_slice.end as u64,
487 ),
488 );
489 render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
490 } else {
491 log::warn!("Missing texture: {:?}", mesh.texture_id);
492 }
493 }
494 Primitive::Callback(callback) => {
495 let Some(cbfn) = callback.callback.downcast_ref::<Callback>() else {
496 continue;
498 };
499
500 let info = PaintCallbackInfo {
501 viewport: callback.rect,
502 clip_rect: *clip_rect,
503 pixels_per_point,
504 screen_size_px: size_in_pixels,
505 };
506
507 let viewport_px = info.viewport_in_pixels();
508 if viewport_px.width_px > 0 && viewport_px.height_px > 0 {
509 profiling::scope!("callback");
510
511 needs_reset = true;
512
513 render_pass.set_viewport(
522 viewport_px.left_px as f32,
523 viewport_px.top_px as f32,
524 viewport_px.width_px as f32,
525 viewport_px.height_px as f32,
526 0.0,
527 1.0,
528 );
529
530 cbfn.0.paint(info, render_pass, &self.callback_resources);
531 }
532 }
533 }
534 }
535
536 render_pass.set_scissor_rect(0, 0, size_in_pixels[0], size_in_pixels[1]);
537 }
538
539 pub fn update_texture(
541 &mut self,
542 device: &wgpu::Device,
543 queue: &wgpu::Queue,
544 id: epaint::TextureId,
545 image_delta: &epaint::ImageDelta,
546 ) {
547 profiling::function_scope!();
548
549 let width = image_delta.image.width() as u32;
550 let height = image_delta.image.height() as u32;
551
552 let size = wgpu::Extent3d {
553 width,
554 height,
555 depth_or_array_layers: 1,
556 };
557
558 let data_color32 = match &image_delta.image {
559 epaint::ImageData::Color(image) => {
560 assert_eq!(
561 width as usize * height as usize,
562 image.pixels.len(),
563 "Mismatch between texture size and texel count"
564 );
565 Cow::Borrowed(&image.pixels)
566 }
567 epaint::ImageData::Font(image) => {
568 assert_eq!(
569 width as usize * height as usize,
570 image.pixels.len(),
571 "Mismatch between texture size and texel count"
572 );
573 profiling::scope!("font -> sRGBA");
574 Cow::Owned(image.srgba_pixels(None).collect::<Vec<epaint::Color32>>())
575 }
576 };
577 let data_bytes: &[u8] = bytemuck::cast_slice(data_color32.as_slice());
578
579 let queue_write_data_to_texture = |texture, origin| {
580 profiling::scope!("write_texture");
581 queue.write_texture(
582 wgpu::TexelCopyTextureInfo {
583 texture,
584 mip_level: 0,
585 origin,
586 aspect: wgpu::TextureAspect::All,
587 },
588 data_bytes,
589 wgpu::TexelCopyBufferLayout {
590 offset: 0,
591 bytes_per_row: Some(4 * width),
592 rows_per_image: Some(height),
593 },
594 size,
595 );
596 };
597
598 let label_str = format!("egui_texid_{id:?}");
600 let label = Some(label_str.as_str());
601
602 let (texture, origin, bind_group) = if let Some(pos) = image_delta.pos {
603 let Texture {
605 texture,
606 bind_group,
607 options,
608 } = self
609 .textures
610 .remove(&id)
611 .expect("Tried to update a texture that has not been allocated yet.");
612 let texture = texture.expect("Tried to update user texture.");
613 let options = options.expect("Tried to update user texture.");
614 let origin = wgpu::Origin3d {
615 x: pos[0] as u32,
616 y: pos[1] as u32,
617 z: 0,
618 };
619
620 (
621 texture,
622 origin,
623 if image_delta.options == options {
626 Some(bind_group)
627 } else {
628 None
629 },
630 )
631 } else {
632 let texture = {
634 profiling::scope!("create_texture");
635 device.create_texture(&wgpu::TextureDescriptor {
636 label,
637 size,
638 mip_level_count: 1,
639 sample_count: 1,
640 dimension: wgpu::TextureDimension::D2,
641 format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
643 view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb],
644 })
645 };
646 let origin = wgpu::Origin3d::ZERO;
647 (texture, origin, None)
648 };
649
650 let bind_group = bind_group.unwrap_or_else(|| {
651 let sampler = self
652 .samplers
653 .entry(image_delta.options)
654 .or_insert_with(|| create_sampler(image_delta.options, device));
655 device.create_bind_group(&wgpu::BindGroupDescriptor {
656 label,
657 layout: &self.texture_bind_group_layout,
658 entries: &[
659 wgpu::BindGroupEntry {
660 binding: 0,
661 resource: wgpu::BindingResource::TextureView(
662 &texture.create_view(&wgpu::TextureViewDescriptor::default()),
663 ),
664 },
665 wgpu::BindGroupEntry {
666 binding: 1,
667 resource: wgpu::BindingResource::Sampler(sampler),
668 },
669 ],
670 })
671 });
672
673 queue_write_data_to_texture(&texture, origin);
674 self.textures.insert(
675 id,
676 Texture {
677 texture: Some(texture),
678 bind_group,
679 options: Some(image_delta.options),
680 },
681 );
682 }
683
684 pub fn free_texture(&mut self, id: &epaint::TextureId) {
685 if let Some(texture) = self.textures.remove(id).and_then(|t| t.texture) {
686 texture.destroy();
687 }
688 }
689
690 pub fn texture(&self, id: &epaint::TextureId) -> Option<&Texture> {
695 self.textures.get(id)
696 }
697
698 pub fn register_native_texture(
704 &mut self,
705 device: &wgpu::Device,
706 texture: &wgpu::TextureView,
707 texture_filter: wgpu::FilterMode,
708 ) -> epaint::TextureId {
709 self.register_native_texture_with_sampler_options(
710 device,
711 texture,
712 wgpu::SamplerDescriptor {
713 label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
714 mag_filter: texture_filter,
715 min_filter: texture_filter,
716 ..Default::default()
717 },
718 )
719 }
720
721 pub fn update_egui_texture_from_wgpu_texture(
725 &mut self,
726 device: &wgpu::Device,
727 texture: &wgpu::TextureView,
728 texture_filter: wgpu::FilterMode,
729 id: epaint::TextureId,
730 ) {
731 self.update_egui_texture_from_wgpu_texture_with_sampler_options(
732 device,
733 texture,
734 wgpu::SamplerDescriptor {
735 label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
736 mag_filter: texture_filter,
737 min_filter: texture_filter,
738 ..Default::default()
739 },
740 id,
741 );
742 }
743
744 #[allow(clippy::needless_pass_by_value)] pub fn register_native_texture_with_sampler_options(
754 &mut self,
755 device: &wgpu::Device,
756 texture: &wgpu::TextureView,
757 sampler_descriptor: wgpu::SamplerDescriptor<'_>,
758 ) -> epaint::TextureId {
759 profiling::function_scope!();
760
761 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
762 compare: None,
763 ..sampler_descriptor
764 });
765
766 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
767 label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
768 layout: &self.texture_bind_group_layout,
769 entries: &[
770 wgpu::BindGroupEntry {
771 binding: 0,
772 resource: wgpu::BindingResource::TextureView(texture),
773 },
774 wgpu::BindGroupEntry {
775 binding: 1,
776 resource: wgpu::BindingResource::Sampler(&sampler),
777 },
778 ],
779 });
780
781 let id = epaint::TextureId::User(self.next_user_texture_id);
782 self.textures.insert(
783 id,
784 Texture {
785 texture: None,
786 bind_group,
787 options: None,
788 },
789 );
790 self.next_user_texture_id += 1;
791
792 id
793 }
794
795 #[allow(clippy::needless_pass_by_value)] pub fn update_egui_texture_from_wgpu_texture_with_sampler_options(
801 &mut self,
802 device: &wgpu::Device,
803 texture: &wgpu::TextureView,
804 sampler_descriptor: wgpu::SamplerDescriptor<'_>,
805 id: epaint::TextureId,
806 ) {
807 profiling::function_scope!();
808
809 let Texture {
810 bind_group: user_texture_binding,
811 ..
812 } = self
813 .textures
814 .get_mut(&id)
815 .expect("Tried to update a texture that has not been allocated yet.");
816
817 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
818 compare: None,
819 ..sampler_descriptor
820 });
821
822 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
823 label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
824 layout: &self.texture_bind_group_layout,
825 entries: &[
826 wgpu::BindGroupEntry {
827 binding: 0,
828 resource: wgpu::BindingResource::TextureView(texture),
829 },
830 wgpu::BindGroupEntry {
831 binding: 1,
832 resource: wgpu::BindingResource::Sampler(&sampler),
833 },
834 ],
835 });
836
837 *user_texture_binding = bind_group;
838 }
839
840 pub fn update_buffers(
845 &mut self,
846 device: &wgpu::Device,
847 queue: &wgpu::Queue,
848 encoder: &mut wgpu::CommandEncoder,
849 paint_jobs: &[epaint::ClippedPrimitive],
850 screen_descriptor: &ScreenDescriptor,
851 ) -> Vec<wgpu::CommandBuffer> {
852 profiling::function_scope!();
853
854 let screen_size_in_points = screen_descriptor.screen_size_in_points();
855
856 let uniform_buffer_content = UniformBuffer {
857 screen_size_in_points,
858 dithering: u32::from(self.dithering),
859 _padding: Default::default(),
860 };
861 if uniform_buffer_content != self.previous_uniform_buffer_content {
862 profiling::scope!("update uniforms");
863 queue.write_buffer(
864 &self.uniform_buffer,
865 0,
866 bytemuck::cast_slice(&[uniform_buffer_content]),
867 );
868 self.previous_uniform_buffer_content = uniform_buffer_content;
869 }
870
871 let mut callbacks = Vec::new();
873 let (vertex_count, index_count) = {
874 profiling::scope!("count_vertices_indices");
875 paint_jobs.iter().fold((0, 0), |acc, clipped_primitive| {
876 match &clipped_primitive.primitive {
877 Primitive::Mesh(mesh) => {
878 (acc.0 + mesh.vertices.len(), acc.1 + mesh.indices.len())
879 }
880 Primitive::Callback(callback) => {
881 if let Some(c) = callback.callback.downcast_ref::<Callback>() {
882 callbacks.push(c.0.as_ref());
883 } else {
884 log::warn!("Unknown paint callback: expected `egui_wgpu::Callback`");
885 };
886 acc
887 }
888 }
889 })
890 };
891
892 if index_count > 0 {
893 profiling::scope!("indices", index_count.to_string().as_str());
894
895 self.index_buffer.slices.clear();
896
897 let required_index_buffer_size = (std::mem::size_of::<u32>() * index_count) as u64;
898 if self.index_buffer.capacity < required_index_buffer_size {
899 self.index_buffer.capacity =
901 (self.index_buffer.capacity * 2).at_least(required_index_buffer_size);
902 self.index_buffer.buffer = create_index_buffer(device, self.index_buffer.capacity);
903 }
904
905 let index_buffer_staging = queue.write_buffer_with(
906 &self.index_buffer.buffer,
907 0,
908 NonZeroU64::new(required_index_buffer_size).unwrap(),
909 );
910
911 let Some(mut index_buffer_staging) = index_buffer_staging else {
912 panic!("Failed to create staging buffer for index data. Index count: {index_count}. Required index buffer size: {required_index_buffer_size}. Actual size {} and capacity: {} (bytes)", self.index_buffer.buffer.size(), self.index_buffer.capacity);
913 };
914
915 let mut index_offset = 0;
916 for epaint::ClippedPrimitive { primitive, .. } in paint_jobs {
917 match primitive {
918 Primitive::Mesh(mesh) => {
919 let size = mesh.indices.len() * std::mem::size_of::<u32>();
920 let slice = index_offset..(size + index_offset);
921 index_buffer_staging[slice.clone()]
922 .copy_from_slice(bytemuck::cast_slice(&mesh.indices));
923 self.index_buffer.slices.push(slice);
924 index_offset += size;
925 }
926 Primitive::Callback(_) => {}
927 }
928 }
929 }
930 if vertex_count > 0 {
931 profiling::scope!("vertices", vertex_count.to_string().as_str());
932
933 self.vertex_buffer.slices.clear();
934
935 let required_vertex_buffer_size = (std::mem::size_of::<Vertex>() * vertex_count) as u64;
936 if self.vertex_buffer.capacity < required_vertex_buffer_size {
937 self.vertex_buffer.capacity =
939 (self.vertex_buffer.capacity * 2).at_least(required_vertex_buffer_size);
940 self.vertex_buffer.buffer =
941 create_vertex_buffer(device, self.vertex_buffer.capacity);
942 }
943
944 let vertex_buffer_staging = queue.write_buffer_with(
945 &self.vertex_buffer.buffer,
946 0,
947 NonZeroU64::new(required_vertex_buffer_size).unwrap(),
948 );
949
950 let Some(mut vertex_buffer_staging) = vertex_buffer_staging else {
951 panic!("Failed to create staging buffer for vertex data. Vertex count: {vertex_count}. Required vertex buffer size: {required_vertex_buffer_size}. Actual size {} and capacity: {} (bytes)", self.vertex_buffer.buffer.size(), self.vertex_buffer.capacity);
952 };
953
954 let mut vertex_offset = 0;
955 for epaint::ClippedPrimitive { primitive, .. } in paint_jobs {
956 match primitive {
957 Primitive::Mesh(mesh) => {
958 let size = mesh.vertices.len() * std::mem::size_of::<Vertex>();
959 let slice = vertex_offset..(size + vertex_offset);
960 vertex_buffer_staging[slice.clone()]
961 .copy_from_slice(bytemuck::cast_slice(&mesh.vertices));
962 self.vertex_buffer.slices.push(slice);
963 vertex_offset += size;
964 }
965 Primitive::Callback(_) => {}
966 }
967 }
968 }
969
970 let mut user_cmd_bufs = Vec::new();
971 {
972 profiling::scope!("prepare callbacks");
973 for callback in &callbacks {
974 user_cmd_bufs.extend(callback.prepare(
975 device,
976 queue,
977 screen_descriptor,
978 encoder,
979 &mut self.callback_resources,
980 ));
981 }
982 }
983 {
984 profiling::scope!("finish prepare callbacks");
985 for callback in &callbacks {
986 user_cmd_bufs.extend(callback.finish_prepare(
987 device,
988 queue,
989 encoder,
990 &mut self.callback_resources,
991 ));
992 }
993 }
994
995 user_cmd_bufs
996 }
997}
998
999fn create_sampler(
1000 options: epaint::textures::TextureOptions,
1001 device: &wgpu::Device,
1002) -> wgpu::Sampler {
1003 let mag_filter = match options.magnification {
1004 epaint::textures::TextureFilter::Nearest => wgpu::FilterMode::Nearest,
1005 epaint::textures::TextureFilter::Linear => wgpu::FilterMode::Linear,
1006 };
1007 let min_filter = match options.minification {
1008 epaint::textures::TextureFilter::Nearest => wgpu::FilterMode::Nearest,
1009 epaint::textures::TextureFilter::Linear => wgpu::FilterMode::Linear,
1010 };
1011 let address_mode = match options.wrap_mode {
1012 epaint::textures::TextureWrapMode::ClampToEdge => wgpu::AddressMode::ClampToEdge,
1013 epaint::textures::TextureWrapMode::Repeat => wgpu::AddressMode::Repeat,
1014 epaint::textures::TextureWrapMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat,
1015 };
1016 device.create_sampler(&wgpu::SamplerDescriptor {
1017 label: Some(&format!(
1018 "egui sampler (mag: {mag_filter:?}, min {min_filter:?})"
1019 )),
1020 mag_filter,
1021 min_filter,
1022 address_mode_u: address_mode,
1023 address_mode_v: address_mode,
1024 ..Default::default()
1025 })
1026}
1027
1028fn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {
1029 profiling::function_scope!();
1030 device.create_buffer(&wgpu::BufferDescriptor {
1031 label: Some("egui_vertex_buffer"),
1032 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
1033 size,
1034 mapped_at_creation: false,
1035 })
1036}
1037
1038fn create_index_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {
1039 profiling::function_scope!();
1040 device.create_buffer(&wgpu::BufferDescriptor {
1041 label: Some("egui_index_buffer"),
1042 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
1043 size,
1044 mapped_at_creation: false,
1045 })
1046}
1047
1048struct ScissorRect {
1050 x: u32,
1051 y: u32,
1052 width: u32,
1053 height: u32,
1054}
1055
1056impl ScissorRect {
1057 fn new(clip_rect: &epaint::Rect, pixels_per_point: f32, target_size: [u32; 2]) -> Self {
1058 let clip_min_x = pixels_per_point * clip_rect.min.x;
1060 let clip_min_y = pixels_per_point * clip_rect.min.y;
1061 let clip_max_x = pixels_per_point * clip_rect.max.x;
1062 let clip_max_y = pixels_per_point * clip_rect.max.y;
1063
1064 let clip_min_x = clip_min_x.round() as u32;
1066 let clip_min_y = clip_min_y.round() as u32;
1067 let clip_max_x = clip_max_x.round() as u32;
1068 let clip_max_y = clip_max_y.round() as u32;
1069
1070 let clip_min_x = clip_min_x.clamp(0, target_size[0]);
1072 let clip_min_y = clip_min_y.clamp(0, target_size[1]);
1073 let clip_max_x = clip_max_x.clamp(clip_min_x, target_size[0]);
1074 let clip_max_y = clip_max_y.clamp(clip_min_y, target_size[1]);
1075
1076 Self {
1077 x: clip_min_x,
1078 y: clip_min_y,
1079 width: clip_max_x - clip_min_x,
1080 height: clip_max_y - clip_min_y,
1081 }
1082 }
1083}
1084
1085#[cfg(not(all(
1087 target_arch = "wasm32",
1088 not(feature = "fragile-send-sync-non-atomic-wasm"),
1089)))]
1090#[test]
1091fn renderer_impl_send_sync() {
1092 fn assert_send_sync<T: Send + Sync>() {}
1093 assert_send_sync::<Renderer>();
1094}