egui-wgpu: correctly handle viewport rectangle for callbacks
This is important for when a callback shape is inside a ScrollArea.
This commit is contained in:
parent
0571bf67e2
commit
09d636b089
4 changed files with 99 additions and 113 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::{borrow::Cow, collections::HashMap, num::NonZeroU32};
|
use std::{borrow::Cow, collections::HashMap, num::NonZeroU32};
|
||||||
|
|
||||||
use egui::{epaint::Primitive, PaintCallbackInfo};
|
use egui::{epaint::Primitive, NumExt, PaintCallbackInfo};
|
||||||
use type_map::TypeMap;
|
use type_map::TypeMap;
|
||||||
use wgpu;
|
use wgpu;
|
||||||
use wgpu::util::DeviceExt as _;
|
use wgpu::util::DeviceExt as _;
|
||||||
|
@ -361,24 +361,21 @@ impl RenderPass {
|
||||||
needs_reset = false;
|
needs_reset = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let PixelRect {
|
{
|
||||||
x,
|
let rect = ScissorRect::new(clip_rect, pixels_per_point, size_in_pixels);
|
||||||
y,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
} = calculate_pixel_rect(clip_rect, pixels_per_point, size_in_pixels);
|
|
||||||
|
|
||||||
// Skip rendering with zero-sized clip areas.
|
if rect.width == 0 || rect.height == 0 {
|
||||||
if width == 0 || height == 0 {
|
// Skip rendering with zero-sized clip areas.
|
||||||
// If this is a mesh, we need to advance the index and vertex buffer iterators
|
if let Primitive::Mesh(_) = primitive {
|
||||||
if let Primitive::Mesh(_) = primitive {
|
// If this is a mesh, we need to advance the index and vertex buffer iterators
|
||||||
index_buffers.next().unwrap();
|
index_buffers.next().unwrap();
|
||||||
vertex_buffers.next().unwrap();
|
vertex_buffers.next().unwrap();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rpass.set_scissor_rect(x, y, width, height);
|
rpass.set_scissor_rect(rect.x, rect.y, rect.width, rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
match primitive {
|
match primitive {
|
||||||
Primitive::Mesh(mesh) => {
|
Primitive::Mesh(mesh) => {
|
||||||
|
@ -408,34 +405,28 @@ impl RenderPass {
|
||||||
if callback.rect.is_positive() {
|
if callback.rect.is_positive() {
|
||||||
needs_reset = true;
|
needs_reset = true;
|
||||||
|
|
||||||
// Set the viewport rect
|
{
|
||||||
let PixelRect {
|
// Set the viewport rect
|
||||||
x,
|
// Transform callback rect to physical pixels:
|
||||||
y,
|
let rect_min_x = pixels_per_point * callback.rect.min.x;
|
||||||
width,
|
let rect_min_y = pixels_per_point * callback.rect.min.y;
|
||||||
height,
|
let rect_max_x = pixels_per_point * callback.rect.max.x;
|
||||||
} = calculate_pixel_rect(&callback.rect, pixels_per_point, size_in_pixels);
|
let rect_max_y = pixels_per_point * callback.rect.max.y;
|
||||||
rpass.set_viewport(
|
|
||||||
x as f32,
|
|
||||||
y as f32,
|
|
||||||
width as f32,
|
|
||||||
height as f32,
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set the scissor rect
|
let rect_min_x = rect_min_x.round();
|
||||||
let PixelRect {
|
let rect_min_y = rect_min_y.round();
|
||||||
x,
|
let rect_max_x = rect_max_x.round();
|
||||||
y,
|
let rect_max_y = rect_max_y.round();
|
||||||
width,
|
|
||||||
height,
|
rpass.set_viewport(
|
||||||
} = calculate_pixel_rect(clip_rect, pixels_per_point, size_in_pixels);
|
rect_min_x,
|
||||||
// Skip rendering with zero-sized clip areas.
|
rect_min_y,
|
||||||
if width == 0 || height == 0 {
|
rect_max_x - rect_min_x,
|
||||||
continue;
|
rect_max_y - rect_min_y,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
rpass.set_scissor_rect(x, y, width, height);
|
|
||||||
|
|
||||||
(cbfn.paint)(
|
(cbfn.paint)(
|
||||||
PaintCallbackInfo {
|
PaintCallbackInfo {
|
||||||
|
@ -776,50 +767,42 @@ impl RenderPass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Rect in physical pixel space, used for setting viewport and cliipping rectangles.
|
/// A Rect in physical pixel space, used for setting cliipping rectangles.
|
||||||
struct PixelRect {
|
struct ScissorRect {
|
||||||
x: u32,
|
x: u32,
|
||||||
y: u32,
|
y: u32,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the Egui clip rect to a physical pixel rect we can use for the GPU viewport/scissor
|
impl ScissorRect {
|
||||||
fn calculate_pixel_rect(
|
fn new(clip_rect: &egui::Rect, pixels_per_point: f32, target_size: [u32; 2]) -> Self {
|
||||||
clip_rect: &egui::Rect,
|
// Transform clip rect to physical pixels:
|
||||||
pixels_per_point: f32,
|
let clip_min_x = pixels_per_point * clip_rect.min.x;
|
||||||
target_size: [u32; 2],
|
let clip_min_y = pixels_per_point * clip_rect.min.y;
|
||||||
) -> PixelRect {
|
let clip_max_x = pixels_per_point * clip_rect.max.x;
|
||||||
// Transform clip rect to physical pixels.
|
let clip_max_y = pixels_per_point * clip_rect.max.y;
|
||||||
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;
|
|
||||||
|
|
||||||
// Make sure clip rect can fit within an `u32`.
|
// Round to integer:
|
||||||
let clip_min_x = clip_min_x.clamp(0.0, target_size[0] as f32);
|
let clip_min_x = clip_min_x.round() as u32;
|
||||||
let clip_min_y = clip_min_y.clamp(0.0, target_size[1] as f32);
|
let clip_min_y = clip_min_y.round() as u32;
|
||||||
let clip_max_x = clip_max_x.clamp(clip_min_x, target_size[0] as f32);
|
let clip_max_x = clip_max_x.round() as u32;
|
||||||
let clip_max_y = clip_max_y.clamp(clip_min_y, target_size[1] as f32);
|
let clip_max_y = clip_max_y.round() as u32;
|
||||||
|
|
||||||
let clip_min_x = clip_min_x.round() as u32;
|
// Clamp:
|
||||||
let clip_min_y = clip_min_y.round() as u32;
|
let clip_min_x = clip_min_x.clamp(0, target_size[0]);
|
||||||
let clip_max_x = clip_max_x.round() as u32;
|
let clip_min_y = clip_min_y.clamp(0, target_size[1]);
|
||||||
let clip_max_y = clip_max_y.round() as u32;
|
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]);
|
||||||
|
|
||||||
let width = (clip_max_x - clip_min_x).max(1);
|
let width = (clip_max_x - clip_min_x).at_least(1);
|
||||||
let height = (clip_max_y - clip_min_y).max(1);
|
let height = (clip_max_y - clip_min_y).at_least(1);
|
||||||
|
|
||||||
// Clip scissor rectangle to target size.
|
ScissorRect {
|
||||||
let x = clip_min_x.min(target_size[0]);
|
x: clip_min_x,
|
||||||
let y = clip_min_y.min(target_size[1]);
|
y: clip_min_y,
|
||||||
let width = width.min(target_size[0] - x);
|
width,
|
||||||
let height = height.min(target_size[1] - y);
|
height,
|
||||||
|
}
|
||||||
PixelRect {
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,21 +24,23 @@ impl Custom3d {
|
||||||
impl eframe::App for Custom3d {
|
impl eframe::App for Custom3d {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
egui::ScrollArea::both()
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
.auto_shrink([false; 2])
|
||||||
ui.label("The triangle is being painted using ");
|
.show(ui, |ui| {
|
||||||
ui.hyperlink_to("glow", "https://github.com/grovesNL/glow");
|
ui.horizontal(|ui| {
|
||||||
ui.label(" (OpenGL).");
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
});
|
ui.label("The triangle is being painted using ");
|
||||||
ui.label(
|
ui.hyperlink_to("glow", "https://github.com/grovesNL/glow");
|
||||||
"It's not a very impressive demo, but it shows you can embed 3D inside of egui.",
|
ui.label(" (OpenGL).");
|
||||||
);
|
});
|
||||||
|
ui.label("It's not a very impressive demo, but it shows you can embed 3D inside of egui.");
|
||||||
|
|
||||||
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
||||||
self.custom_painting(ui);
|
self.custom_painting(ui);
|
||||||
});
|
});
|
||||||
ui.label("Drag to rotate!");
|
ui.label("Drag to rotate!");
|
||||||
ui.add(egui_demo_lib::egui_github_link_file!());
|
ui.add(egui_demo_lib::egui_github_link_file!());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,21 +98,23 @@ impl Custom3d {
|
||||||
impl eframe::App for Custom3d {
|
impl eframe::App for Custom3d {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
egui::ScrollArea::both()
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
.auto_shrink([false; 2])
|
||||||
ui.label("The triangle is being painted using ");
|
.show(ui, |ui| {
|
||||||
ui.hyperlink_to("WGPU", "https://wgpu.rs");
|
ui.horizontal(|ui| {
|
||||||
ui.label(" (Portable Rust graphics API awesomeness)");
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
});
|
ui.label("The triangle is being painted using ");
|
||||||
ui.label(
|
ui.hyperlink_to("WGPU", "https://wgpu.rs");
|
||||||
"It's not a very impressive demo, but it shows you can embed 3D inside of egui.",
|
ui.label(" (Portable Rust graphics API awesomeness)");
|
||||||
);
|
});
|
||||||
|
ui.label("It's not a very impressive demo, but it shows you can embed 3D inside of egui.");
|
||||||
|
|
||||||
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
||||||
self.custom_painting(ui);
|
self.custom_painting(ui);
|
||||||
});
|
});
|
||||||
ui.label("Drag to rotate!");
|
ui.label("Drag to rotate!");
|
||||||
ui.add(egui_demo_lib::egui_github_link_file!());
|
ui.add(egui_demo_lib::egui_github_link_file!());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,12 +140,10 @@ impl Custom3d {
|
||||||
let cb = egui_wgpu::CallbackFn::new()
|
let cb = egui_wgpu::CallbackFn::new()
|
||||||
.prepare(move |device, queue, paint_callback_resources| {
|
.prepare(move |device, queue, paint_callback_resources| {
|
||||||
let resources: &TriangleRenderResources = paint_callback_resources.get().unwrap();
|
let resources: &TriangleRenderResources = paint_callback_resources.get().unwrap();
|
||||||
|
|
||||||
resources.prepare(device, queue, angle);
|
resources.prepare(device, queue, angle);
|
||||||
})
|
})
|
||||||
.paint(move |_info, rpass, paint_callback_resources| {
|
.paint(move |_info, rpass, paint_callback_resources| {
|
||||||
let resources: &TriangleRenderResources = paint_callback_resources.get().unwrap();
|
let resources: &TriangleRenderResources = paint_callback_resources.get().unwrap();
|
||||||
|
|
||||||
resources.paint(rpass);
|
resources.paint(rpass);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -724,17 +724,18 @@ fn set_clip_rect(
|
||||||
let clip_max_x = pixels_per_point * clip_rect.max.x;
|
let clip_max_x = pixels_per_point * clip_rect.max.x;
|
||||||
let clip_max_y = pixels_per_point * clip_rect.max.y;
|
let clip_max_y = pixels_per_point * clip_rect.max.y;
|
||||||
|
|
||||||
// Make sure clip rect can fit within a `u32`:
|
// Round to integer:
|
||||||
let clip_min_x = clip_min_x.clamp(0.0, size_in_pixels.0 as f32);
|
|
||||||
let clip_min_y = clip_min_y.clamp(0.0, size_in_pixels.1 as f32);
|
|
||||||
let clip_max_x = clip_max_x.clamp(clip_min_x, size_in_pixels.0 as f32);
|
|
||||||
let clip_max_y = clip_max_y.clamp(clip_min_y, size_in_pixels.1 as f32);
|
|
||||||
|
|
||||||
let clip_min_x = clip_min_x.round() as i32;
|
let clip_min_x = clip_min_x.round() as i32;
|
||||||
let clip_min_y = clip_min_y.round() as i32;
|
let clip_min_y = clip_min_y.round() as i32;
|
||||||
let clip_max_x = clip_max_x.round() as i32;
|
let clip_max_x = clip_max_x.round() as i32;
|
||||||
let clip_max_y = clip_max_y.round() as i32;
|
let clip_max_y = clip_max_y.round() as i32;
|
||||||
|
|
||||||
|
// Clamp:
|
||||||
|
let clip_min_x = clip_min_x.clamp(0, size_in_pixels.0 as i32);
|
||||||
|
let clip_min_y = clip_min_y.clamp(0, size_in_pixels.1 as i32);
|
||||||
|
let clip_max_x = clip_max_x.clamp(clip_min_x, size_in_pixels.0 as i32);
|
||||||
|
let clip_max_y = clip_max_y.clamp(clip_min_y, size_in_pixels.1 as i32);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
gl.scissor(
|
gl.scissor(
|
||||||
clip_min_x,
|
clip_min_x,
|
||||||
|
|
Loading…
Reference in a new issue