Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Custom Shader Widget #2085

Merged
merged 26 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
781ef1f
Added support for custom shader widget for iced_wgpu backend.
bungoboingo Sep 14, 2023
36e8521
Removed `Into` for Rectangle<f32> from u32
bungoboingo Sep 18, 2023
91fca02
Reexport Transformation from widget::shader
bungoboingo Sep 21, 2023
65f4ff0
Added redraw request handling to widget events.
bungoboingo Sep 28, 2023
de9420e
Fix latest `wgpu` changes
hecrj Nov 14, 2023
46a48af
Write missing documentation for `custom` module in `iced_wgpu`
hecrj Nov 14, 2023
3e8ed05
Update `wgpu` in `custom_shader` example
hecrj Nov 14, 2023
33f6262
Fix `clippy` lints :crab:
hecrj Nov 14, 2023
226eac3
Remove old `widget` modules in `iced_renderer`
hecrj Nov 14, 2023
2dda913
Run `cargo fmt`
hecrj Nov 14, 2023
9489e29
Re-organize `custom` module as `pipeline` module
hecrj Nov 14, 2023
882ae30
Enable `iced_renderer/wgpu` feature in `iced_widget`
hecrj Nov 14, 2023
c2baf18
Use `Instant` from `iced_core` instead of `std`
hecrj Nov 14, 2023
280d373
Fix broken intra-doc links
hecrj Nov 14, 2023
91d7df5
Create `shader` function helper in `iced_widget`
hecrj Nov 14, 2023
63f36b0
Export `wgpu` crate in `shader` module in `iced_widget`
hecrj Nov 14, 2023
78a0638
Use a single source for amount of cubes in `custom_shader` example
hecrj Nov 14, 2023
9ddfaf3
Rename `cubes` to `scene` in `custom_shader` example
hecrj Nov 14, 2023
34b5cb7
Remove `Default` implementation in `custom_shader` example
hecrj Nov 14, 2023
fee3bf0
Kill current render pass only when custom pipelines are present in layer
hecrj Nov 14, 2023
b1b2467
Fix render pass label in `iced_wgpu`
hecrj Nov 14, 2023
811aa67
Improve module hierarchy of `custom_shader` example
hecrj Nov 14, 2023
77dfa60
Move `textures` directory outside of `src` in `custom_shader` example
hecrj Nov 14, 2023
74b920a
Remove unnecessary `self` in `iced_style::theme`
hecrj Nov 14, 2023
8f384c8
Remove unsused `custom.rs` file in `iced_wgpu`
hecrj Nov 14, 2023
0968c5b
Remove unused import in `custom_shader` example
hecrj Nov 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions examples/custom_shader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ mod cubes;
mod pipeline;
mod primitive;

use crate::camera::Camera;
use crate::cubes::Cubes;
use crate::pipeline::Pipeline;

use iced::executor;
use iced::time::Instant;
use iced::widget::{
checkbox, column, container, row, slider, text, vertical_space, Shader,
};
use iced::window;
use iced::{
executor, window, Alignment, Application, Color, Command, Element, Length,
Renderer, Subscription, Theme,
Alignment, Application, Color, Command, Element, Length, Renderer,
Subscription, Theme,
};
use std::time::Instant;

fn main() -> iced::Result {
IcedCubes::run(iced::Settings::default())
Expand Down
15 changes: 8 additions & 7 deletions examples/custom_shader/src/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ pub mod vertex;
mod buffer;
mod uniforms;

use crate::camera::Camera;
use crate::pipeline::Pipeline;
use crate::primitive::cube::Cube;
pub use buffer::Buffer;
pub use cube::Cube;
pub use uniforms::Uniforms;
pub use vertex::Vertex;

use crate::Camera;
use crate::Pipeline;

use iced::advanced::graphics::Transformation;
use iced::widget::shader;
use iced::{Color, Rectangle, Size};

pub use crate::primitive::vertex::Vertex;
pub use buffer::Buffer;
pub use uniforms::Uniforms;

/// A collection of `Cube`s that can be rendered.
#[derive(Debug)]
pub struct Primitive {
Expand Down
44 changes: 23 additions & 21 deletions renderer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#![forbid(rust_2018_idioms)]
#![deny(unsafe_code, unused_results, rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[cfg(feature = "wgpu")]
pub use iced_wgpu as wgpu;

pub mod compositor;

#[cfg(feature = "geometry")]
pub mod geometry;

mod settings;
pub mod widget;

pub use iced_graphics as graphics;
pub use iced_graphics::core;
Expand Down Expand Up @@ -60,26 +62,6 @@ impl<T> Renderer<T> {
}
}
}

pub fn draw_custom<P: widget::shader::Primitive>(
&mut self,
bounds: Rectangle,
primitive: P,
) {
match self {
Renderer::TinySkia(_) => {
log::warn!(
"Custom shader primitive is unavailable with tiny-skia."
);
}
#[cfg(feature = "wgpu")]
Renderer::Wgpu(renderer) => {
renderer.draw_primitive(iced_wgpu::Primitive::Custom(
iced_wgpu::primitive::Custom::shader(bounds, primitive),
));
}
}
}
}

impl<T> core::Renderer for Renderer<T> {
Expand Down Expand Up @@ -292,3 +274,23 @@ impl<T> crate::graphics::geometry::Renderer for Renderer<T> {
}
}
}

#[cfg(feature = "wgpu")]
impl<T> iced_wgpu::primitive::pipeline::Renderer for Renderer<T> {
fn draw_pipeline_primitive(
hecrj marked this conversation as resolved.
Show resolved Hide resolved
&mut self,
bounds: Rectangle,
primitive: impl wgpu::primitive::pipeline::Primitive,
) {
match self {
Self::TinySkia(_renderer) => {
log::warn!(
"Custom shader primitive is unavailable with tiny-skia."
);
}
Self::Wgpu(renderer) => {
renderer.draw_pipeline_primitive(bounds, primitive);
}
}
}
}
2 changes: 0 additions & 2 deletions renderer/src/widget.rs

This file was deleted.

29 changes: 15 additions & 14 deletions wgpu/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ use crate::core::{Color, Size};
use crate::graphics::backend;
use crate::graphics::color;
use crate::graphics::{Transformation, Viewport};
use crate::primitive::pipeline;
use crate::primitive::{self, Primitive};
use crate::{custom, quad, text, triangle};
use crate::quad;
use crate::text;
use crate::triangle;
use crate::{Layer, Settings};

#[cfg(feature = "tracing")]
Expand All @@ -23,7 +26,7 @@ pub struct Backend {
quad_pipeline: quad::Pipeline,
text_pipeline: text::Pipeline,
triangle_pipeline: triangle::Pipeline,
pipeline_storage: custom::Storage,
pipeline_storage: pipeline::Storage,

#[cfg(any(feature = "image", feature = "svg"))]
image_pipeline: image::Pipeline,
Expand All @@ -49,7 +52,7 @@ impl Backend {
quad_pipeline,
text_pipeline,
triangle_pipeline,
pipeline_storage: custom::Storage::default(),
pipeline_storage: pipeline::Storage::default(),

#[cfg(any(feature = "image", feature = "svg"))]
image_pipeline,
Expand Down Expand Up @@ -183,9 +186,9 @@ impl Backend {
);
}

if !layer.shaders.is_empty() {
for shader in &layer.shaders {
shader.primitive.prepare(
if !layer.pipelines.is_empty() {
for pipeline in &layer.pipelines {
pipeline.primitive.prepare(
format,
device,
queue,
Expand Down Expand Up @@ -323,19 +326,17 @@ impl Backend {
// kill render pass to let custom shaders get mut access to encoder
let _ = ManuallyDrop::into_inner(render_pass);

if !layer.shaders.is_empty() {
for shader in &layer.shaders {
//This extra check is needed since each custom pipeline must set it's own
//scissor rect, which will panic if bounds.w/h < 1
let bounds = shader.bounds * scale_factor;
if !layer.pipelines.is_empty() {
for pipeline in &layer.pipelines {
let bounds = (pipeline.bounds * scale_factor).snap();

if bounds.width < 1.0 || bounds.height < 1.0 {
if bounds.width < 1 || bounds.height < 1 {
continue;
}

shader.primitive.render(
pipeline.primitive.render(
&self.pipeline_storage,
bounds.snap(),
bounds,
target,
target_size,
encoder,
Expand Down
63 changes: 2 additions & 61 deletions wgpu/src/custom.rs
Original file line number Diff line number Diff line change
@@ -1,67 +1,8 @@
//! Draw custom primitives.
use crate::core::{Rectangle, Size};
use crate::graphics::Transformation;
use crate::primitive;

use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt::Debug;

/// Stores custom, user-provided pipelines.
#[derive(Default, Debug)]
pub struct Storage {
pipelines: HashMap<TypeId, Box<dyn Any>>,
}

impl Storage {
/// Returns `true` if `Storage` contains a pipeline with type `T`.
pub fn has<T: 'static>(&self) -> bool {
self.pipelines.get(&TypeId::of::<T>()).is_some()
}

/// Inserts the pipeline `T` in to [`Storage`].
pub fn store<T: 'static>(&mut self, pipeline: T) {
let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
}

/// Returns a reference to pipeline with type `T` if it exists in [`Storage`].
pub fn get<T: 'static>(&self) -> Option<&T> {
self.pipelines.get(&TypeId::of::<T>()).map(|pipeline| {
pipeline
.downcast_ref::<T>()
.expect("Pipeline with this type does not exist in Storage.")
})
}

/// Returns a mutable reference to pipeline `T` if it exists in [`Storage`].
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.pipelines.get_mut(&TypeId::of::<T>()).map(|pipeline| {
pipeline
.downcast_mut::<T>()
.expect("Pipeline with this type does not exist in Storage.")
})
}
}

/// A set of methods which allows a [`Primitive`] to be rendered.
pub trait Primitive: Debug + Send + Sync + 'static {
/// Processes the [`Primitive`], allowing for GPU buffer allocation.
fn prepare(
&self,
format: wgpu::TextureFormat,
device: &wgpu::Device,
queue: &wgpu::Queue,
target_size: Size<u32>,
scale_factor: f32,
transform: Transformation,
storage: &mut Storage,
);

/// Renders the [`Primitive`].
fn render(
&self,
storage: &Storage,
bounds: Rectangle<u32>,
target: &wgpu::TextureView,
target_size: Size<u32>,
encoder: &mut wgpu::CommandEncoder,
);
}
19 changes: 12 additions & 7 deletions wgpu/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ pub struct Layer<'a> {
/// The images of the [`Layer`].
pub images: Vec<Image>,

/// The custom shader primitives of this [`Layer`].
pub shaders: Vec<primitive::Shader>,
/// The custom pipelines of this [`Layer`].
pub pipelines: Vec<primitive::Pipeline>,
}

impl<'a> Layer<'a> {
Expand All @@ -48,7 +48,7 @@ impl<'a> Layer<'a> {
meshes: Vec::new(),
text: Vec::new(),
images: Vec::new(),
shaders: Vec::new(),
pipelines: Vec::new(),
}
}

Expand Down Expand Up @@ -312,16 +312,21 @@ impl<'a> Layer<'a> {
}
}
},
primitive::Custom::Shader(shader) => {
primitive::Custom::Pipeline(pipeline) => {
let layer = &mut layers[current_layer];

let bounds = Rectangle::new(
Point::new(translation.x, translation.y),
shader.bounds.size(),
pipeline.bounds.size(),
);

if layer.bounds.intersection(&bounds).is_some() {
layer.shaders.push(shader.clone());
if let Some(clip_bounds) =
layer.bounds.intersection(&bounds)
{
layer.pipelines.push(primitive::Pipeline {
bounds: clip_bounds,
primitive: pipeline.primitive.clone(),
});
}
}
},
Expand Down
1 change: 0 additions & 1 deletion wgpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
rustdoc::broken_intra_doc_links
)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
pub mod custom;
pub mod layer;
pub mod primitive;
pub mod settings;
Expand Down
43 changes: 8 additions & 35 deletions wgpu/src/primitive.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Draw using different graphical primitives.
pub mod pipeline;

pub use pipeline::Pipeline;

use crate::core::Rectangle;
use crate::custom;
use crate::graphics::{Damage, Mesh};
use std::any::Any;

use std::fmt::Debug;
use std::sync::Arc;

/// The graphical primitives supported by `iced_wgpu`.
pub type Primitive = crate::graphics::Primitive<Custom>;
Expand All @@ -14,44 +16,15 @@ pub type Primitive = crate::graphics::Primitive<Custom>;
pub enum Custom {
/// A mesh primitive.
Mesh(Mesh),
/// A custom shader primitive
Shader(Shader),
}

impl Custom {
/// Create a custom [`Shader`] primitive.
pub fn shader<P: custom::Primitive>(
bounds: Rectangle,
primitive: P,
) -> Self {
Self::Shader(Shader {
bounds,
primitive: Arc::new(primitive),
})
}
/// A custom pipeline primitive.
Pipeline(Pipeline),
}

impl Damage for Custom {
fn bounds(&self) -> Rectangle {
match self {
Self::Mesh(mesh) => mesh.bounds(),
Self::Shader(shader) => shader.bounds,
Self::Pipeline(pipeline) => pipeline.bounds,
}
}
}

#[derive(Clone, Debug)]
/// A custom primitive which can be used to render primitives associated with a custom pipeline.
pub struct Shader {
/// The bounds of the [`Shader`].
pub bounds: Rectangle,

/// The [`custom::Primitive`] to render.
pub primitive: Arc<dyn custom::Primitive>,
}

impl PartialEq for Shader {
fn eq(&self, other: &Self) -> bool {
self.primitive.type_id() == other.primitive.type_id()
}
}
Loading
Loading