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