#![allow(unsafe_code)]
use std::{borrow::Cow, num::NonZeroU64, ops::Range};
use ahash::HashMap;
use epaint::{emath::NumExt, PaintCallbackInfo, Primitive, Vertex};
use wgpu::util::DeviceExt as _;
pub type CallbackResources = type_map::concurrent::TypeMap;
pub struct Callback(Box<dyn CallbackTrait>);
impl Callback {
pub fn new_paint_callback(
rect: epaint::emath::Rect,
callback: impl CallbackTrait + 'static,
) -> epaint::PaintCallback {
epaint::PaintCallback {
rect,
callback: std::sync::Arc::new(Self(Box::new(callback))),
}
}
}
pub trait CallbackTrait: Send + Sync {
fn prepare(
&self,
_device: &wgpu::Device,
_queue: &wgpu::Queue,
_screen_descriptor: &ScreenDescriptor,
_egui_encoder: &mut wgpu::CommandEncoder,
_callback_resources: &mut CallbackResources,
) -> Vec<wgpu::CommandBuffer> {
Vec::new()
}
fn finish_prepare(
&self,
_device: &wgpu::Device,
_queue: &wgpu::Queue,
_egui_encoder: &mut wgpu::CommandEncoder,
_callback_resources: &mut CallbackResources,
) -> Vec<wgpu::CommandBuffer> {
Vec::new()
}
fn paint<'a>(
&'a self,
info: PaintCallbackInfo,
render_pass: &mut wgpu::RenderPass<'a>,
callback_resources: &'a CallbackResources,
);
}
pub struct ScreenDescriptor {
pub size_in_pixels: [u32; 2],
pub pixels_per_point: f32,
}
impl ScreenDescriptor {
fn screen_size_in_points(&self) -> [f32; 2] {
[
self.size_in_pixels[0] as f32 / self.pixels_per_point,
self.size_in_pixels[1] as f32 / self.pixels_per_point,
]
}
}
#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
struct UniformBuffer {
screen_size_in_points: [f32; 2],
_padding: [u32; 2],
}
impl PartialEq for UniformBuffer {
fn eq(&self, other: &Self) -> bool {
self.screen_size_in_points == other.screen_size_in_points
}
}
struct SlicedBuffer {
buffer: wgpu::Buffer,
slices: Vec<Range<usize>>,
capacity: wgpu::BufferAddress,
}
pub struct Renderer {
pipeline: wgpu::RenderPipeline,
index_buffer: SlicedBuffer,
vertex_buffer: SlicedBuffer,
uniform_buffer: wgpu::Buffer,
previous_uniform_buffer_content: UniformBuffer,
uniform_bind_group: wgpu::BindGroup,
texture_bind_group_layout: wgpu::BindGroupLayout,
textures: HashMap<epaint::TextureId, (Option<wgpu::Texture>, wgpu::BindGroup)>,
next_user_texture_id: u64,
samplers: HashMap<epaint::textures::TextureOptions, wgpu::Sampler>,
pub callback_resources: CallbackResources,
}
impl Renderer {
pub fn new(
device: &wgpu::Device,
output_color_format: wgpu::TextureFormat,
output_depth_format: Option<wgpu::TextureFormat>,
msaa_samples: u32,
) -> Self {
crate::profile_function!();
let shader = wgpu::ShaderModuleDescriptor {
label: Some("egui"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("egui.wgsl"))),
};
let module = {
crate::profile_scope!("create_shader_module");
device.create_shader_module(shader)
};
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("egui_uniform_buffer"),
contents: bytemuck::cast_slice(&[UniformBuffer {
screen_size_in_points: [0.0, 0.0],
_padding: Default::default(),
}]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
let uniform_bind_group_layout = {
crate::profile_scope!("create_bind_group_layout");
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("egui_uniform_bind_group_layout"),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
has_dynamic_offset: false,
min_binding_size: NonZeroU64::new(std::mem::size_of::<UniformBuffer>() as _),
ty: wgpu::BufferBindingType::Uniform,
},
count: None,
}],
})
};
let uniform_bind_group = {
crate::profile_scope!("create_bind_group");
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("egui_uniform_bind_group"),
layout: &uniform_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &uniform_buffer,
offset: 0,
size: None,
}),
}],
})
};
let texture_bind_group_layout = {
crate::profile_scope!("create_bind_group_layout");
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("egui_texture_bind_group_layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
})
};
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("egui_pipeline_layout"),
bind_group_layouts: &[&uniform_bind_group_layout, &texture_bind_group_layout],
push_constant_ranges: &[],
});
let depth_stencil = output_depth_format.map(|format| wgpu::DepthStencilState {
format,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::Always,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
});
let pipeline = {
crate::profile_scope!("create_render_pipeline");
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("egui_pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
entry_point: "vs_main",
module: &module,
buffers: &[wgpu::VertexBufferLayout {
array_stride: 5 * 4,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Uint32],
}],
compilation_options: wgpu::PipelineCompilationOptions::default()
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
unclipped_depth: false,
conservative: false,
cull_mode: None,
front_face: wgpu::FrontFace::default(),
polygon_mode: wgpu::PolygonMode::default(),
strip_index_format: None,
},
depth_stencil,
multisample: wgpu::MultisampleState {
alpha_to_coverage_enabled: false,
count: msaa_samples,
mask: !0,
},
fragment: Some(wgpu::FragmentState {
module: &module,
entry_point: if output_color_format.is_srgb() {
log::warn!("Detected a linear (sRGBA aware) framebuffer {:?}. egui prefers Rgba8Unorm or Bgra8Unorm", output_color_format);
"fs_main_linear_framebuffer"
} else {
"fs_main_gamma_framebuffer" },
targets: &[Some(wgpu::ColorTargetState {
format: output_color_format,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::OneMinusDstAlpha,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: wgpu::PipelineCompilationOptions::default()
}),
multiview: None,
}
)
};
const VERTEX_BUFFER_START_CAPACITY: wgpu::BufferAddress =
(std::mem::size_of::<Vertex>() * 1024) as _;
const INDEX_BUFFER_START_CAPACITY: wgpu::BufferAddress =
(std::mem::size_of::<u32>() * 1024 * 3) as _;
Self {
pipeline,
vertex_buffer: SlicedBuffer {
buffer: create_vertex_buffer(device, VERTEX_BUFFER_START_CAPACITY),
slices: Vec::with_capacity(64),
capacity: VERTEX_BUFFER_START_CAPACITY,
},
index_buffer: SlicedBuffer {
buffer: create_index_buffer(device, INDEX_BUFFER_START_CAPACITY),
slices: Vec::with_capacity(64),
capacity: INDEX_BUFFER_START_CAPACITY,
},
uniform_buffer,
previous_uniform_buffer_content: UniformBuffer {
screen_size_in_points: [0.0, 0.0],
_padding: [0, 0],
},
uniform_bind_group,
texture_bind_group_layout,
textures: HashMap::default(),
next_user_texture_id: 0,
samplers: HashMap::default(),
callback_resources: CallbackResources::default(),
}
}
pub fn render<'rp>(
&'rp self,
render_pass: &mut wgpu::RenderPass<'rp>,
paint_jobs: &'rp [epaint::ClippedPrimitive],
screen_descriptor: &ScreenDescriptor,
) {
crate::profile_function!();
let pixels_per_point = screen_descriptor.pixels_per_point;
let size_in_pixels = screen_descriptor.size_in_pixels;
let mut needs_reset = true;
let mut index_buffer_slices = self.index_buffer.slices.iter();
let mut vertex_buffer_slices = self.vertex_buffer.slices.iter();
for epaint::ClippedPrimitive {
clip_rect,
primitive,
} in paint_jobs
{
if needs_reset {
render_pass.set_viewport(
0.0,
0.0,
size_in_pixels[0] as f32,
size_in_pixels[1] as f32,
0.0,
1.0,
);
render_pass.set_pipeline(&self.pipeline);
render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
needs_reset = false;
}
{
let rect = ScissorRect::new(clip_rect, pixels_per_point, size_in_pixels);
if rect.width == 0 || rect.height == 0 {
if let Primitive::Mesh(_) = primitive {
index_buffer_slices.next().unwrap();
vertex_buffer_slices.next().unwrap();
}
continue;
}
render_pass.set_scissor_rect(rect.x, rect.y, rect.width, rect.height);
}
match primitive {
Primitive::Mesh(mesh) => {
let index_buffer_slice = index_buffer_slices.next().unwrap();
let vertex_buffer_slice = vertex_buffer_slices.next().unwrap();
if let Some((_texture, bind_group)) = self.textures.get(&mesh.texture_id) {
render_pass.set_bind_group(1, bind_group, &[]);
render_pass.set_index_buffer(
self.index_buffer.buffer.slice(
index_buffer_slice.start as u64..index_buffer_slice.end as u64,
),
wgpu::IndexFormat::Uint32,
);
render_pass.set_vertex_buffer(
0,
self.vertex_buffer.buffer.slice(
vertex_buffer_slice.start as u64..vertex_buffer_slice.end as u64,
),
);
render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
} else {
log::warn!("Missing texture: {:?}", mesh.texture_id);
}
}
Primitive::Callback(callback) => {
let Some(cbfn) = callback.callback.downcast_ref::<Callback>() else {
continue;
};
let info = PaintCallbackInfo {
viewport: callback.rect,
clip_rect: *clip_rect,
pixels_per_point,
screen_size_px: size_in_pixels,
};
let viewport_px = info.viewport_in_pixels();
if viewport_px.width_px > 0 && viewport_px.height_px > 0 {
crate::profile_scope!("callback");
needs_reset = true;
render_pass.set_viewport(
viewport_px.left_px as f32,
viewport_px.top_px as f32,
viewport_px.width_px as f32,
viewport_px.height_px as f32,
0.0,
1.0,
);
cbfn.0.paint(info, render_pass, &self.callback_resources);
}
}
}
}
render_pass.set_scissor_rect(0, 0, size_in_pixels[0], size_in_pixels[1]);
}
pub fn update_texture(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
id: epaint::TextureId,
image_delta: &epaint::ImageDelta,
) {
crate::profile_function!();
let width = image_delta.image.width() as u32;
let height = image_delta.image.height() as u32;
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let data_color32 = match &image_delta.image {
epaint::ImageData::Color(image) => {
assert_eq!(
width as usize * height as usize,
image.pixels.len(),
"Mismatch between texture size and texel count"
);
Cow::Borrowed(&image.pixels)
}
epaint::ImageData::Font(image) => {
assert_eq!(
width as usize * height as usize,
image.pixels.len(),
"Mismatch between texture size and texel count"
);
crate::profile_scope!("font -> sRGBA");
Cow::Owned(image.srgba_pixels(None).collect::<Vec<egui::Color32>>())
}
};
let data_bytes: &[u8] = bytemuck::cast_slice(data_color32.as_slice());
let queue_write_data_to_texture = |texture, origin| {
crate::profile_scope!("write_texture");
queue.write_texture(
wgpu::ImageCopyTexture {
texture,
mip_level: 0,
origin,
aspect: wgpu::TextureAspect::All,
},
data_bytes,
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * width),
rows_per_image: Some(height),
},
size,
);
};
if let Some(pos) = image_delta.pos {
let (texture, _bind_group) = self
.textures
.get(&id)
.expect("Tried to update a texture that has not been allocated yet.");
let origin = wgpu::Origin3d {
x: pos[0] as u32,
y: pos[1] as u32,
z: 0,
};
queue_write_data_to_texture(
texture.as_ref().expect("Tried to update user texture."),
origin,
);
} else {
let label_str = format!("egui_texid_{id:?}");
let label = Some(label_str.as_str());
let texture = {
crate::profile_scope!("create_texture");
device.create_texture(&wgpu::TextureDescriptor {
label,
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb],
})
};
let sampler = self
.samplers
.entry(image_delta.options)
.or_insert_with(|| create_sampler(image_delta.options, device));
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label,
layout: &self.texture_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(
&texture.create_view(&wgpu::TextureViewDescriptor::default()),
),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(sampler),
},
],
});
let origin = wgpu::Origin3d::ZERO;
queue_write_data_to_texture(&texture, origin);
self.textures.insert(id, (Some(texture), bind_group));
};
}
pub fn free_texture(&mut self, id: &epaint::TextureId) {
self.textures.remove(id);
}
pub fn texture(
&self,
id: &epaint::TextureId,
) -> Option<&(Option<wgpu::Texture>, wgpu::BindGroup)> {
self.textures.get(id)
}
pub fn register_native_texture(
&mut self,
device: &wgpu::Device,
texture: &wgpu::TextureView,
texture_filter: wgpu::FilterMode,
) -> epaint::TextureId {
self.register_native_texture_with_sampler_options(
device,
texture,
wgpu::SamplerDescriptor {
label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
mag_filter: texture_filter,
min_filter: texture_filter,
..Default::default()
},
)
}
pub fn update_egui_texture_from_wgpu_texture(
&mut self,
device: &wgpu::Device,
texture: &wgpu::TextureView,
texture_filter: wgpu::FilterMode,
id: epaint::TextureId,
) {
self.update_egui_texture_from_wgpu_texture_with_sampler_options(
device,
texture,
wgpu::SamplerDescriptor {
label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
mag_filter: texture_filter,
min_filter: texture_filter,
..Default::default()
},
id,
);
}
#[allow(clippy::needless_pass_by_value)] pub fn register_native_texture_with_sampler_options(
&mut self,
device: &wgpu::Device,
texture: &wgpu::TextureView,
sampler_descriptor: wgpu::SamplerDescriptor<'_>,
) -> epaint::TextureId {
crate::profile_function!();
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
compare: None,
..sampler_descriptor
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
layout: &self.texture_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(texture),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
},
],
});
let id = epaint::TextureId::User(self.next_user_texture_id);
self.textures.insert(id, (None, bind_group));
self.next_user_texture_id += 1;
id
}
#[allow(clippy::needless_pass_by_value)] pub fn update_egui_texture_from_wgpu_texture_with_sampler_options(
&mut self,
device: &wgpu::Device,
texture: &wgpu::TextureView,
sampler_descriptor: wgpu::SamplerDescriptor<'_>,
id: epaint::TextureId,
) {
crate::profile_function!();
let (_user_texture, user_texture_binding) = self
.textures
.get_mut(&id)
.expect("Tried to update a texture that has not been allocated yet.");
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
compare: None,
..sampler_descriptor
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some(format!("egui_user_image_{}", self.next_user_texture_id).as_str()),
layout: &self.texture_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(texture),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
},
],
});
*user_texture_binding = bind_group;
}
pub fn update_buffers(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
encoder: &mut wgpu::CommandEncoder,
paint_jobs: &[epaint::ClippedPrimitive],
screen_descriptor: &ScreenDescriptor,
) -> Vec<wgpu::CommandBuffer> {
crate::profile_function!();
let screen_size_in_points = screen_descriptor.screen_size_in_points();
let uniform_buffer_content = UniformBuffer {
screen_size_in_points,
_padding: Default::default(),
};
if uniform_buffer_content != self.previous_uniform_buffer_content {
crate::profile_scope!("update uniforms");
queue.write_buffer(
&self.uniform_buffer,
0,
bytemuck::cast_slice(&[uniform_buffer_content]),
);
self.previous_uniform_buffer_content = uniform_buffer_content;
}
let mut callbacks = Vec::new();
let (vertex_count, index_count) = {
crate::profile_scope!("count_vertices_indices");
paint_jobs.iter().fold((0, 0), |acc, clipped_primitive| {
match &clipped_primitive.primitive {
Primitive::Mesh(mesh) => {
(acc.0 + mesh.vertices.len(), acc.1 + mesh.indices.len())
}
Primitive::Callback(callback) => {
if let Some(c) = callback.callback.downcast_ref::<Callback>() {
callbacks.push(c.0.as_ref());
} else {
log::warn!("Unknown paint callback: expected `egui_wgpu::Callback`");
};
acc
}
}
})
};
if index_count > 0 {
crate::profile_scope!("indices", index_count.to_string());
self.index_buffer.slices.clear();
let required_index_buffer_size = (std::mem::size_of::<u32>() * index_count) as u64;
if self.index_buffer.capacity < required_index_buffer_size {
self.index_buffer.capacity =
(self.index_buffer.capacity * 2).at_least(required_index_buffer_size);
self.index_buffer.buffer = create_index_buffer(device, self.index_buffer.capacity);
}
let index_buffer_staging = queue.write_buffer_with(
&self.index_buffer.buffer,
0,
NonZeroU64::new(required_index_buffer_size).unwrap(),
);
let Some(mut index_buffer_staging) = index_buffer_staging else {
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);
};
let mut index_offset = 0;
for epaint::ClippedPrimitive { primitive, .. } in paint_jobs {
match primitive {
Primitive::Mesh(mesh) => {
let size = mesh.indices.len() * std::mem::size_of::<u32>();
let slice = index_offset..(size + index_offset);
index_buffer_staging[slice.clone()]
.copy_from_slice(bytemuck::cast_slice(&mesh.indices));
self.index_buffer.slices.push(slice);
index_offset += size;
}
Primitive::Callback(_) => {}
}
}
}
if vertex_count > 0 {
crate::profile_scope!("vertices", vertex_count.to_string());
self.vertex_buffer.slices.clear();
let required_vertex_buffer_size = (std::mem::size_of::<Vertex>() * vertex_count) as u64;
if self.vertex_buffer.capacity < required_vertex_buffer_size {
self.vertex_buffer.capacity =
(self.vertex_buffer.capacity * 2).at_least(required_vertex_buffer_size);
self.vertex_buffer.buffer =
create_vertex_buffer(device, self.vertex_buffer.capacity);
}
let vertex_buffer_staging = queue.write_buffer_with(
&self.vertex_buffer.buffer,
0,
NonZeroU64::new(required_vertex_buffer_size).unwrap(),
);
let Some(mut vertex_buffer_staging) = vertex_buffer_staging else {
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);
};
let mut vertex_offset = 0;
for epaint::ClippedPrimitive { primitive, .. } in paint_jobs {
match primitive {
Primitive::Mesh(mesh) => {
let size = mesh.vertices.len() * std::mem::size_of::<Vertex>();
let slice = vertex_offset..(size + vertex_offset);
vertex_buffer_staging[slice.clone()]
.copy_from_slice(bytemuck::cast_slice(&mesh.vertices));
self.vertex_buffer.slices.push(slice);
vertex_offset += size;
}
Primitive::Callback(_) => {}
}
}
}
let mut user_cmd_bufs = Vec::new();
{
crate::profile_scope!("prepare callbacks");
for callback in &callbacks {
user_cmd_bufs.extend(callback.prepare(
device,
queue,
screen_descriptor,
encoder,
&mut self.callback_resources,
));
}
}
{
crate::profile_scope!("finish prepare callbacks");
for callback in &callbacks {
user_cmd_bufs.extend(callback.finish_prepare(
device,
queue,
encoder,
&mut self.callback_resources,
));
}
}
user_cmd_bufs
}
}
fn create_sampler(
options: epaint::textures::TextureOptions,
device: &wgpu::Device,
) -> wgpu::Sampler {
let mag_filter = match options.magnification {
epaint::textures::TextureFilter::Nearest => wgpu::FilterMode::Nearest,
epaint::textures::TextureFilter::Linear => wgpu::FilterMode::Linear,
};
let min_filter = match options.minification {
epaint::textures::TextureFilter::Nearest => wgpu::FilterMode::Nearest,
epaint::textures::TextureFilter::Linear => wgpu::FilterMode::Linear,
};
let address_mode = match options.wrap_mode {
epaint::textures::TextureWrapMode::ClampToEdge => wgpu::AddressMode::ClampToEdge,
epaint::textures::TextureWrapMode::Repeat => wgpu::AddressMode::Repeat,
epaint::textures::TextureWrapMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat,
};
device.create_sampler(&wgpu::SamplerDescriptor {
label: Some(&format!(
"egui sampler (mag: {mag_filter:?}, min {min_filter:?})"
)),
mag_filter,
min_filter,
address_mode_u: address_mode,
address_mode_v: address_mode,
..Default::default()
})
}
fn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {
crate::profile_function!();
device.create_buffer(&wgpu::BufferDescriptor {
label: Some("egui_vertex_buffer"),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
size,
mapped_at_creation: false,
})
}
fn create_index_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer {
crate::profile_function!();
device.create_buffer(&wgpu::BufferDescriptor {
label: Some("egui_index_buffer"),
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
size,
mapped_at_creation: false,
})
}
struct ScissorRect {
x: u32,
y: u32,
width: u32,
height: u32,
}
impl ScissorRect {
fn new(clip_rect: &epaint::Rect, pixels_per_point: f32, target_size: [u32; 2]) -> Self {
let clip_min_x = pixels_per_point * clip_rect.min.x;
let clip_min_y = pixels_per_point * clip_rect.min.y;
let clip_max_x = pixels_per_point * clip_rect.max.x;
let clip_max_y = pixels_per_point * clip_rect.max.y;
let clip_min_x = clip_min_x.round() as u32;
let clip_min_y = clip_min_y.round() as u32;
let clip_max_x = clip_max_x.round() as u32;
let clip_max_y = clip_max_y.round() as u32;
let clip_min_x = clip_min_x.clamp(0, target_size[0]);
let clip_min_y = clip_min_y.clamp(0, target_size[1]);
let clip_max_x = clip_max_x.clamp(clip_min_x, target_size[0]);
let clip_max_y = clip_max_y.clamp(clip_min_y, target_size[1]);
Self {
x: clip_min_x,
y: clip_min_y,
width: clip_max_x - clip_min_x,
height: clip_max_y - clip_min_y,
}
}
}
#[test]
fn renderer_impl_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Renderer>();
}