Allow iced_wgpu to render to any TextureView

This commit is contained in:
Héctor Ramón Jiménez 2020-02-09 03:25:13 +01:00
parent 95880ca74b
commit f1e20a61f1
13 changed files with 355 additions and 240 deletions

View file

@ -19,7 +19,7 @@
//! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
//! [WebGPU API]: https://gpuweb.github.io/gpuweb/
//! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
#![deny(missing_docs)]
//#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unused_results)]
#![forbid(unsafe_code)]
@ -27,19 +27,25 @@
pub mod defaults;
pub mod triangle;
pub mod widget;
pub mod window;
mod image;
mod primitive;
mod quad;
mod renderer;
mod settings;
mod target;
mod text;
mod transformation;
mod viewport;
pub use defaults::Defaults;
pub use primitive::Primitive;
pub use renderer::{Renderer, Target};
pub use renderer::Renderer;
pub use settings::Settings;
pub use target::Target;
pub use viewport::Viewport;
#[doc(no_inline)]
pub use widget::*;

View file

@ -1,29 +1,20 @@
use crate::{
image, quad, text, triangle, Defaults, Image, Primitive, Quad, Settings,
Transformation,
Target, Transformation,
};
use iced_native::{
layout, window, Background, Color, Layout, MouseCursor, Point, Rectangle,
Vector, Widget,
layout, Background, Color, Layout, MouseCursor, Point, Rectangle, Vector,
Widget,
};
use std::sync::Arc;
use wgpu::{
Adapter, BackendBit, CommandEncoderDescriptor, Device, DeviceDescriptor,
Extensions, Limits, PowerPreference, Queue, RequestAdapterOptions,
};
mod target;
mod widget;
pub use target::Target;
/// A [`wgpu`] renderer.
///
/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
#[derive(Debug)]
pub struct Renderer {
device: Device,
queue: Queue,
quad_pipeline: quad::Pipeline,
image_pipeline: image::Pipeline,
text_pipeline: text::Pipeline,
@ -53,29 +44,16 @@ impl<'a> Layer<'a> {
}
impl Renderer {
fn new(settings: Settings) -> Self {
let adapter = Adapter::request(&RequestAdapterOptions {
power_preference: PowerPreference::Default,
backends: BackendBit::all(),
})
.expect("Request adapter");
let (mut device, queue) = adapter.request_device(&DeviceDescriptor {
extensions: Extensions {
anisotropic_filtering: false,
},
limits: Limits { max_bind_groups: 2 },
});
let text_pipeline =
text::Pipeline::new(&mut device, settings.default_font);
let quad_pipeline = quad::Pipeline::new(&mut device);
let image_pipeline = crate::image::Pipeline::new(&mut device);
let triangle_pipeline = triangle::Pipeline::new(&mut device);
/// Creates a new [`Renderer`].
///
/// [`Renderer`]: struct.Renderer.html
pub fn new(settings: Settings, device: &mut wgpu::Device) -> Self {
let text_pipeline = text::Pipeline::new(device, settings.default_font);
let quad_pipeline = quad::Pipeline::new(device);
let image_pipeline = crate::image::Pipeline::new(device);
let triangle_pipeline = triangle::Pipeline::new(device);
Self {
device,
queue,
quad_pipeline,
image_pipeline,
text_pipeline,
@ -83,38 +61,25 @@ impl Renderer {
}
}
fn draw<T: AsRef<str>>(
/// Draws the provided primitives in the given [`Target`].
///
/// The text provided as overlay will be renderer on top of the primitives.
/// This is useful for rendering debug information.
///
/// [`Target`]: struct.Target.html
pub fn draw<T: AsRef<str>>(
&mut self,
device: &mut wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
target: Target<'_>,
(primitive, mouse_cursor): &(Primitive, MouseCursor),
overlay: &[T],
target: &mut Target,
) -> MouseCursor {
log::debug!("Drawing");
let (width, height) = target.dimensions();
let scale_factor = target.scale_factor();
let transformation = target.transformation();
let frame = target.next_frame();
let mut encoder = self
.device
.create_command_encoder(&CommandEncoderDescriptor { todo: 0 });
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
load_op: wgpu::LoadOp::Clear,
store_op: wgpu::StoreOp::Store,
clear_color: wgpu::Color {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
},
}],
depth_stencil_attachment: None,
});
let (width, height) = target.viewport.dimensions();
let scale_factor = target.viewport.scale_factor();
let transformation = target.viewport.transformation();
let mut layers = Vec::new();
@ -133,15 +98,15 @@ impl Renderer {
for layer in layers {
self.flush(
device,
scale_factor,
transformation,
&layer,
&mut encoder,
&frame.view,
encoder,
target.texture,
);
}
self.queue.submit(&[encoder.finish()]);
self.image_pipeline.trim_cache();
*mouse_cursor
@ -336,6 +301,7 @@ impl Renderer {
fn flush(
&mut self,
device: &mut wgpu::Device,
scale_factor: f32,
transformation: Transformation,
layer: &Layer<'_>,
@ -352,7 +318,7 @@ impl Renderer {
);
self.triangle_pipeline.draw(
&mut self.device,
device,
encoder,
target,
translated,
@ -364,7 +330,7 @@ impl Renderer {
if layer.quads.len() > 0 {
self.quad_pipeline.draw(
&mut self.device,
device,
encoder,
&layer.quads,
transformation,
@ -383,7 +349,7 @@ impl Renderer {
);
self.image_pipeline.draw(
&mut self.device,
device,
encoder,
&layer.images,
translated_and_scaled,
@ -429,7 +395,7 @@ impl Renderer {
}
self.text_pipeline.draw_queued(
&mut self.device,
device,
encoder,
target,
transformation,
@ -461,24 +427,6 @@ impl iced_native::Renderer for Renderer {
}
}
impl window::Renderer for Renderer {
type Settings = Settings;
type Target = Target;
fn new(settings: Settings) -> Self {
Self::new(settings)
}
fn draw<T: AsRef<str>>(
&mut self,
output: &Self::Output,
overlay: &[T],
target: &mut Target,
) -> MouseCursor {
self.draw(output, overlay, target)
}
}
impl layout::Debugger for Renderer {
fn explain<Message>(
&mut self,

View file

@ -1,91 +0,0 @@
use crate::{Renderer, Transformation};
use iced_native::window;
use raw_window_handle::HasRawWindowHandle;
/// A rendering target.
#[derive(Debug)]
pub struct Target {
surface: wgpu::Surface,
width: u32,
height: u32,
scale_factor: f32,
transformation: Transformation,
swap_chain: wgpu::SwapChain,
}
impl Target {
pub(crate) fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub(crate) fn scale_factor(&self) -> f32 {
self.scale_factor
}
pub(crate) fn transformation(&self) -> Transformation {
self.transformation
}
pub(crate) fn next_frame(&mut self) -> wgpu::SwapChainOutput<'_> {
self.swap_chain.get_next_texture()
}
}
impl window::Target for Target {
type Renderer = Renderer;
fn new<W: HasRawWindowHandle>(
window: &W,
width: u32,
height: u32,
scale_factor: f64,
renderer: &Renderer,
) -> Target {
let surface = wgpu::Surface::create(window);
let swap_chain =
new_swap_chain(&surface, width, height, &renderer.device);
Target {
surface,
width,
height,
scale_factor: scale_factor as f32,
transformation: Transformation::orthographic(width, height),
swap_chain,
}
}
fn resize(
&mut self,
width: u32,
height: u32,
scale_factor: f64,
renderer: &Renderer,
) {
self.width = width;
self.height = height;
self.scale_factor = scale_factor as f32;
self.transformation = Transformation::orthographic(width, height);
self.swap_chain =
new_swap_chain(&self.surface, width, height, &renderer.device);
}
}
fn new_swap_chain(
surface: &wgpu::Surface,
width: u32,
height: u32,
device: &wgpu::Device,
) -> wgpu::SwapChain {
device.create_swap_chain(
&surface,
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8UnormSrgb,
width,
height,
present_mode: wgpu::PresentMode::Vsync,
},
)
}

8
wgpu/src/target.rs Normal file
View file

@ -0,0 +1,8 @@
use crate::Viewport;
/// A rendering target.
#[derive(Debug)]
pub struct Target<'a> {
pub texture: &'a wgpu::TextureView,
pub viewport: &'a Viewport,
}

32
wgpu/src/viewport.rs Normal file
View file

@ -0,0 +1,32 @@
use crate::Transformation;
#[derive(Debug)]
pub struct Viewport {
width: u32,
height: u32,
scale_factor: f32,
transformation: Transformation,
}
impl Viewport {
pub fn new(width: u32, height: u32, scale_factor: f64) -> Viewport {
Viewport {
width,
height,
scale_factor: scale_factor as f32,
transformation: Transformation::orthographic(width, height),
}
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn scale_factor(&self) -> f32 {
self.scale_factor
}
pub(crate) fn transformation(&self) -> Transformation {
self.transformation
}
}

5
wgpu/src/window.rs Normal file
View file

@ -0,0 +1,5 @@
mod backend;
mod swap_chain;
pub use backend::Backend;
pub use swap_chain::SwapChain;

View file

@ -0,0 +1,99 @@
use crate::{window::SwapChain, Renderer, Settings, Target};
use iced_native::MouseCursor;
use raw_window_handle::HasRawWindowHandle;
#[derive(Debug)]
pub struct Backend {
device: wgpu::Device,
queue: wgpu::Queue,
}
impl iced_native::window::Backend for Backend {
type Settings = Settings;
type Renderer = Renderer;
type Surface = wgpu::Surface;
type SwapChain = SwapChain;
fn new(settings: Self::Settings) -> (Backend, Renderer) {
let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
backends: wgpu::BackendBit::all(),
})
.expect("Request adapter");
let (mut device, queue) =
adapter.request_device(&wgpu::DeviceDescriptor {
extensions: wgpu::Extensions {
anisotropic_filtering: false,
},
limits: wgpu::Limits { max_bind_groups: 2 },
});
let renderer = Renderer::new(settings, &mut device);
(Backend { device, queue }, renderer)
}
fn create_surface<W: HasRawWindowHandle>(
&mut self,
window: &W,
) -> wgpu::Surface {
wgpu::Surface::create(window)
}
fn create_swap_chain(
&mut self,
surface: &Self::Surface,
width: u32,
height: u32,
scale_factor: f64,
) -> SwapChain {
SwapChain::new(&self.device, surface, width, height, scale_factor)
}
fn draw<T: AsRef<str>>(
&mut self,
renderer: &mut Self::Renderer,
swap_chain: &mut SwapChain,
output: &<Self::Renderer as iced_native::Renderer>::Output,
overlay: &[T],
) -> MouseCursor {
let (frame, viewport) = swap_chain.next_frame();
let mut encoder = self.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor { todo: 0 },
);
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
load_op: wgpu::LoadOp::Clear,
store_op: wgpu::StoreOp::Store,
clear_color: wgpu::Color {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
},
}],
depth_stencil_attachment: None,
});
let mouse_cursor = renderer.draw(
&mut self.device,
&mut encoder,
Target {
texture: &frame.view,
viewport,
},
output,
overlay,
);
self.queue.submit(&[encoder.finish()]);
mouse_cursor
}
}

View file

@ -0,0 +1,49 @@
use crate::Viewport;
/// The rendering target of a window.
///
/// It represents a series of virtual framebuffers with a scale factor.
#[derive(Debug)]
pub struct SwapChain {
raw: wgpu::SwapChain,
viewport: Viewport,
}
impl SwapChain {}
impl SwapChain {
pub fn new(
device: &wgpu::Device,
surface: &wgpu::Surface,
width: u32,
height: u32,
scale_factor: f64,
) -> SwapChain {
SwapChain {
raw: new_swap_chain(surface, width, height, device),
viewport: Viewport::new(width, height, scale_factor),
}
}
pub fn next_frame(&mut self) -> (wgpu::SwapChainOutput<'_>, &Viewport) {
(self.raw.get_next_texture(), &self.viewport)
}
}
fn new_swap_chain(
surface: &wgpu::Surface,
width: u32,
height: u32,
device: &wgpu::Device,
) -> wgpu::SwapChain {
device.create_swap_chain(
&surface,
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
format: wgpu::TextureFormat::Bgra8UnormSrgb,
width,
height,
present_mode: wgpu::PresentMode::Vsync,
},
)
}