Split local state from Engine in iced_wgpu
This commit is contained in:
parent
4b075b9731
commit
576dd22733
22 changed files with 768 additions and 641 deletions
|
|
@ -80,16 +80,15 @@ fn benchmark<'a>(
|
|||
|
||||
let format = wgpu::TextureFormat::Bgra8UnormSrgb;
|
||||
|
||||
let mut engine = iced_wgpu::Engine::new(
|
||||
let engine = iced_wgpu::Engine::new(
|
||||
adapter,
|
||||
device,
|
||||
queue,
|
||||
device.clone(),
|
||||
queue.clone(),
|
||||
format,
|
||||
Some(Antialiasing::MSAAx4),
|
||||
);
|
||||
|
||||
let mut renderer =
|
||||
Renderer::new(device, &engine, Font::DEFAULT, Pixels::from(16));
|
||||
let mut renderer = Renderer::new(engine, Font::DEFAULT, Pixels::from(16));
|
||||
|
||||
let viewport =
|
||||
graphics::Viewport::with_physical_size(Size::new(3840, 2160), 2.0);
|
||||
|
|
@ -134,16 +133,7 @@ fn benchmark<'a>(
|
|||
|
||||
cache = Some(user_interface.into_cache());
|
||||
|
||||
let mut encoder =
|
||||
device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: None,
|
||||
});
|
||||
|
||||
renderer.present::<&str>(
|
||||
&mut engine,
|
||||
device,
|
||||
queue,
|
||||
&mut encoder,
|
||||
let submission = renderer.present::<&str>(
|
||||
Some(Color::BLACK),
|
||||
format,
|
||||
&texture_view,
|
||||
|
|
@ -151,7 +141,6 @@ fn benchmark<'a>(
|
|||
&[],
|
||||
);
|
||||
|
||||
let submission = engine.submit(queue, encoder);
|
||||
let _ = device.poll(wgpu::Maintain::WaitForSubmissionIndex(submission));
|
||||
|
||||
i += 1;
|
||||
|
|
|
|||
|
|
@ -106,7 +106,12 @@ impl Default for Style {
|
|||
/// a window nor a compositor.
|
||||
pub trait Headless {
|
||||
/// Creates a new [`Headless`] renderer;
|
||||
fn new(default_font: Font, default_text_size: Pixels) -> Self;
|
||||
fn new(
|
||||
default_font: Font,
|
||||
default_text_size: Pixels,
|
||||
) -> impl Future<Output = Option<Self>>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Draws offscreen into a screenshot, returning a collection of
|
||||
/// bytes representing the rendered pixels in RGBA order.
|
||||
|
|
|
|||
|
|
@ -35,11 +35,9 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
|
|||
Loading,
|
||||
Ready {
|
||||
window: Arc<winit::window::Window>,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
surface: wgpu::Surface<'static>,
|
||||
format: wgpu::TextureFormat,
|
||||
engine: Engine,
|
||||
device: wgpu::Device,
|
||||
renderer: Renderer,
|
||||
scene: Scene,
|
||||
state: program::State<Controls>,
|
||||
|
|
@ -146,13 +144,9 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
|
|||
// Initialize iced
|
||||
let mut debug = Debug::new();
|
||||
let engine =
|
||||
Engine::new(&adapter, &device, &queue, format, None);
|
||||
let mut renderer = Renderer::new(
|
||||
&device,
|
||||
&engine,
|
||||
Font::default(),
|
||||
Pixels::from(16),
|
||||
);
|
||||
Engine::new(&adapter, device.clone(), queue, format, None);
|
||||
let mut renderer =
|
||||
Renderer::new(engine, Font::default(), Pixels::from(16));
|
||||
|
||||
let state = program::State::new(
|
||||
controls,
|
||||
|
|
@ -166,11 +160,9 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
|
|||
|
||||
*self = Self::Ready {
|
||||
window,
|
||||
device,
|
||||
queue,
|
||||
surface,
|
||||
format,
|
||||
engine,
|
||||
device,
|
||||
renderer,
|
||||
scene,
|
||||
state,
|
||||
|
|
@ -193,10 +185,8 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
|
|||
let Self::Ready {
|
||||
window,
|
||||
device,
|
||||
queue,
|
||||
surface,
|
||||
format,
|
||||
engine,
|
||||
renderer,
|
||||
scene,
|
||||
state,
|
||||
|
|
@ -264,10 +254,6 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
|
|||
|
||||
// And then iced on top
|
||||
renderer.present(
|
||||
engine,
|
||||
device,
|
||||
queue,
|
||||
&mut encoder,
|
||||
None,
|
||||
frame.texture.format(),
|
||||
&view,
|
||||
|
|
@ -276,7 +262,6 @@ pub fn main() -> Result<(), winit::error::EventLoopError> {
|
|||
);
|
||||
|
||||
// Then we submit the work
|
||||
engine.submit(queue, encoder);
|
||||
frame.present();
|
||||
|
||||
// Update the mouse cursor
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0e355b080ad33905145e9f70a3b29e2481197c8fc8f42491acd5358238ebbd5f
|
||||
804a1bb6d49e3b3158463202960447d9e7820b967280f41dd0c34c00d3edf2c3
|
||||
|
|
@ -87,12 +87,11 @@ pub trait Compositor: Sized {
|
|||
/// the texture ordered as `RGBA` in the `sRGB` color space.
|
||||
///
|
||||
/// [`Renderer`]: Self::Renderer
|
||||
fn screenshot<T: AsRef<str>>(
|
||||
fn screenshot(
|
||||
&mut self,
|
||||
renderer: &mut Self::Renderer,
|
||||
viewport: &Viewport,
|
||||
background_color: Color,
|
||||
overlay: &[T],
|
||||
) -> Vec<u8>;
|
||||
}
|
||||
|
||||
|
|
@ -199,12 +198,11 @@ impl Compositor for () {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn screenshot<T: AsRef<str>>(
|
||||
fn screenshot(
|
||||
&mut self,
|
||||
_renderer: &mut Self::Renderer,
|
||||
_viewport: &Viewport,
|
||||
_background_color: Color,
|
||||
_overlay: &[T],
|
||||
) -> Vec<u8> {
|
||||
vec![]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -3,7 +3,8 @@ use crate::core::image;
|
|||
use crate::core::renderer;
|
||||
use crate::core::svg;
|
||||
use crate::core::{
|
||||
self, Background, Color, Image, Point, Rectangle, Size, Svg, Transformation,
|
||||
self, Background, Color, Font, Image, Pixels, Point, Rectangle, Size, Svg,
|
||||
Transformation,
|
||||
};
|
||||
use crate::graphics;
|
||||
use crate::graphics::compositor;
|
||||
|
|
@ -353,29 +354,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn screenshot<T: AsRef<str>>(
|
||||
fn screenshot(
|
||||
&mut self,
|
||||
renderer: &mut Self::Renderer,
|
||||
viewport: &graphics::Viewport,
|
||||
background_color: Color,
|
||||
overlay: &[T],
|
||||
) -> Vec<u8> {
|
||||
match (self, renderer) {
|
||||
(Self::Primary(compositor), Renderer::Primary(renderer)) => {
|
||||
compositor.screenshot(
|
||||
renderer,
|
||||
viewport,
|
||||
background_color,
|
||||
overlay,
|
||||
)
|
||||
compositor.screenshot(renderer, viewport, background_color)
|
||||
}
|
||||
(Self::Secondary(compositor), Renderer::Secondary(renderer)) => {
|
||||
compositor.screenshot(
|
||||
renderer,
|
||||
viewport,
|
||||
background_color,
|
||||
overlay,
|
||||
)
|
||||
compositor.screenshot(renderer, viewport, background_color)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
|
@ -617,6 +607,41 @@ mod geometry {
|
|||
}
|
||||
}
|
||||
|
||||
impl<A, B> renderer::Headless for Renderer<A, B>
|
||||
where
|
||||
A: renderer::Headless,
|
||||
B: renderer::Headless,
|
||||
{
|
||||
async fn new(
|
||||
default_font: Font,
|
||||
default_text_size: Pixels,
|
||||
) -> Option<Self> {
|
||||
if let Some(renderer) = A::new(default_font, default_text_size).await {
|
||||
return Some(Self::Primary(renderer));
|
||||
}
|
||||
|
||||
B::new(default_font, default_text_size)
|
||||
.await
|
||||
.map(Self::Secondary)
|
||||
}
|
||||
|
||||
fn screenshot(
|
||||
&mut self,
|
||||
size: Size<u32>,
|
||||
scale_factor: f32,
|
||||
background_color: Color,
|
||||
) -> Vec<u8> {
|
||||
match self {
|
||||
crate::fallback::Renderer::Primary(renderer) => {
|
||||
renderer.screenshot(size, scale_factor, background_color)
|
||||
}
|
||||
crate::fallback::Renderer::Secondary(renderer) => {
|
||||
renderer.screenshot(size, scale_factor, background_color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> compositor::Default for Renderer<A, B>
|
||||
where
|
||||
A: compositor::Default,
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ pub type Compositor = renderer::Compositor;
|
|||
|
||||
#[cfg(all(feature = "wgpu", feature = "tiny-skia"))]
|
||||
mod renderer {
|
||||
use crate::core::renderer;
|
||||
use crate::core::{Color, Font, Pixels, Size};
|
||||
|
||||
pub type Renderer = crate::fallback::Renderer<
|
||||
iced_wgpu::Renderer,
|
||||
iced_tiny_skia::Renderer,
|
||||
|
|
@ -35,31 +32,6 @@ mod renderer {
|
|||
iced_wgpu::window::Compositor,
|
||||
iced_tiny_skia::window::Compositor,
|
||||
>;
|
||||
|
||||
impl renderer::Headless for Renderer {
|
||||
fn new(default_font: Font, default_text_size: Pixels) -> Self {
|
||||
Self::Secondary(iced_tiny_skia::Renderer::new(
|
||||
default_font,
|
||||
default_text_size,
|
||||
))
|
||||
}
|
||||
|
||||
fn screenshot(
|
||||
&mut self,
|
||||
size: Size<u32>,
|
||||
scale_factor: f32,
|
||||
background_color: Color,
|
||||
) -> Vec<u8> {
|
||||
match self {
|
||||
crate::fallback::Renderer::Primary(_) => unreachable!(
|
||||
"iced_wgpu does not support headless mode yet!"
|
||||
),
|
||||
crate::fallback::Renderer::Secondary(renderer) => {
|
||||
renderer.screenshot(size, scale_factor, background_color)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "wgpu", not(feature = "tiny-skia")))]
|
||||
|
|
|
|||
|
|
@ -186,8 +186,10 @@ where
|
|||
load_font(font).expect("Font must be valid");
|
||||
}
|
||||
|
||||
let mut renderer =
|
||||
Renderer::new(default_font, settings.default_text_size);
|
||||
let mut renderer = iced_runtime::futures::futures::executor::block_on(
|
||||
Renderer::new(default_font, settings.default_text_size),
|
||||
)
|
||||
.expect("Create new headless renderer");
|
||||
|
||||
let raw = UserInterface::build(
|
||||
element,
|
||||
|
|
|
|||
|
|
@ -407,8 +407,11 @@ impl compositor::Default for Renderer {
|
|||
}
|
||||
|
||||
impl renderer::Headless for Renderer {
|
||||
fn new(default_font: Font, default_text_size: Pixels) -> Self {
|
||||
Self::new(default_font, default_text_size)
|
||||
async fn new(
|
||||
default_font: Font,
|
||||
default_text_size: Pixels,
|
||||
) -> Option<Self> {
|
||||
Some(Self::new(default_font, default_text_size))
|
||||
}
|
||||
|
||||
fn screenshot(
|
||||
|
|
@ -420,11 +423,6 @@ impl renderer::Headless for Renderer {
|
|||
let viewport =
|
||||
Viewport::with_physical_size(size, f64::from(scale_factor));
|
||||
|
||||
window::compositor::screenshot::<&str>(
|
||||
self,
|
||||
&viewport,
|
||||
background_color,
|
||||
&[],
|
||||
)
|
||||
window::compositor::screenshot(self, &viewport, background_color)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,14 +126,13 @@ impl crate::graphics::Compositor for Compositor {
|
|||
)
|
||||
}
|
||||
|
||||
fn screenshot<T: AsRef<str>>(
|
||||
fn screenshot(
|
||||
&mut self,
|
||||
renderer: &mut Self::Renderer,
|
||||
viewport: &Viewport,
|
||||
background_color: Color,
|
||||
overlay: &[T],
|
||||
) -> Vec<u8> {
|
||||
screenshot(renderer, viewport, background_color, overlay)
|
||||
screenshot(renderer, viewport, background_color)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -219,11 +218,10 @@ pub fn present<T: AsRef<str>>(
|
|||
buffer.present().map_err(|_| compositor::SurfaceError::Lost)
|
||||
}
|
||||
|
||||
pub fn screenshot<T: AsRef<str>>(
|
||||
pub fn screenshot(
|
||||
renderer: &mut Renderer,
|
||||
viewport: &Viewport,
|
||||
background_color: Color,
|
||||
overlay: &[T],
|
||||
) -> Vec<u8> {
|
||||
let size = viewport.physical_size();
|
||||
|
||||
|
|
@ -233,7 +231,7 @@ pub fn screenshot<T: AsRef<str>>(
|
|||
let mut clip_mask = tiny_skia::Mask::new(size.width, size.height)
|
||||
.expect("Create clip mask");
|
||||
|
||||
renderer.draw(
|
||||
renderer.draw::<&str>(
|
||||
&mut tiny_skia::PixmapMut::from_bytes(
|
||||
bytemuck::cast_slice_mut(&mut offscreen_buffer),
|
||||
size.width,
|
||||
|
|
@ -247,7 +245,7 @@ pub fn screenshot<T: AsRef<str>>(
|
|||
size.height as f32,
|
||||
))],
|
||||
background_color,
|
||||
overlay,
|
||||
&[],
|
||||
);
|
||||
|
||||
offscreen_buffer.iter().fold(
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
use crate::buffer;
|
||||
use crate::graphics::Antialiasing;
|
||||
use crate::primitive;
|
||||
use crate::quad;
|
||||
use crate::text;
|
||||
use crate::triangle;
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Engine {
|
||||
pub(crate) staging_belt: wgpu::util::StagingBelt,
|
||||
pub(crate) device: wgpu::Device,
|
||||
pub(crate) queue: wgpu::Queue,
|
||||
pub(crate) format: wgpu::TextureFormat,
|
||||
|
||||
pub(crate) quad_pipeline: quad::Pipeline,
|
||||
|
|
@ -15,46 +18,41 @@ pub struct Engine {
|
|||
pub(crate) triangle_pipeline: triangle::Pipeline,
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
pub(crate) image_pipeline: crate::image::Pipeline,
|
||||
pub(crate) primitive_storage: primitive::Storage,
|
||||
pub(crate) primitive_storage: Arc<RwLock<primitive::Storage>>,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new(
|
||||
_adapter: &wgpu::Adapter,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
format: wgpu::TextureFormat,
|
||||
antialiasing: Option<Antialiasing>, // TODO: Initialize AA pipelines lazily
|
||||
) -> Self {
|
||||
let text_pipeline = text::Pipeline::new(device, queue, format);
|
||||
let quad_pipeline = quad::Pipeline::new(device, format);
|
||||
let triangle_pipeline =
|
||||
triangle::Pipeline::new(device, format, antialiasing);
|
||||
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
let image_pipeline = {
|
||||
let backend = _adapter.get_info().backend;
|
||||
|
||||
crate::image::Pipeline::new(device, format, backend)
|
||||
};
|
||||
|
||||
Self {
|
||||
// TODO: Resize belt smartly (?)
|
||||
// It would be great if the `StagingBelt` API exposed methods
|
||||
// for introspection to detect when a resize may be worth it.
|
||||
staging_belt: wgpu::util::StagingBelt::new(
|
||||
buffer::MAX_WRITE_SIZE as u64,
|
||||
),
|
||||
format,
|
||||
|
||||
quad_pipeline,
|
||||
text_pipeline,
|
||||
triangle_pipeline,
|
||||
quad_pipeline: quad::Pipeline::new(&device, format),
|
||||
text_pipeline: text::Pipeline::new(&device, &queue, format),
|
||||
triangle_pipeline: triangle::Pipeline::new(
|
||||
&device,
|
||||
format,
|
||||
antialiasing,
|
||||
),
|
||||
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
image_pipeline,
|
||||
image_pipeline: {
|
||||
let backend = _adapter.get_info().backend;
|
||||
|
||||
primitive_storage: primitive::Storage::default(),
|
||||
crate::image::Pipeline::new(&device, format, backend)
|
||||
},
|
||||
|
||||
primitive_storage: Arc::new(RwLock::new(
|
||||
primitive::Storage::default(),
|
||||
)),
|
||||
|
||||
device,
|
||||
queue,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,23 +63,4 @@ impl Engine {
|
|||
) -> crate::image::Cache {
|
||||
self.image_pipeline.create_cache(device)
|
||||
}
|
||||
|
||||
pub fn submit(
|
||||
&mut self,
|
||||
queue: &wgpu::Queue,
|
||||
encoder: wgpu::CommandEncoder,
|
||||
) -> wgpu::SubmissionIndex {
|
||||
self.staging_belt.finish();
|
||||
let index = queue.submit(Some(encoder.finish()));
|
||||
self.staging_belt.recall();
|
||||
|
||||
self.quad_pipeline.end_frame();
|
||||
self.text_pipeline.end_frame();
|
||||
self.triangle_pipeline.end_frame();
|
||||
|
||||
#[cfg(any(feature = "image", feature = "svg"))]
|
||||
self.image_pipeline.end_frame();
|
||||
|
||||
index
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,16 +21,14 @@ pub use crate::graphics::Image;
|
|||
|
||||
pub type Batch = Vec<Image>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pipeline {
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
raw: wgpu::RenderPipeline,
|
||||
backend: wgpu::Backend,
|
||||
nearest_sampler: wgpu::Sampler,
|
||||
linear_sampler: wgpu::Sampler,
|
||||
texture_layout: Arc<wgpu::BindGroupLayout>,
|
||||
constant_layout: wgpu::BindGroupLayout,
|
||||
layers: Vec<Layer>,
|
||||
prepare_layer: usize,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
|
|
@ -194,26 +192,37 @@ impl Pipeline {
|
|||
});
|
||||
|
||||
Pipeline {
|
||||
pipeline,
|
||||
raw: pipeline,
|
||||
backend,
|
||||
nearest_sampler,
|
||||
linear_sampler,
|
||||
texture_layout: Arc::new(texture_layout),
|
||||
constant_layout,
|
||||
layers: Vec::new(),
|
||||
prepare_layer: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_cache(&self, device: &wgpu::Device) -> Cache {
|
||||
Cache::new(device, self.backend, self.texture_layout.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
layers: Vec<Layer>,
|
||||
prepare_layer: usize,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn prepare(
|
||||
&mut self,
|
||||
pipeline: &Pipeline,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
cache: &mut Cache,
|
||||
images: &Batch,
|
||||
transformation: Transformation,
|
||||
|
|
@ -285,9 +294,9 @@ impl Pipeline {
|
|||
if self.layers.len() <= self.prepare_layer {
|
||||
self.layers.push(Layer::new(
|
||||
device,
|
||||
&self.constant_layout,
|
||||
&self.nearest_sampler,
|
||||
&self.linear_sampler,
|
||||
&pipeline.constant_layout,
|
||||
&pipeline.nearest_sampler,
|
||||
&pipeline.linear_sampler,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -308,13 +317,14 @@ impl Pipeline {
|
|||
|
||||
pub fn render<'a>(
|
||||
&'a self,
|
||||
pipeline: &'a Pipeline,
|
||||
cache: &'a Cache,
|
||||
layer: usize,
|
||||
bounds: Rectangle<u32>,
|
||||
render_pass: &mut wgpu::RenderPass<'a>,
|
||||
) {
|
||||
if let Some(layer) = self.layers.get(layer) {
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_pipeline(&pipeline.raw);
|
||||
|
||||
render_pass.set_scissor_rect(
|
||||
bounds.x,
|
||||
|
|
|
|||
343
wgpu/src/lib.rs
343
wgpu/src/lib.rs
|
|
@ -60,6 +60,7 @@ pub use settings::Settings;
|
|||
#[cfg(feature = "geometry")]
|
||||
pub use geometry::Geometry;
|
||||
|
||||
use crate::core::renderer;
|
||||
use crate::core::{
|
||||
Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
|
||||
Vector,
|
||||
|
|
@ -73,23 +74,30 @@ use crate::graphics::text::{Editor, Paragraph};
|
|||
/// [`iced`]: https://github.com/iced-rs/iced
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Renderer {
|
||||
engine: Engine,
|
||||
|
||||
default_font: Font,
|
||||
default_text_size: Pixels,
|
||||
layers: layer::Stack,
|
||||
|
||||
triangle_storage: triangle::Storage,
|
||||
text_storage: text::Storage,
|
||||
quad: quad::State,
|
||||
triangle: triangle::State,
|
||||
text: text::State,
|
||||
text_viewport: text::Viewport,
|
||||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
image: image::State,
|
||||
|
||||
// TODO: Centralize all the image feature handling
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
image_cache: std::cell::RefCell<image::Cache>,
|
||||
|
||||
staging_belt: wgpu::util::StagingBelt,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
engine: &Engine,
|
||||
engine: Engine,
|
||||
default_font: Font,
|
||||
default_text_size: Pixels,
|
||||
) -> Self {
|
||||
|
|
@ -98,52 +106,206 @@ impl Renderer {
|
|||
default_text_size,
|
||||
layers: layer::Stack::new(),
|
||||
|
||||
triangle_storage: triangle::Storage::new(),
|
||||
text_storage: text::Storage::new(),
|
||||
text_viewport: engine.text_pipeline.create_viewport(device),
|
||||
quad: quad::State::new(),
|
||||
triangle: triangle::State::new(
|
||||
&engine.device,
|
||||
&engine.triangle_pipeline,
|
||||
),
|
||||
text: text::State::new(),
|
||||
text_viewport: engine.text_pipeline.create_viewport(&engine.device),
|
||||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
image: image::State::new(),
|
||||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
image_cache: std::cell::RefCell::new(
|
||||
engine.create_image_cache(device),
|
||||
engine.create_image_cache(&engine.device),
|
||||
),
|
||||
|
||||
// TODO: Resize belt smartly (?)
|
||||
// It would be great if the `StagingBelt` API exposed methods
|
||||
// for introspection to detect when a resize may be worth it.
|
||||
staging_belt: wgpu::util::StagingBelt::new(
|
||||
buffer::MAX_WRITE_SIZE as u64,
|
||||
),
|
||||
|
||||
engine,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
clear_color: Option<Color>,
|
||||
target: &wgpu::TextureView,
|
||||
viewport: &Viewport,
|
||||
) -> wgpu::CommandEncoder {
|
||||
let mut encoder = self.engine.device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("iced_wgpu encoder"),
|
||||
},
|
||||
);
|
||||
|
||||
self.prepare(&mut encoder, viewport);
|
||||
self.render(&mut encoder, target, clear_color, viewport);
|
||||
|
||||
self.quad.end_frame();
|
||||
self.triangle.end_frame();
|
||||
self.text.end_frame();
|
||||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
{
|
||||
self.image.end_frame();
|
||||
self.image_cache.borrow_mut().trim();
|
||||
}
|
||||
|
||||
encoder
|
||||
}
|
||||
|
||||
pub fn present<T: AsRef<str>>(
|
||||
&mut self,
|
||||
engine: &mut Engine,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
clear_color: Option<Color>,
|
||||
format: wgpu::TextureFormat,
|
||||
_format: wgpu::TextureFormat,
|
||||
frame: &wgpu::TextureView,
|
||||
viewport: &Viewport,
|
||||
overlay: &[T],
|
||||
) {
|
||||
) -> wgpu::SubmissionIndex {
|
||||
self.draw_overlay(overlay, viewport);
|
||||
self.prepare(engine, device, queue, format, encoder, viewport);
|
||||
self.render(engine, encoder, frame, clear_color, viewport);
|
||||
|
||||
self.triangle_storage.trim();
|
||||
self.text_storage.trim();
|
||||
let encoder = self.draw(clear_color, frame, viewport);
|
||||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
self.image_cache.borrow_mut().trim();
|
||||
self.staging_belt.finish();
|
||||
let submission = self.engine.queue.submit([encoder.finish()]);
|
||||
self.staging_belt.recall();
|
||||
submission
|
||||
}
|
||||
|
||||
/// Renders the current surface to an offscreen buffer.
|
||||
///
|
||||
/// Returns RGBA bytes of the texture data.
|
||||
pub fn screenshot(
|
||||
&mut self,
|
||||
viewport: &Viewport,
|
||||
background_color: Color,
|
||||
) -> Vec<u8> {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct BufferDimensions {
|
||||
width: u32,
|
||||
height: u32,
|
||||
unpadded_bytes_per_row: usize,
|
||||
padded_bytes_per_row: usize,
|
||||
}
|
||||
|
||||
impl BufferDimensions {
|
||||
fn new(size: Size<u32>) -> Self {
|
||||
let unpadded_bytes_per_row = size.width as usize * 4; //slice of buffer per row; always RGBA
|
||||
let alignment = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize; //256
|
||||
let padded_bytes_per_row_padding = (alignment
|
||||
- unpadded_bytes_per_row % alignment)
|
||||
% alignment;
|
||||
let padded_bytes_per_row =
|
||||
unpadded_bytes_per_row + padded_bytes_per_row_padding;
|
||||
|
||||
Self {
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
unpadded_bytes_per_row,
|
||||
padded_bytes_per_row,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dimensions = BufferDimensions::new(viewport.physical_size());
|
||||
|
||||
let texture_extent = wgpu::Extent3d {
|
||||
width: dimensions.width,
|
||||
height: dimensions.height,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
|
||||
let texture =
|
||||
self.engine.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("iced_wgpu.offscreen.source_texture"),
|
||||
size: texture_extent,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: self.engine.format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||
| wgpu::TextureUsages::COPY_SRC
|
||||
| wgpu::TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
});
|
||||
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
let mut encoder = self.draw(Some(background_color), &view, &viewport);
|
||||
|
||||
let texture = crate::color::convert(
|
||||
&self.engine.device,
|
||||
&mut encoder,
|
||||
texture,
|
||||
if graphics::color::GAMMA_CORRECTION {
|
||||
wgpu::TextureFormat::Rgba8UnormSrgb
|
||||
} else {
|
||||
wgpu::TextureFormat::Rgba8Unorm
|
||||
},
|
||||
);
|
||||
|
||||
let output_buffer =
|
||||
self.engine.device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("iced_wgpu.offscreen.output_texture_buffer"),
|
||||
size: (dimensions.padded_bytes_per_row
|
||||
* dimensions.height as usize) as u64,
|
||||
usage: wgpu::BufferUsages::MAP_READ
|
||||
| wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
encoder.copy_texture_to_buffer(
|
||||
texture.as_image_copy(),
|
||||
wgpu::TexelCopyBufferInfo {
|
||||
buffer: &output_buffer,
|
||||
layout: wgpu::TexelCopyBufferLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(dimensions.padded_bytes_per_row as u32),
|
||||
rows_per_image: None,
|
||||
},
|
||||
},
|
||||
texture_extent,
|
||||
);
|
||||
|
||||
self.staging_belt.finish();
|
||||
let index = self.engine.queue.submit([encoder.finish()]);
|
||||
self.staging_belt.recall();
|
||||
|
||||
let slice = output_buffer.slice(..);
|
||||
slice.map_async(wgpu::MapMode::Read, |_| {});
|
||||
|
||||
let _ = self
|
||||
.engine
|
||||
.device
|
||||
.poll(wgpu::Maintain::WaitForSubmissionIndex(index));
|
||||
|
||||
let mapped_buffer = slice.get_mapped_range();
|
||||
|
||||
mapped_buffer.chunks(dimensions.padded_bytes_per_row).fold(
|
||||
vec![],
|
||||
|mut acc, row| {
|
||||
acc.extend(&row[..dimensions.unpadded_bytes_per_row]);
|
||||
acc
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn prepare(
|
||||
&mut self,
|
||||
engine: &mut Engine,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
_format: wgpu::TextureFormat,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
viewport: &Viewport,
|
||||
) {
|
||||
let scale_factor = viewport.scale_factor() as f32;
|
||||
|
||||
self.text_viewport.update(queue, viewport.physical_size());
|
||||
self.text_viewport
|
||||
.update(&self.engine.queue, viewport.physical_size());
|
||||
|
||||
let physical_bounds = Rectangle::<f32>::from(Rectangle::with_size(
|
||||
viewport.physical_size(),
|
||||
|
|
@ -159,10 +321,11 @@ impl Renderer {
|
|||
}
|
||||
|
||||
if !layer.quads.is_empty() {
|
||||
engine.quad_pipeline.prepare(
|
||||
device,
|
||||
self.quad.prepare(
|
||||
&self.engine.quad_pipeline,
|
||||
&self.engine.device,
|
||||
&mut self.staging_belt,
|
||||
encoder,
|
||||
&mut engine.staging_belt,
|
||||
&layer.quads,
|
||||
viewport.projection(),
|
||||
scale_factor,
|
||||
|
|
@ -170,11 +333,11 @@ impl Renderer {
|
|||
}
|
||||
|
||||
if !layer.triangles.is_empty() {
|
||||
engine.triangle_pipeline.prepare(
|
||||
device,
|
||||
self.triangle.prepare(
|
||||
&self.engine.triangle_pipeline,
|
||||
&self.engine.device,
|
||||
&mut self.staging_belt,
|
||||
encoder,
|
||||
&mut engine.staging_belt,
|
||||
&mut self.triangle_storage,
|
||||
&layer.triangles,
|
||||
Transformation::scale(scale_factor),
|
||||
viewport.physical_size(),
|
||||
|
|
@ -182,12 +345,18 @@ impl Renderer {
|
|||
}
|
||||
|
||||
if !layer.primitives.is_empty() {
|
||||
let mut primitive_storage = self
|
||||
.engine
|
||||
.primitive_storage
|
||||
.write()
|
||||
.expect("Write primitive storage");
|
||||
|
||||
for instance in &layer.primitives {
|
||||
instance.primitive.prepare(
|
||||
device,
|
||||
queue,
|
||||
engine.format,
|
||||
&mut engine.primitive_storage,
|
||||
&self.engine.device,
|
||||
&self.engine.queue,
|
||||
self.engine.format,
|
||||
&mut primitive_storage,
|
||||
&instance.bounds,
|
||||
viewport,
|
||||
);
|
||||
|
|
@ -196,10 +365,11 @@ impl Renderer {
|
|||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
if !layer.images.is_empty() {
|
||||
engine.image_pipeline.prepare(
|
||||
device,
|
||||
self.image.prepare(
|
||||
&self.engine.image_pipeline,
|
||||
&self.engine.device,
|
||||
&mut self.staging_belt,
|
||||
encoder,
|
||||
&mut engine.staging_belt,
|
||||
&mut self.image_cache.borrow_mut(),
|
||||
&layer.images,
|
||||
viewport.projection(),
|
||||
|
|
@ -208,12 +378,12 @@ impl Renderer {
|
|||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
engine.text_pipeline.prepare(
|
||||
device,
|
||||
queue,
|
||||
self.text.prepare(
|
||||
&self.engine.text_pipeline,
|
||||
&self.engine.device,
|
||||
&self.engine.queue,
|
||||
&self.text_viewport,
|
||||
encoder,
|
||||
&mut self.text_storage,
|
||||
&layer.text,
|
||||
layer.bounds,
|
||||
Transformation::scale(scale_factor),
|
||||
|
|
@ -224,7 +394,6 @@ impl Renderer {
|
|||
|
||||
fn render(
|
||||
&mut self,
|
||||
engine: &mut Engine,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
frame: &wgpu::TextureView,
|
||||
clear_color: Option<Color>,
|
||||
|
|
@ -291,7 +460,8 @@ impl Renderer {
|
|||
};
|
||||
|
||||
if !layer.quads.is_empty() {
|
||||
engine.quad_pipeline.render(
|
||||
self.quad.render(
|
||||
&self.engine.quad_pipeline,
|
||||
quad_layer,
|
||||
scissor_rect,
|
||||
&layer.quads,
|
||||
|
|
@ -304,10 +474,10 @@ impl Renderer {
|
|||
if !layer.triangles.is_empty() {
|
||||
let _ = ManuallyDrop::into_inner(render_pass);
|
||||
|
||||
mesh_layer += engine.triangle_pipeline.render(
|
||||
mesh_layer += self.triangle.render(
|
||||
&self.engine.triangle_pipeline,
|
||||
encoder,
|
||||
frame,
|
||||
&self.triangle_storage,
|
||||
mesh_layer,
|
||||
&layer.triangles,
|
||||
physical_bounds,
|
||||
|
|
@ -337,6 +507,12 @@ impl Renderer {
|
|||
if !layer.primitives.is_empty() {
|
||||
let _ = ManuallyDrop::into_inner(render_pass);
|
||||
|
||||
let primitive_storage = self
|
||||
.engine
|
||||
.primitive_storage
|
||||
.read()
|
||||
.expect("Read primitive storage");
|
||||
|
||||
for instance in &layer.primitives {
|
||||
if let Some(clip_bounds) = (instance.bounds * scale)
|
||||
.intersection(&physical_bounds)
|
||||
|
|
@ -344,7 +520,7 @@ impl Renderer {
|
|||
{
|
||||
instance.primitive.render(
|
||||
encoder,
|
||||
&engine.primitive_storage,
|
||||
&primitive_storage,
|
||||
frame,
|
||||
&clip_bounds,
|
||||
);
|
||||
|
|
@ -373,7 +549,8 @@ impl Renderer {
|
|||
|
||||
#[cfg(any(feature = "svg", feature = "image"))]
|
||||
if !layer.images.is_empty() {
|
||||
engine.image_pipeline.render(
|
||||
self.image.render(
|
||||
&self.engine.image_pipeline,
|
||||
&image_cache,
|
||||
image_layer,
|
||||
scissor_rect,
|
||||
|
|
@ -384,9 +561,9 @@ impl Renderer {
|
|||
}
|
||||
|
||||
if !layer.text.is_empty() {
|
||||
text_layer += engine.text_pipeline.render(
|
||||
text_layer += self.text.render(
|
||||
&self.engine.text_pipeline,
|
||||
&self.text_viewport,
|
||||
&self.text_storage,
|
||||
text_layer,
|
||||
&layer.text,
|
||||
scissor_rect,
|
||||
|
|
@ -630,3 +807,67 @@ impl primitive::Renderer for Renderer {
|
|||
impl graphics::compositor::Default for crate::Renderer {
|
||||
type Compositor = window::Compositor;
|
||||
}
|
||||
|
||||
impl renderer::Headless for Renderer {
|
||||
async fn new(
|
||||
default_font: Font,
|
||||
default_text_size: Pixels,
|
||||
) -> Option<Self> {
|
||||
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
||||
backends: wgpu::Backends::from_env()
|
||||
.unwrap_or(wgpu::Backends::PRIMARY),
|
||||
flags: wgpu::InstanceFlags::empty(),
|
||||
..wgpu::InstanceDescriptor::default()
|
||||
});
|
||||
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||
force_fallback_adapter: false,
|
||||
compatible_surface: None,
|
||||
})
|
||||
.await?;
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: Some("iced_wgpu [headless]"),
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits {
|
||||
max_bind_groups: 2,
|
||||
..wgpu::Limits::default()
|
||||
},
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.ok()?;
|
||||
|
||||
let engine = Engine::new(
|
||||
&adapter,
|
||||
device,
|
||||
queue,
|
||||
if graphics::color::GAMMA_CORRECTION {
|
||||
wgpu::TextureFormat::Rgba8UnormSrgb
|
||||
} else {
|
||||
wgpu::TextureFormat::Rgba8Unorm
|
||||
},
|
||||
Some(graphics::Antialiasing::MSAAx4),
|
||||
);
|
||||
|
||||
Some(Self::new(engine, default_font, default_text_size))
|
||||
}
|
||||
|
||||
fn screenshot(
|
||||
&mut self,
|
||||
size: Size<u32>,
|
||||
scale_factor: f32,
|
||||
background_color: Color,
|
||||
) -> Vec<u8> {
|
||||
self.screenshot(
|
||||
&Viewport::with_physical_size(size, f64::from(scale_factor)),
|
||||
background_color,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
153
wgpu/src/quad.rs
153
wgpu/src/quad.rs
|
|
@ -43,15 +43,96 @@ pub struct Quad {
|
|||
pub shadow_blur_radius: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pipeline {
|
||||
solid: solid::Pipeline,
|
||||
gradient: gradient::Pipeline,
|
||||
constant_layout: wgpu::BindGroupLayout,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
layers: Vec<Layer>,
|
||||
prepare_layer: usize,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn prepare(
|
||||
&mut self,
|
||||
pipeline: &Pipeline,
|
||||
device: &wgpu::Device,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
quads: &Batch,
|
||||
transformation: Transformation,
|
||||
scale: f32,
|
||||
) {
|
||||
if self.layers.len() <= self.prepare_layer {
|
||||
self.layers
|
||||
.push(Layer::new(device, &pipeline.constant_layout));
|
||||
}
|
||||
|
||||
let layer = &mut self.layers[self.prepare_layer];
|
||||
layer.prepare(device, encoder, belt, quads, transformation, scale);
|
||||
|
||||
self.prepare_layer += 1;
|
||||
}
|
||||
|
||||
pub fn render<'a>(
|
||||
&'a self,
|
||||
pipeline: &'a Pipeline,
|
||||
layer: usize,
|
||||
bounds: Rectangle<u32>,
|
||||
quads: &Batch,
|
||||
render_pass: &mut wgpu::RenderPass<'a>,
|
||||
) {
|
||||
if let Some(layer) = self.layers.get(layer) {
|
||||
render_pass.set_scissor_rect(
|
||||
bounds.x,
|
||||
bounds.y,
|
||||
bounds.width,
|
||||
bounds.height,
|
||||
);
|
||||
|
||||
let mut solid_offset = 0;
|
||||
let mut gradient_offset = 0;
|
||||
|
||||
for (kind, count) in &quads.order {
|
||||
match kind {
|
||||
Kind::Solid => {
|
||||
pipeline.solid.render(
|
||||
render_pass,
|
||||
&layer.constants,
|
||||
&layer.solid,
|
||||
solid_offset..(solid_offset + count),
|
||||
);
|
||||
|
||||
solid_offset += count;
|
||||
}
|
||||
Kind::Gradient => {
|
||||
pipeline.gradient.render(
|
||||
render_pass,
|
||||
&layer.constants,
|
||||
&layer.gradient,
|
||||
gradient_offset..(gradient_offset + count),
|
||||
);
|
||||
|
||||
gradient_offset += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_frame(&mut self) {
|
||||
self.prepare_layer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Pipeline {
|
||||
let constant_layout =
|
||||
|
|
@ -74,79 +155,9 @@ impl Pipeline {
|
|||
Self {
|
||||
solid: solid::Pipeline::new(device, format, &constant_layout),
|
||||
gradient: gradient::Pipeline::new(device, format, &constant_layout),
|
||||
layers: Vec::new(),
|
||||
prepare_layer: 0,
|
||||
constant_layout,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
quads: &Batch,
|
||||
transformation: Transformation,
|
||||
scale: f32,
|
||||
) {
|
||||
if self.layers.len() <= self.prepare_layer {
|
||||
self.layers.push(Layer::new(device, &self.constant_layout));
|
||||
}
|
||||
|
||||
let layer = &mut self.layers[self.prepare_layer];
|
||||
layer.prepare(device, encoder, belt, quads, transformation, scale);
|
||||
|
||||
self.prepare_layer += 1;
|
||||
}
|
||||
|
||||
pub fn render<'a>(
|
||||
&'a self,
|
||||
layer: usize,
|
||||
bounds: Rectangle<u32>,
|
||||
quads: &Batch,
|
||||
render_pass: &mut wgpu::RenderPass<'a>,
|
||||
) {
|
||||
if let Some(layer) = self.layers.get(layer) {
|
||||
render_pass.set_scissor_rect(
|
||||
bounds.x,
|
||||
bounds.y,
|
||||
bounds.width,
|
||||
bounds.height,
|
||||
);
|
||||
|
||||
let mut solid_offset = 0;
|
||||
let mut gradient_offset = 0;
|
||||
|
||||
for (kind, count) in &quads.order {
|
||||
match kind {
|
||||
Kind::Solid => {
|
||||
self.solid.render(
|
||||
render_pass,
|
||||
&layer.constants,
|
||||
&layer.solid,
|
||||
solid_offset..(solid_offset + count),
|
||||
);
|
||||
|
||||
solid_offset += count;
|
||||
}
|
||||
Kind::Gradient => {
|
||||
self.gradient.render(
|
||||
render_pass,
|
||||
&layer.constants,
|
||||
&layer.gradient,
|
||||
gradient_offset..(gradient_offset + count),
|
||||
);
|
||||
|
||||
gradient_offset += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_frame(&mut self) {
|
||||
self.prepare_layer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl Layer {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pipeline {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ impl Layer {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pipeline {
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::graphics::text::{Editor, Paragraph, font_system, to_color};
|
|||
use rustc_hash::FxHashMap;
|
||||
use std::collections::hash_map;
|
||||
use std::sync::atomic::{self, AtomicU64};
|
||||
use std::sync::{self, Arc};
|
||||
use std::sync::{self, Arc, RwLock};
|
||||
|
||||
pub use crate::graphics::Text;
|
||||
|
||||
|
|
@ -94,10 +94,6 @@ struct Group {
|
|||
}
|
||||
|
||||
impl Storage {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn get(&self, cache: &Cache) -> Option<(&cryoglyph::TextAtlas, &Upload)> {
|
||||
if cache.text.is_empty() {
|
||||
return None;
|
||||
|
|
@ -272,48 +268,40 @@ impl Viewport {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Pipeline {
|
||||
state: cryoglyph::Cache,
|
||||
format: wgpu::TextureFormat,
|
||||
atlas: cryoglyph::TextAtlas,
|
||||
cache: cryoglyph::Cache,
|
||||
atlas: Arc<RwLock<cryoglyph::TextAtlas>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
renderers: Vec<cryoglyph::TextRenderer>,
|
||||
prepare_layer: usize,
|
||||
cache: BufferCache,
|
||||
storage: Storage,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
format: wgpu::TextureFormat,
|
||||
) -> Self {
|
||||
let state = cryoglyph::Cache::new(device);
|
||||
let atlas = cryoglyph::TextAtlas::with_color_mode(
|
||||
device, queue, &state, format, COLOR_MODE,
|
||||
);
|
||||
|
||||
Pipeline {
|
||||
state,
|
||||
format,
|
||||
renderers: Vec::new(),
|
||||
atlas,
|
||||
prepare_layer: 0,
|
||||
cache: BufferCache::new(),
|
||||
}
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn prepare(
|
||||
&mut self,
|
||||
pipeline: &Pipeline,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
viewport: &Viewport,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
storage: &mut Storage,
|
||||
batch: &Batch,
|
||||
layer_bounds: Rectangle,
|
||||
layer_transformation: Transformation,
|
||||
) {
|
||||
let mut atlas = pipeline.atlas.write().expect("Write to text atlas");
|
||||
|
||||
for item in batch {
|
||||
match item {
|
||||
Item::Group {
|
||||
|
|
@ -322,7 +310,7 @@ impl Pipeline {
|
|||
} => {
|
||||
if self.renderers.len() <= self.prepare_layer {
|
||||
self.renderers.push(cryoglyph::TextRenderer::new(
|
||||
&mut self.atlas,
|
||||
&mut atlas,
|
||||
device,
|
||||
wgpu::MultisampleState::default(),
|
||||
None,
|
||||
|
|
@ -336,7 +324,7 @@ impl Pipeline {
|
|||
&viewport.0,
|
||||
encoder,
|
||||
renderer,
|
||||
&mut self.atlas,
|
||||
&mut atlas,
|
||||
&mut self.cache,
|
||||
text,
|
||||
layer_bounds * layer_transformation,
|
||||
|
|
@ -358,13 +346,13 @@ impl Pipeline {
|
|||
transformation,
|
||||
cache,
|
||||
} => {
|
||||
storage.prepare(
|
||||
self.storage.prepare(
|
||||
device,
|
||||
queue,
|
||||
&viewport.0,
|
||||
encoder,
|
||||
self.format,
|
||||
&self.state,
|
||||
pipeline.format,
|
||||
&pipeline.cache,
|
||||
cache,
|
||||
layer_transformation * *transformation,
|
||||
layer_bounds * layer_transformation,
|
||||
|
|
@ -376,13 +364,14 @@ impl Pipeline {
|
|||
|
||||
pub fn render<'a>(
|
||||
&'a self,
|
||||
pipeline: &'a Pipeline,
|
||||
viewport: &'a Viewport,
|
||||
storage: &'a Storage,
|
||||
start: usize,
|
||||
batch: &'a Batch,
|
||||
bounds: Rectangle<u32>,
|
||||
render_pass: &mut wgpu::RenderPass<'a>,
|
||||
) -> usize {
|
||||
let atlas = pipeline.atlas.read().expect("Read text atlas");
|
||||
let mut layer_count = 0;
|
||||
|
||||
render_pass.set_scissor_rect(
|
||||
|
|
@ -398,13 +387,13 @@ impl Pipeline {
|
|||
let renderer = &self.renderers[start + layer_count];
|
||||
|
||||
renderer
|
||||
.render(&self.atlas, &viewport.0, render_pass)
|
||||
.render(&atlas, &viewport.0, render_pass)
|
||||
.expect("Render text");
|
||||
|
||||
layer_count += 1;
|
||||
}
|
||||
Item::Cached { cache, .. } => {
|
||||
if let Some((atlas, upload)) = storage.get(cache) {
|
||||
if let Some((atlas, upload)) = self.storage.get(cache) {
|
||||
upload
|
||||
.renderer
|
||||
.render(atlas, &viewport.0, render_pass)
|
||||
|
|
@ -417,18 +406,37 @@ impl Pipeline {
|
|||
layer_count
|
||||
}
|
||||
|
||||
pub fn create_viewport(&self, device: &wgpu::Device) -> Viewport {
|
||||
Viewport(cryoglyph::Viewport::new(device, &self.state))
|
||||
}
|
||||
|
||||
pub fn end_frame(&mut self) {
|
||||
self.atlas.trim();
|
||||
self.cache.trim();
|
||||
self.storage.trim();
|
||||
|
||||
self.prepare_layer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
format: wgpu::TextureFormat,
|
||||
) -> Self {
|
||||
let cache = cryoglyph::Cache::new(device);
|
||||
let atlas = cryoglyph::TextAtlas::with_color_mode(
|
||||
device, queue, &cache, format, COLOR_MODE,
|
||||
);
|
||||
|
||||
Pipeline {
|
||||
format,
|
||||
cache,
|
||||
atlas: Arc::new(RwLock::new(atlas)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_viewport(&self, device: &wgpu::Device) -> Viewport {
|
||||
Viewport(cryoglyph::Viewport::new(device, &self.cache))
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare(
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
|
|
|
|||
|
|
@ -153,42 +153,47 @@ impl Storage {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pipeline {
|
||||
blit: Option<msaa::Blit>,
|
||||
msaa: Option<msaa::Pipeline>,
|
||||
solid: solid::Pipeline,
|
||||
gradient: gradient::Pipeline,
|
||||
layers: Vec<Layer>,
|
||||
prepare_layer: usize,
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
format: wgpu::TextureFormat,
|
||||
antialiasing: Option<Antialiasing>,
|
||||
) -> Pipeline {
|
||||
Pipeline {
|
||||
blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
|
||||
solid: solid::Pipeline::new(device, format, antialiasing),
|
||||
gradient: gradient::Pipeline::new(device, format, antialiasing),
|
||||
pub struct State {
|
||||
msaa: Option<msaa::State>,
|
||||
layers: Vec<Layer>,
|
||||
prepare_layer: usize,
|
||||
storage: Storage,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(device: &wgpu::Device, pipeline: &Pipeline) -> Self {
|
||||
Self {
|
||||
msaa: pipeline
|
||||
.msaa
|
||||
.as_ref()
|
||||
.map(|pipeline| msaa::State::new(device, pipeline)),
|
||||
layers: Vec::new(),
|
||||
prepare_layer: 0,
|
||||
storage: Storage::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare(
|
||||
&mut self,
|
||||
pipeline: &Pipeline,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
storage: &mut Storage,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
items: &[Item],
|
||||
scale: Transformation,
|
||||
target_size: Size<u32>,
|
||||
) {
|
||||
let projection = if let Some(blit) = &mut self.blit {
|
||||
blit.prepare(device, encoder, belt, target_size) * scale
|
||||
let projection = if let Some((state, pipeline)) =
|
||||
self.msaa.as_mut().zip(pipeline.msaa.as_ref())
|
||||
{
|
||||
state.prepare(device, encoder, belt, pipeline, target_size) * scale
|
||||
} else {
|
||||
Transformation::orthographic(target_size.width, target_size.height)
|
||||
* scale
|
||||
|
|
@ -203,8 +208,8 @@ impl Pipeline {
|
|||
if self.layers.len() <= self.prepare_layer {
|
||||
self.layers.push(Layer::new(
|
||||
device,
|
||||
&self.solid,
|
||||
&self.gradient,
|
||||
&pipeline.solid,
|
||||
&pipeline.gradient,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -213,8 +218,8 @@ impl Pipeline {
|
|||
device,
|
||||
encoder,
|
||||
belt,
|
||||
&self.solid,
|
||||
&self.gradient,
|
||||
&pipeline.solid,
|
||||
&pipeline.gradient,
|
||||
meshes,
|
||||
projection * *transformation,
|
||||
);
|
||||
|
|
@ -225,12 +230,12 @@ impl Pipeline {
|
|||
transformation,
|
||||
cache,
|
||||
} => {
|
||||
storage.prepare(
|
||||
self.storage.prepare(
|
||||
device,
|
||||
encoder,
|
||||
belt,
|
||||
&self.solid,
|
||||
&self.gradient,
|
||||
&pipeline.solid,
|
||||
&pipeline.gradient,
|
||||
cache,
|
||||
projection * *transformation,
|
||||
);
|
||||
|
|
@ -241,9 +246,9 @@ impl Pipeline {
|
|||
|
||||
pub fn render(
|
||||
&mut self,
|
||||
pipeline: &Pipeline,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
target: &wgpu::TextureView,
|
||||
storage: &Storage,
|
||||
start: usize,
|
||||
batch: &Batch,
|
||||
bounds: Rectangle,
|
||||
|
|
@ -269,7 +274,7 @@ impl Pipeline {
|
|||
transformation,
|
||||
cache,
|
||||
} => {
|
||||
let upload = storage.get(cache)?;
|
||||
let upload = self.storage.get(cache)?;
|
||||
|
||||
Some((
|
||||
&upload.layer,
|
||||
|
|
@ -282,9 +287,9 @@ impl Pipeline {
|
|||
render(
|
||||
encoder,
|
||||
target,
|
||||
self.blit.as_mut(),
|
||||
&self.solid,
|
||||
&self.gradient,
|
||||
self.msaa.as_ref().zip(pipeline.msaa.as_ref()),
|
||||
&pipeline.solid,
|
||||
&pipeline.gradient,
|
||||
bounds,
|
||||
items,
|
||||
);
|
||||
|
|
@ -293,47 +298,54 @@ impl Pipeline {
|
|||
}
|
||||
|
||||
pub fn end_frame(&mut self) {
|
||||
self.storage.trim();
|
||||
|
||||
self.prepare_layer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
format: wgpu::TextureFormat,
|
||||
antialiasing: Option<Antialiasing>,
|
||||
) -> Pipeline {
|
||||
Pipeline {
|
||||
msaa: antialiasing.map(|a| msaa::Pipeline::new(device, format, a)),
|
||||
solid: solid::Pipeline::new(device, format, antialiasing),
|
||||
gradient: gradient::Pipeline::new(device, format, antialiasing),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render<'a>(
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
target: &wgpu::TextureView,
|
||||
mut blit: Option<&mut msaa::Blit>,
|
||||
mut msaa: Option<(&msaa::State, &msaa::Pipeline)>,
|
||||
solid: &solid::Pipeline,
|
||||
gradient: &gradient::Pipeline,
|
||||
bounds: Rectangle,
|
||||
group: impl Iterator<Item = (&'a Layer, &'a [Mesh], Transformation)>,
|
||||
) {
|
||||
{
|
||||
let (attachment, resolve_target, load) = if let Some(blit) = &mut blit {
|
||||
let (attachment, resolve_target) = blit.targets();
|
||||
|
||||
(
|
||||
attachment,
|
||||
Some(resolve_target),
|
||||
wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
||||
)
|
||||
let mut render_pass = if let Some((_state, pipeline)) = &mut msaa {
|
||||
pipeline.render_pass(encoder)
|
||||
} else {
|
||||
(target, None, wgpu::LoadOp::Load)
|
||||
};
|
||||
|
||||
let mut render_pass =
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("iced_wgpu.triangle.render_pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: attachment,
|
||||
resolve_target,
|
||||
view: target,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load,
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
for (layer, meshes, transformation) in group {
|
||||
layer.render(
|
||||
|
|
@ -347,8 +359,8 @@ fn render<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(blit) = blit {
|
||||
blit.draw(encoder, target);
|
||||
if let Some((state, pipeline)) = msaa {
|
||||
state.render(pipeline, encoder, target);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -649,7 +661,7 @@ mod solid {
|
|||
use crate::graphics::mesh;
|
||||
use crate::triangle;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pipeline {
|
||||
pub pipeline: wgpu::RenderPipeline,
|
||||
pub constants_layout: wgpu::BindGroupLayout,
|
||||
|
|
@ -801,7 +813,7 @@ mod gradient {
|
|||
use crate::graphics::mesh;
|
||||
use crate::triangle;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pipeline {
|
||||
pub pipeline: wgpu::RenderPipeline,
|
||||
pub constants_layout: wgpu::BindGroupLayout,
|
||||
|
|
|
|||
|
|
@ -2,35 +2,28 @@ use crate::core::{Size, Transformation};
|
|||
use crate::graphics;
|
||||
|
||||
use std::num::NonZeroU64;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Blit {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pipeline {
|
||||
format: wgpu::TextureFormat,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
constants: wgpu::BindGroup,
|
||||
ratio: wgpu::Buffer,
|
||||
sampler: wgpu::Sampler,
|
||||
raw: wgpu::RenderPipeline,
|
||||
constant_layout: wgpu::BindGroupLayout,
|
||||
texture_layout: wgpu::BindGroupLayout,
|
||||
sample_count: u32,
|
||||
targets: Option<Targets>,
|
||||
last_region: Option<Size<u32>>,
|
||||
targets: Arc<RwLock<Option<Targets>>>,
|
||||
}
|
||||
|
||||
impl Blit {
|
||||
impl Pipeline {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
format: wgpu::TextureFormat,
|
||||
antialiasing: graphics::Antialiasing,
|
||||
) -> Blit {
|
||||
) -> Pipeline {
|
||||
let sampler =
|
||||
device.create_sampler(&wgpu::SamplerDescriptor::default());
|
||||
|
||||
let ratio = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("iced-wgpu::triangle::msaa ratio"),
|
||||
size: std::mem::size_of::<Ratio>() as u64,
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let constant_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("iced_wgpu::triangle:msaa uniforms layout"),
|
||||
|
|
@ -56,22 +49,6 @@ impl Blit {
|
|||
],
|
||||
});
|
||||
|
||||
let constant_bind_group =
|
||||
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("iced_wgpu::triangle::msaa uniforms bind group"),
|
||||
layout: &constant_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: ratio.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let texture_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("iced_wgpu::triangle::msaa texture layout"),
|
||||
|
|
@ -143,31 +120,30 @@ impl Blit {
|
|||
cache: None,
|
||||
});
|
||||
|
||||
Blit {
|
||||
Self {
|
||||
format,
|
||||
pipeline,
|
||||
constants: constant_bind_group,
|
||||
ratio,
|
||||
sampler,
|
||||
raw: pipeline,
|
||||
constant_layout,
|
||||
texture_layout,
|
||||
sample_count: antialiasing.sample_count(),
|
||||
targets: None,
|
||||
last_region: None,
|
||||
targets: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare(
|
||||
&mut self,
|
||||
fn targets(
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
region_size: Size<u32>,
|
||||
) -> Transformation {
|
||||
match &mut self.targets {
|
||||
) -> Targets {
|
||||
let mut targets = self.targets.write().expect("Write MSAA targets");
|
||||
|
||||
match targets.as_mut() {
|
||||
Some(targets)
|
||||
if region_size.width <= targets.size.width
|
||||
&& region_size.height <= targets.size.height => {}
|
||||
_ => {
|
||||
self.targets = Some(Targets::new(
|
||||
*targets = Some(Targets::new(
|
||||
device,
|
||||
self.format,
|
||||
&self.texture_layout,
|
||||
|
|
@ -177,47 +153,21 @@ impl Blit {
|
|||
}
|
||||
}
|
||||
|
||||
let targets = self.targets.as_mut().unwrap();
|
||||
|
||||
if Some(region_size) != self.last_region {
|
||||
let ratio = Ratio {
|
||||
u: region_size.width as f32 / targets.size.width as f32,
|
||||
v: region_size.height as f32 / targets.size.height as f32,
|
||||
};
|
||||
|
||||
belt.write_buffer(
|
||||
encoder,
|
||||
&self.ratio,
|
||||
0,
|
||||
NonZeroU64::new(std::mem::size_of::<Ratio>() as u64)
|
||||
.expect("non-empty ratio"),
|
||||
device,
|
||||
)
|
||||
.copy_from_slice(bytemuck::bytes_of(&ratio));
|
||||
|
||||
self.last_region = Some(region_size);
|
||||
targets.as_ref().unwrap().clone()
|
||||
}
|
||||
|
||||
Transformation::orthographic(targets.size.width, targets.size.height)
|
||||
}
|
||||
|
||||
pub fn targets(&self) -> (&wgpu::TextureView, &wgpu::TextureView) {
|
||||
let targets = self.targets.as_ref().unwrap();
|
||||
|
||||
(&targets.attachment, &targets.resolve)
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
pub fn render_pass<'a>(
|
||||
&self,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
target: &wgpu::TextureView,
|
||||
) {
|
||||
let mut render_pass =
|
||||
encoder: &'a mut wgpu::CommandEncoder,
|
||||
) -> wgpu::RenderPass<'a> {
|
||||
let targets = self.targets.read().expect("Read MSAA targets");
|
||||
let targets = targets.as_ref().unwrap();
|
||||
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("iced_wgpu::triangle::msaa render pass"),
|
||||
label: Some("iced_wgpu.triangle.render_pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: target,
|
||||
resolve_target: None,
|
||||
view: &targets.attachment,
|
||||
resolve_target: Some(&targets.resolve),
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: wgpu::StoreOp::Store,
|
||||
|
|
@ -226,20 +176,11 @@ impl Blit {
|
|||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_bind_group(0, &self.constants, &[]);
|
||||
render_pass.set_bind_group(
|
||||
1,
|
||||
&self.targets.as_ref().unwrap().bind_group,
|
||||
&[],
|
||||
);
|
||||
render_pass.draw(0..6, 0..1);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct Targets {
|
||||
attachment: wgpu::TextureView,
|
||||
resolve: wgpu::TextureView,
|
||||
|
|
@ -308,9 +249,117 @@ impl Targets {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
#[repr(C)]
|
||||
struct Ratio {
|
||||
u: f32,
|
||||
v: f32,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ratio: wgpu::Buffer,
|
||||
constants: wgpu::BindGroup,
|
||||
last_ratio: Option<Ratio>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(device: &wgpu::Device, pipeline: &Pipeline) -> Self {
|
||||
let ratio = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("iced_wgpu::triangle::msaa ratio"),
|
||||
size: std::mem::size_of::<Ratio>() as u64,
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("iced_wgpu::triangle::msaa uniforms bind group"),
|
||||
layout: &pipeline.constant_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Sampler(&pipeline.sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: ratio.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Self {
|
||||
ratio,
|
||||
constants,
|
||||
last_ratio: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
pipeline: &Pipeline,
|
||||
region_size: Size<u32>,
|
||||
) -> Transformation {
|
||||
let targets = pipeline.targets(device, region_size);
|
||||
|
||||
let ratio = Ratio {
|
||||
u: region_size.width as f32 / targets.size.width as f32,
|
||||
v: region_size.height as f32 / targets.size.height as f32,
|
||||
};
|
||||
|
||||
if Some(ratio) != self.last_ratio {
|
||||
belt.write_buffer(
|
||||
encoder,
|
||||
&self.ratio,
|
||||
0,
|
||||
NonZeroU64::new(std::mem::size_of::<Ratio>() as u64)
|
||||
.expect("non-empty ratio"),
|
||||
device,
|
||||
)
|
||||
.copy_from_slice(bytemuck::bytes_of(&ratio));
|
||||
|
||||
self.last_ratio = Some(ratio);
|
||||
}
|
||||
|
||||
Transformation::orthographic(targets.size.width, targets.size.height)
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
&self,
|
||||
pipeline: &Pipeline,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
target: &wgpu::TextureView,
|
||||
) {
|
||||
let mut render_pass =
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("iced_wgpu::triangle::msaa render pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: target,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&pipeline.raw);
|
||||
render_pass.set_bind_group(0, &self.constants, &[]);
|
||||
render_pass.set_bind_group(
|
||||
1,
|
||||
&pipeline
|
||||
.targets
|
||||
.read()
|
||||
.expect("Read MSAA targets")
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.bind_group,
|
||||
&[],
|
||||
);
|
||||
render_pass.draw(0..6, 0..1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//! Connect a window with a renderer.
|
||||
use crate::core::{Color, Size};
|
||||
use crate::core::Color;
|
||||
use crate::graphics::color;
|
||||
use crate::graphics::compositor;
|
||||
use crate::graphics::error;
|
||||
|
|
@ -12,8 +12,6 @@ use crate::{Engine, Renderer};
|
|||
pub struct Compositor {
|
||||
instance: wgpu::Instance,
|
||||
adapter: wgpu::Adapter,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
format: wgpu::TextureFormat,
|
||||
alpha_mode: wgpu::CompositeAlphaMode,
|
||||
engine: Engine,
|
||||
|
|
@ -178,8 +176,8 @@ impl Compositor {
|
|||
Ok((device, queue)) => {
|
||||
let engine = Engine::new(
|
||||
&adapter,
|
||||
&device,
|
||||
&queue,
|
||||
device,
|
||||
queue,
|
||||
format,
|
||||
settings.antialiasing,
|
||||
);
|
||||
|
|
@ -187,8 +185,6 @@ impl Compositor {
|
|||
return Ok(Compositor {
|
||||
instance,
|
||||
adapter,
|
||||
device,
|
||||
queue,
|
||||
format,
|
||||
alpha_mode,
|
||||
engine,
|
||||
|
|
@ -215,7 +211,6 @@ pub async fn new<W: compositor::Window>(
|
|||
|
||||
/// Presents the given primitives with the given [`Compositor`].
|
||||
pub fn present<T: AsRef<str>>(
|
||||
compositor: &mut Compositor,
|
||||
renderer: &mut Renderer,
|
||||
surface: &mut wgpu::Surface<'static>,
|
||||
viewport: &Viewport,
|
||||
|
|
@ -225,21 +220,11 @@ pub fn present<T: AsRef<str>>(
|
|||
) -> Result<(), compositor::SurfaceError> {
|
||||
match surface.get_current_texture() {
|
||||
Ok(frame) => {
|
||||
let mut encoder = compositor.device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("iced_wgpu encoder"),
|
||||
},
|
||||
);
|
||||
|
||||
let view = &frame
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
renderer.present(
|
||||
&mut compositor.engine,
|
||||
&compositor.device,
|
||||
&compositor.queue,
|
||||
&mut encoder,
|
||||
let _submission = renderer.present(
|
||||
Some(background_color),
|
||||
frame.texture.format(),
|
||||
view,
|
||||
|
|
@ -247,8 +232,6 @@ pub fn present<T: AsRef<str>>(
|
|||
overlay,
|
||||
);
|
||||
|
||||
let _ = compositor.engine.submit(&compositor.queue, encoder);
|
||||
|
||||
// Present the frame
|
||||
on_pre_present();
|
||||
frame.present();
|
||||
|
|
@ -305,8 +288,7 @@ impl graphics::Compositor for Compositor {
|
|||
|
||||
fn create_renderer(&self) -> Self::Renderer {
|
||||
Renderer::new(
|
||||
&self.device,
|
||||
&self.engine,
|
||||
self.engine.clone(),
|
||||
self.settings.default_font,
|
||||
self.settings.default_text_size,
|
||||
)
|
||||
|
|
@ -337,7 +319,7 @@ impl graphics::Compositor for Compositor {
|
|||
height: u32,
|
||||
) {
|
||||
surface.configure(
|
||||
&self.device,
|
||||
&self.engine.device,
|
||||
&wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: self.format,
|
||||
|
|
@ -370,7 +352,6 @@ impl graphics::Compositor for Compositor {
|
|||
on_pre_present: impl FnOnce(),
|
||||
) -> Result<(), compositor::SurfaceError> {
|
||||
present(
|
||||
self,
|
||||
renderer,
|
||||
surface,
|
||||
viewport,
|
||||
|
|
@ -380,143 +361,12 @@ impl graphics::Compositor for Compositor {
|
|||
)
|
||||
}
|
||||
|
||||
fn screenshot<T: AsRef<str>>(
|
||||
fn screenshot(
|
||||
&mut self,
|
||||
renderer: &mut Self::Renderer,
|
||||
viewport: &Viewport,
|
||||
background_color: Color,
|
||||
overlay: &[T],
|
||||
) -> Vec<u8> {
|
||||
screenshot(self, renderer, viewport, background_color, overlay)
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the current surface to an offscreen buffer.
|
||||
///
|
||||
/// Returns RGBA bytes of the texture data.
|
||||
pub fn screenshot<T: AsRef<str>>(
|
||||
compositor: &mut Compositor,
|
||||
renderer: &mut Renderer,
|
||||
viewport: &Viewport,
|
||||
background_color: Color,
|
||||
overlay: &[T],
|
||||
) -> Vec<u8> {
|
||||
let dimensions = BufferDimensions::new(viewport.physical_size());
|
||||
|
||||
let texture_extent = wgpu::Extent3d {
|
||||
width: dimensions.width,
|
||||
height: dimensions.height,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
|
||||
let texture = compositor.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("iced_wgpu.offscreen.source_texture"),
|
||||
size: texture_extent,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: compositor.format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||
| wgpu::TextureUsages::COPY_SRC
|
||||
| wgpu::TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
});
|
||||
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
let mut encoder = compositor.device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("iced_wgpu.offscreen.encoder"),
|
||||
},
|
||||
);
|
||||
|
||||
renderer.present(
|
||||
&mut compositor.engine,
|
||||
&compositor.device,
|
||||
&compositor.queue,
|
||||
&mut encoder,
|
||||
Some(background_color),
|
||||
texture.format(),
|
||||
&view,
|
||||
viewport,
|
||||
overlay,
|
||||
);
|
||||
|
||||
let texture = crate::color::convert(
|
||||
&compositor.device,
|
||||
&mut encoder,
|
||||
texture,
|
||||
if color::GAMMA_CORRECTION {
|
||||
wgpu::TextureFormat::Rgba8UnormSrgb
|
||||
} else {
|
||||
wgpu::TextureFormat::Rgba8Unorm
|
||||
},
|
||||
);
|
||||
|
||||
let output_buffer =
|
||||
compositor.device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("iced_wgpu.offscreen.output_texture_buffer"),
|
||||
size: (dimensions.padded_bytes_per_row * dimensions.height as usize)
|
||||
as u64,
|
||||
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
encoder.copy_texture_to_buffer(
|
||||
texture.as_image_copy(),
|
||||
wgpu::TexelCopyBufferInfo {
|
||||
buffer: &output_buffer,
|
||||
layout: wgpu::TexelCopyBufferLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(dimensions.padded_bytes_per_row as u32),
|
||||
rows_per_image: None,
|
||||
},
|
||||
},
|
||||
texture_extent,
|
||||
);
|
||||
|
||||
let index = compositor.engine.submit(&compositor.queue, encoder);
|
||||
|
||||
let slice = output_buffer.slice(..);
|
||||
slice.map_async(wgpu::MapMode::Read, |_| {});
|
||||
|
||||
let _ = compositor
|
||||
.device
|
||||
.poll(wgpu::Maintain::WaitForSubmissionIndex(index));
|
||||
|
||||
let mapped_buffer = slice.get_mapped_range();
|
||||
|
||||
mapped_buffer.chunks(dimensions.padded_bytes_per_row).fold(
|
||||
vec![],
|
||||
|mut acc, row| {
|
||||
acc.extend(&row[..dimensions.unpadded_bytes_per_row]);
|
||||
acc
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct BufferDimensions {
|
||||
width: u32,
|
||||
height: u32,
|
||||
unpadded_bytes_per_row: usize,
|
||||
padded_bytes_per_row: usize,
|
||||
}
|
||||
|
||||
impl BufferDimensions {
|
||||
fn new(size: Size<u32>) -> Self {
|
||||
let unpadded_bytes_per_row = size.width as usize * 4; //slice of buffer per row; always RGBA
|
||||
let alignment = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize; //256
|
||||
let padded_bytes_per_row_padding =
|
||||
(alignment - unpadded_bytes_per_row % alignment) % alignment;
|
||||
let padded_bytes_per_row =
|
||||
unpadded_bytes_per_row + padded_bytes_per_row_padding;
|
||||
|
||||
Self {
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
unpadded_bytes_per_row,
|
||||
padded_bytes_per_row,
|
||||
}
|
||||
renderer.screenshot(viewport, background_color)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -773,7 +773,6 @@ async fn run_instance<P, C>(
|
|||
&mut messages,
|
||||
&mut clipboard,
|
||||
&mut control_sender,
|
||||
&mut debug,
|
||||
&mut user_interfaces,
|
||||
&mut window_manager,
|
||||
&mut ui_caches,
|
||||
|
|
@ -966,7 +965,6 @@ async fn run_instance<P, C>(
|
|||
&mut messages,
|
||||
&mut clipboard,
|
||||
&mut control_sender,
|
||||
&mut debug,
|
||||
&mut user_interfaces,
|
||||
&mut window_manager,
|
||||
&mut ui_caches,
|
||||
|
|
@ -1187,7 +1185,6 @@ fn run_action<P, C>(
|
|||
messages: &mut Vec<P::Message>,
|
||||
clipboard: &mut Clipboard,
|
||||
control_sender: &mut mpsc::UnboundedSender<Control>,
|
||||
debug: &mut Debug,
|
||||
interfaces: &mut FxHashMap<
|
||||
window::Id,
|
||||
UserInterface<'_, P::Message, P::Theme, P::Renderer>,
|
||||
|
|
@ -1477,7 +1474,6 @@ fn run_action<P, C>(
|
|||
&mut window.renderer,
|
||||
window.state.viewport(),
|
||||
window.state.background_color(),
|
||||
&debug.overlay(),
|
||||
);
|
||||
|
||||
let _ = channel.send(core::window::Screenshot::new(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue