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