Reworked wgpu buffers, updated glow side to have proper transform location storage, attempting to fix visibility modifiers, implemented some of the feedback received in initial PR.
This commit is contained in:
parent
5d0fffc626
commit
6e7b3ced0b
20 changed files with 411 additions and 417 deletions
|
|
@ -2,7 +2,7 @@ use std::{f32::consts::PI, time::Instant};
|
||||||
|
|
||||||
use iced::executor;
|
use iced::executor;
|
||||||
use iced::widget::canvas::{
|
use iced::widget::canvas::{
|
||||||
self, Cache, Canvas, Cursor, Geometry, Path, Stroke, StrokeStyle,
|
self, Cache, Canvas, Cursor, Geometry, Path, Stroke, Style,
|
||||||
};
|
};
|
||||||
use iced::{
|
use iced::{
|
||||||
Application, Command, Element, Length, Point, Rectangle, Settings,
|
Application, Command, Element, Length, Point, Rectangle, Settings,
|
||||||
|
|
@ -114,7 +114,7 @@ impl<Message> canvas::Program<Message> for Arc {
|
||||||
frame.stroke(
|
frame.stroke(
|
||||||
&path,
|
&path,
|
||||||
Stroke {
|
Stroke {
|
||||||
style: StrokeStyle::Solid(palette.text),
|
style: Style::Solid(palette.text),
|
||||||
width: 10.0,
|
width: 10.0,
|
||||||
..Stroke::default()
|
..Stroke::default()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use iced::executor;
|
use iced::executor;
|
||||||
use iced::widget::canvas::{
|
use iced::widget::canvas::{
|
||||||
Cache, Cursor, Geometry, LineCap, Path, Stroke, StrokeStyle,
|
Cache, Cursor, Geometry, LineCap, Path, Stroke, Style,
|
||||||
};
|
};
|
||||||
use iced::widget::{canvas, container};
|
use iced::widget::{canvas, container};
|
||||||
use iced::{
|
use iced::{
|
||||||
|
|
@ -111,7 +111,7 @@ impl<Message> canvas::Program<Message> for Clock {
|
||||||
let thin_stroke = || -> Stroke {
|
let thin_stroke = || -> Stroke {
|
||||||
Stroke {
|
Stroke {
|
||||||
width,
|
width,
|
||||||
style: StrokeStyle::Solid(Color::WHITE),
|
style: Style::Solid(Color::WHITE),
|
||||||
line_cap: LineCap::Round,
|
line_cap: LineCap::Round,
|
||||||
..Stroke::default()
|
..Stroke::default()
|
||||||
}
|
}
|
||||||
|
|
@ -120,7 +120,7 @@ impl<Message> canvas::Program<Message> for Clock {
|
||||||
let wide_stroke = || -> Stroke {
|
let wide_stroke = || -> Stroke {
|
||||||
Stroke {
|
Stroke {
|
||||||
width: width * 3.0,
|
width: width * 3.0,
|
||||||
style: StrokeStyle::Solid(Color::WHITE),
|
style: Style::Solid(Color::WHITE),
|
||||||
line_cap: LineCap::Round,
|
line_cap: LineCap::Round,
|
||||||
..Stroke::default()
|
..Stroke::default()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use rand::{Rng, thread_rng};
|
use rand::{Rng, thread_rng};
|
||||||
use crate::canvas::{Cursor, FillStyle, Geometry};
|
use crate::canvas::{Cursor, Geometry};
|
||||||
use iced::widget::canvas::{Cache, Fill, Frame};
|
use iced::widget::canvas::{Cache, Fill, Frame};
|
||||||
use iced::widget::{canvas, Canvas};
|
use iced::widget::{canvas, Canvas};
|
||||||
use iced::Settings;
|
use iced::Settings;
|
||||||
|
|
@ -8,6 +8,7 @@ use iced::{
|
||||||
Renderer, Size, Theme,
|
Renderer, Size, Theme,
|
||||||
};
|
};
|
||||||
use iced_graphics::gradient::Gradient;
|
use iced_graphics::gradient::Gradient;
|
||||||
|
use iced_graphics::widget::canvas::Style;
|
||||||
|
|
||||||
fn main() -> iced::Result {
|
fn main() -> iced::Result {
|
||||||
ModernArt::run(Settings {
|
ModernArt::run(Settings {
|
||||||
|
|
@ -120,7 +121,7 @@ fn generate_box(frame: &mut Frame, bounds: Size) -> bool {
|
||||||
top_left,
|
top_left,
|
||||||
size,
|
size,
|
||||||
Fill {
|
Fill {
|
||||||
style: FillStyle::Solid(random_color()),
|
style: Style::Solid(random_color()),
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -129,7 +130,7 @@ fn generate_box(frame: &mut Frame, bounds: Size) -> bool {
|
||||||
top_left,
|
top_left,
|
||||||
size,
|
size,
|
||||||
Fill {
|
Fill {
|
||||||
style: FillStyle::Gradient(&gradient(
|
style: Style::Gradient(&gradient(
|
||||||
top_left,
|
top_left,
|
||||||
Point::new(top_left.x + size.width, top_left.y + size.height)
|
Point::new(top_left.x + size.width, top_left.y + size.height)
|
||||||
)),
|
)),
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ use iced::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use crate::canvas::StrokeStyle;
|
use crate::canvas::Style;
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
SolarSystem::run(Settings {
|
SolarSystem::run(Settings {
|
||||||
|
|
@ -179,7 +179,7 @@ impl<Message> canvas::Program<Message> for State {
|
||||||
frame.stroke(
|
frame.stroke(
|
||||||
&orbit,
|
&orbit,
|
||||||
Stroke {
|
Stroke {
|
||||||
style: StrokeStyle::Solid(Color::from_rgba8(0, 153, 255, 0.1)),
|
style: Style::Solid(Color::from_rgba8(0, 153, 255, 0.1)),
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
line_dash: canvas::LineDash {
|
line_dash: canvas::LineDash {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ impl Backend {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !layer.meshes.0.is_empty() {
|
if !layer.meshes.is_empty() {
|
||||||
let scaled = transformation
|
let scaled = transformation
|
||||||
* Transformation::scale(scale_factor, scale_factor);
|
* Transformation::scale(scale_factor, scale_factor);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ uniform uint color_stops_size;
|
||||||
uniform float color_stop_offsets[MAX_STOPS];
|
uniform float color_stop_offsets[MAX_STOPS];
|
||||||
uniform vec4 color_stop_colors[MAX_STOPS];
|
uniform vec4 color_stop_colors[MAX_STOPS];
|
||||||
|
|
||||||
|
//TODO: rewrite without branching to make ALUs happy
|
||||||
void main() {
|
void main() {
|
||||||
vec2 gradient_vec = vec2(gradient_end - gradient_start);
|
vec2 gradient_vec = vec2(gradient_end - gradient_start);
|
||||||
vec2 current_vec = vec2(raw_position.xy - gradient_start);
|
vec2 current_vec = vec2(raw_position.xy - gradient_start);
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,22 @@
|
||||||
mod gradient;
|
mod gradient;
|
||||||
mod solid;
|
mod solid;
|
||||||
|
|
||||||
use crate::program::{self, Shader};
|
use crate::{program, Transformation};
|
||||||
use crate::Transformation;
|
|
||||||
use glow::HasContext;
|
use glow::HasContext;
|
||||||
use iced_graphics::layer::{Mesh, Meshes};
|
use iced_graphics::layer::{attribute_count_of, Mesh};
|
||||||
use iced_graphics::shader;
|
use iced_graphics::shader;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::triangle::gradient::GradientProgram;
|
use crate::triangle::gradient::GradientProgram;
|
||||||
use crate::triangle::solid::SolidProgram;
|
use crate::triangle::solid::SolidProgram;
|
||||||
pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
|
pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
|
||||||
|
use shader::Shader;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Pipeline {
|
pub(crate) struct Pipeline {
|
||||||
vertex_array: <glow::Context as HasContext>::VertexArray,
|
vertex_array: <glow::Context as HasContext>::VertexArray,
|
||||||
vertices: Buffer<Vertex2D>,
|
vertices: Buffer<Vertex2D>,
|
||||||
indices: Buffer<u32>,
|
indices: Buffer<u32>,
|
||||||
current_transform: Transformation,
|
|
||||||
programs: TrianglePrograms,
|
programs: TrianglePrograms,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,7 +67,6 @@ impl Pipeline {
|
||||||
vertex_array,
|
vertex_array,
|
||||||
vertices,
|
vertices,
|
||||||
indices,
|
indices,
|
||||||
current_transform: Transformation::identity(),
|
|
||||||
programs: TrianglePrograms {
|
programs: TrianglePrograms {
|
||||||
solid: SolidProgram::new(gl, shader_version),
|
solid: SolidProgram::new(gl, shader_version),
|
||||||
gradient: GradientProgram::new(gl, shader_version),
|
gradient: GradientProgram::new(gl, shader_version),
|
||||||
|
|
@ -78,7 +76,7 @@ impl Pipeline {
|
||||||
|
|
||||||
pub fn draw(
|
pub fn draw(
|
||||||
&mut self,
|
&mut self,
|
||||||
meshes: &Meshes<'_>,
|
meshes: &[Mesh<'_>],
|
||||||
gl: &glow::Context,
|
gl: &glow::Context,
|
||||||
target_height: u32,
|
target_height: u32,
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
|
|
@ -90,8 +88,8 @@ impl Pipeline {
|
||||||
gl.bind_vertex_array(Some(self.vertex_array))
|
gl.bind_vertex_array(Some(self.vertex_array))
|
||||||
}
|
}
|
||||||
|
|
||||||
//count the total number of vertices & indices we need to handle for all meshes
|
//count the total amount of vertices & indices we need to handle
|
||||||
let (total_vertices, total_indices) = meshes.attribute_count();
|
let (total_vertices, total_indices) = attribute_count_of(meshes);
|
||||||
|
|
||||||
// Then we ensure the current attribute buffers are big enough, resizing if necessary
|
// Then we ensure the current attribute buffers are big enough, resizing if necessary
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
@ -100,25 +98,25 @@ impl Pipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We upload all the vertices and indices upfront
|
// We upload all the vertices and indices upfront
|
||||||
let mut last_vertex = 0;
|
let mut vertex_offset = 0;
|
||||||
let mut last_index = 0;
|
let mut index_offset = 0;
|
||||||
|
|
||||||
for Mesh { buffers, .. } in meshes.0.iter() {
|
for mesh in meshes {
|
||||||
unsafe {
|
unsafe {
|
||||||
gl.buffer_sub_data_u8_slice(
|
gl.buffer_sub_data_u8_slice(
|
||||||
glow::ARRAY_BUFFER,
|
glow::ARRAY_BUFFER,
|
||||||
(last_vertex * std::mem::size_of::<Vertex2D>()) as i32,
|
(vertex_offset * std::mem::size_of::<Vertex2D>()) as i32,
|
||||||
bytemuck::cast_slice(&buffers.vertices),
|
bytemuck::cast_slice(&mesh.buffers.vertices),
|
||||||
);
|
);
|
||||||
|
|
||||||
gl.buffer_sub_data_u8_slice(
|
gl.buffer_sub_data_u8_slice(
|
||||||
glow::ELEMENT_ARRAY_BUFFER,
|
glow::ELEMENT_ARRAY_BUFFER,
|
||||||
(last_index * std::mem::size_of::<u32>()) as i32,
|
(index_offset * std::mem::size_of::<u32>()) as i32,
|
||||||
bytemuck::cast_slice(&buffers.indices),
|
bytemuck::cast_slice(&mesh.buffers.indices),
|
||||||
);
|
);
|
||||||
|
|
||||||
last_vertex += buffers.vertices.len();
|
vertex_offset += mesh.buffers.vertices.len();
|
||||||
last_index += buffers.indices.len();
|
index_offset += mesh.buffers.indices.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,22 +124,11 @@ impl Pipeline {
|
||||||
let mut last_vertex = 0;
|
let mut last_vertex = 0;
|
||||||
let mut last_index = 0;
|
let mut last_index = 0;
|
||||||
|
|
||||||
for (index, Mesh {
|
for mesh in meshes {
|
||||||
buffers,
|
let transform = transformation
|
||||||
origin,
|
* Transformation::translate(mesh.origin.x, mesh.origin.y);
|
||||||
clip_bounds,
|
|
||||||
shader,
|
|
||||||
}) in meshes.0.iter().enumerate()
|
|
||||||
{
|
|
||||||
let transform =
|
|
||||||
transformation * Transformation::translate(origin.x, origin.y);
|
|
||||||
|
|
||||||
if index == 0 {
|
let clip_bounds = (mesh.clip_bounds * scale_factor).snap();
|
||||||
//set initial transform uniform for both programs
|
|
||||||
self.programs.set_transforms(gl, transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
let clip_bounds = (*clip_bounds * scale_factor).snap();
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
gl.scissor(
|
gl.scissor(
|
||||||
|
|
@ -152,25 +139,25 @@ impl Pipeline {
|
||||||
clip_bounds.height as i32,
|
clip_bounds.height as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
let t = if self.current_transform != transform {
|
match mesh.shader {
|
||||||
self.current_transform = transform;
|
Shader::Solid(color) => {
|
||||||
Some(transform)
|
self.programs.solid.use_program(gl, &color, &transform);
|
||||||
} else {
|
}
|
||||||
None
|
Shader::Gradient(gradient) => {
|
||||||
};
|
self.programs.gradient.use_program(gl, &gradient, &transform);
|
||||||
|
}
|
||||||
self.use_with_shader(gl, shader, t);
|
}
|
||||||
|
|
||||||
gl.draw_elements_base_vertex(
|
gl.draw_elements_base_vertex(
|
||||||
glow::TRIANGLES,
|
glow::TRIANGLES,
|
||||||
buffers.indices.len() as i32,
|
mesh.buffers.indices.len() as i32,
|
||||||
glow::UNSIGNED_INT,
|
glow::UNSIGNED_INT,
|
||||||
(last_index * std::mem::size_of::<u32>()) as i32,
|
(last_index * std::mem::size_of::<u32>()) as i32,
|
||||||
last_vertex as i32,
|
last_vertex as i32,
|
||||||
);
|
);
|
||||||
|
|
||||||
last_vertex += buffers.vertices.len();
|
last_vertex += mesh.buffers.vertices.len();
|
||||||
last_index += buffers.indices.len();
|
last_index += mesh.buffers.indices.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,31 +167,6 @@ impl Pipeline {
|
||||||
gl.disable(glow::MULTISAMPLE);
|
gl.disable(glow::MULTISAMPLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn use_with_shader(
|
|
||||||
&mut self,
|
|
||||||
gl: &glow::Context,
|
|
||||||
shader: &shader::Shader,
|
|
||||||
transform: Option<Transformation>,
|
|
||||||
) {
|
|
||||||
match shader {
|
|
||||||
shader::Shader::Solid(color) => {
|
|
||||||
unsafe { gl.use_program(Some(self.programs.solid.program)) }
|
|
||||||
self.programs.solid.set_uniforms(gl, color, transform);
|
|
||||||
}
|
|
||||||
shader::Shader::Gradient(gradient) => {
|
|
||||||
unsafe { gl.use_program(Some(self.programs.gradient.program)) }
|
|
||||||
self.programs.gradient.set_uniforms(gl, gradient, transform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TrianglePrograms {
|
|
||||||
pub fn set_transforms(&self, gl: &glow::Context, transform: Transformation) {
|
|
||||||
update_transform(gl, self.solid.program, Some(transform));
|
|
||||||
update_transform(gl, self.gradient.program, Some(transform));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simple shader program. Uses [`triangle.vert`] for its vertex shader and only binds position
|
/// A simple shader program. Uses [`triangle.vert`] for its vertex shader and only binds position
|
||||||
|
|
@ -215,14 +177,14 @@ pub(super) fn simple_triangle_program(
|
||||||
fragment_shader: &'static str,
|
fragment_shader: &'static str,
|
||||||
) -> <glow::Context as HasContext>::Program {
|
) -> <glow::Context as HasContext>::Program {
|
||||||
unsafe {
|
unsafe {
|
||||||
let vertex_shader = Shader::vertex(
|
let vertex_shader = program::Shader::vertex(
|
||||||
gl,
|
gl,
|
||||||
shader_version,
|
shader_version,
|
||||||
include_str!("shader/common/triangle.vert"),
|
include_str!("shader/common/triangle.vert"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let fragment_shader =
|
let fragment_shader =
|
||||||
Shader::fragment(gl, shader_version, fragment_shader);
|
program::Shader::fragment(gl, shader_version, fragment_shader);
|
||||||
|
|
||||||
program::create(
|
program::create(
|
||||||
gl,
|
gl,
|
||||||
|
|
@ -232,23 +194,17 @@ pub(super) fn simple_triangle_program(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn update_transform(
|
pub fn set_transform(
|
||||||
gl: &glow::Context,
|
gl: &glow::Context,
|
||||||
program: <glow::Context as HasContext>::Program,
|
location: <glow::Context as HasContext>::UniformLocation,
|
||||||
transform: Option<Transformation>
|
transform: Transformation,
|
||||||
) {
|
) {
|
||||||
if let Some(t) = transform {
|
unsafe {
|
||||||
let transform_location =
|
gl.uniform_matrix_4_f32_slice(
|
||||||
unsafe { gl.get_uniform_location(program, "u_Transform") }
|
Some(&location),
|
||||||
.expect("Get transform location.");
|
false,
|
||||||
|
transform.as_ref()
|
||||||
unsafe {
|
);
|
||||||
gl.uniform_matrix_4_f32_slice(
|
|
||||||
Some(&transform_location),
|
|
||||||
false,
|
|
||||||
t.as_ref(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,42 @@
|
||||||
use crate::program::Version;
|
use crate::program::Version;
|
||||||
use crate::triangle::{simple_triangle_program, update_transform};
|
use crate::triangle::{simple_triangle_program, set_transform};
|
||||||
use glow::{Context, HasContext, NativeProgram};
|
use glow::{Context, HasContext, NativeProgram};
|
||||||
|
use iced_graphics::gradient::Linear;
|
||||||
use iced_graphics::gradient::Gradient;
|
use iced_graphics::gradient::Gradient;
|
||||||
use iced_graphics::widget::canvas::gradient::Linear;
|
|
||||||
use iced_graphics::Transformation;
|
use iced_graphics::Transformation;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct GradientProgram {
|
pub struct GradientProgram {
|
||||||
pub(super) program: <Context as HasContext>::Program,
|
pub program: <Context as HasContext>::Program,
|
||||||
pub(super) uniform_data: GradientUniformData,
|
pub uniform_data: GradientUniformData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GradientUniformData {
|
||||||
|
gradient: Gradient,
|
||||||
|
transform: Transformation,
|
||||||
|
uniform_locations: GradientUniformLocations,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct GradientUniformLocations {
|
||||||
|
gradient_start_location: <Context as HasContext>::UniformLocation,
|
||||||
|
gradient_end_location: <Context as HasContext>::UniformLocation,
|
||||||
|
color_stops_size_location: <Context as HasContext>::UniformLocation,
|
||||||
|
//currently the maximum number of stops is 64 due to needing to allocate the
|
||||||
|
//memory for the array of stops with a const value in GLSL
|
||||||
|
color_stops_locations: [ColorStopLocation; 64],
|
||||||
|
transform_location: <Context as HasContext>::UniformLocation,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Debug, Clone)]
|
||||||
|
struct ColorStopLocation {
|
||||||
|
color: <Context as HasContext>::UniformLocation,
|
||||||
|
offset: <Context as HasContext>::UniformLocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GradientProgram {
|
impl GradientProgram {
|
||||||
pub(super) fn new(gl: &Context, shader_version: &Version) -> Self {
|
pub fn new(gl: &Context, shader_version: &Version) -> Self {
|
||||||
let program = simple_triangle_program(
|
let program = simple_triangle_program(
|
||||||
gl,
|
gl,
|
||||||
shader_version,
|
shader_version,
|
||||||
|
|
@ -25,15 +49,17 @@ impl GradientProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_uniforms<'a>(
|
pub fn write_uniforms(
|
||||||
&mut self,
|
&mut self,
|
||||||
gl: &Context,
|
gl: &Context,
|
||||||
gradient: &Gradient,
|
gradient: &Gradient,
|
||||||
transform: Option<Transformation>,
|
transform: &Transformation,
|
||||||
) {
|
) {
|
||||||
update_transform(gl, self.program, transform);
|
if transform != &self.uniform_data.transform {
|
||||||
|
set_transform(gl, self.uniform_data.uniform_locations.transform_location, *transform);
|
||||||
|
}
|
||||||
|
|
||||||
if &self.uniform_data.current_gradient != gradient {
|
if &self.uniform_data.gradient != gradient {
|
||||||
match gradient {
|
match gradient {
|
||||||
Gradient::Linear(linear) => {
|
Gradient::Linear(linear) => {
|
||||||
let gradient_start: [f32; 2] = (linear.start).into();
|
let gradient_start: [f32; 2] = (linear.start).into();
|
||||||
|
|
@ -104,31 +130,17 @@ impl GradientProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.uniform_data.current_gradient = gradient.clone();
|
self.uniform_data.gradient = gradient.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub fn use_program(&mut self, gl: &glow::Context, gradient: &Gradient, transform: &Transformation) {
|
||||||
pub(super) struct GradientUniformData {
|
unsafe {
|
||||||
current_gradient: Gradient,
|
gl.use_program(Some(self.program))
|
||||||
uniform_locations: GradientUniformLocations,
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
self.write_uniforms(gl, gradient, transform);
|
||||||
struct GradientUniformLocations {
|
}
|
||||||
gradient_start_location: <Context as HasContext>::UniformLocation,
|
|
||||||
gradient_end_location: <Context as HasContext>::UniformLocation,
|
|
||||||
color_stops_size_location: <Context as HasContext>::UniformLocation,
|
|
||||||
//currently the maximum number of stops is 64 due to needing to allocate the
|
|
||||||
//memory for the array of stops with a const value in GLSL
|
|
||||||
color_stops_locations: [ColorStopLocation; 64],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Debug, Clone)]
|
|
||||||
struct ColorStopLocation {
|
|
||||||
color: <Context as HasContext>::UniformLocation,
|
|
||||||
offset: <Context as HasContext>::UniformLocation,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GradientUniformData {
|
impl GradientUniformData {
|
||||||
|
|
@ -153,10 +165,7 @@ impl GradientUniformData {
|
||||||
&format!("color_stop_offsets[{}]", index),
|
&format!("color_stop_offsets[{}]", index),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.expect(&format!(
|
.expect("Gradient - Color stop offset location.");
|
||||||
"Gradient - Color stop offset with index {}",
|
|
||||||
index
|
|
||||||
));
|
|
||||||
|
|
||||||
let color = unsafe {
|
let color = unsafe {
|
||||||
gl.get_uniform_location(
|
gl.get_uniform_location(
|
||||||
|
|
@ -164,25 +173,28 @@ impl GradientUniformData {
|
||||||
&format!("color_stop_colors[{}]", index),
|
&format!("color_stop_colors[{}]", index),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.expect(&format!(
|
.expect("Gradient - Color stop color location.");
|
||||||
"Gradient - Color stop colors with index {}",
|
|
||||||
index
|
|
||||||
));
|
|
||||||
|
|
||||||
ColorStopLocation { color, offset }
|
ColorStopLocation { color, offset }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let transform_location =
|
||||||
|
unsafe { gl.get_uniform_location(program, "u_Transform") }
|
||||||
|
.expect("Get transform location.");
|
||||||
|
|
||||||
GradientUniformData {
|
GradientUniformData {
|
||||||
current_gradient: Gradient::Linear(Linear {
|
gradient: Gradient::Linear(Linear {
|
||||||
start: Default::default(),
|
start: Default::default(),
|
||||||
end: Default::default(),
|
end: Default::default(),
|
||||||
color_stops: vec![],
|
color_stops: vec![],
|
||||||
}),
|
}),
|
||||||
|
transform: Transformation::identity(),
|
||||||
uniform_locations: GradientUniformLocations {
|
uniform_locations: GradientUniformLocations {
|
||||||
gradient_start_location,
|
gradient_start_location,
|
||||||
gradient_end_location,
|
gradient_end_location,
|
||||||
color_stops_size_location,
|
color_stops_size_location,
|
||||||
color_stops_locations,
|
color_stops_locations,
|
||||||
|
transform_location,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,38 @@
|
||||||
use crate::program::Version;
|
use crate::program::Version;
|
||||||
use crate::triangle::{simple_triangle_program, update_transform};
|
use crate::triangle::{set_transform, simple_triangle_program};
|
||||||
use crate::Color;
|
use crate::Color;
|
||||||
use glow::{Context, HasContext, NativeProgram};
|
use glow::{Context, HasContext, NativeProgram};
|
||||||
use iced_graphics::Transformation;
|
use iced_graphics::Transformation;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SolidProgram {
|
pub struct SolidProgram {
|
||||||
pub(crate) program: <Context as HasContext>::Program,
|
program: <Context as HasContext>::Program,
|
||||||
pub(crate) uniform_data: SolidUniformData,
|
uniform_data: SolidUniformData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct SolidUniformData {
|
||||||
|
pub color: Color,
|
||||||
|
pub color_location: <Context as HasContext>::UniformLocation,
|
||||||
|
pub transform: Transformation,
|
||||||
|
pub transform_location: <Context as HasContext>::UniformLocation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SolidUniformData {
|
||||||
|
fn new(gl: &Context, program: NativeProgram) -> Self {
|
||||||
|
Self {
|
||||||
|
color: Color::TRANSPARENT,
|
||||||
|
color_location: unsafe {
|
||||||
|
gl.get_uniform_location(program, "color")
|
||||||
|
}
|
||||||
|
.expect("Solid - Color uniform location."),
|
||||||
|
transform: Transformation::identity(),
|
||||||
|
transform_location: unsafe {
|
||||||
|
gl.get_uniform_location(program, "u_Transform")
|
||||||
|
}
|
||||||
|
.expect("Get transform location."),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SolidProgram {
|
impl SolidProgram {
|
||||||
|
|
@ -24,15 +49,17 @@ impl SolidProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_uniforms<'a>(
|
pub fn write_uniforms(
|
||||||
&mut self,
|
&mut self,
|
||||||
gl: &Context,
|
gl: &Context,
|
||||||
color: &Color,
|
color: &Color,
|
||||||
transform: Option<Transformation>,
|
transform: &Transformation,
|
||||||
) {
|
) {
|
||||||
update_transform(gl, self.program, transform);
|
if transform != &self.uniform_data.transform {
|
||||||
|
set_transform(gl, self.uniform_data.transform_location, *transform)
|
||||||
|
}
|
||||||
|
|
||||||
if &self.uniform_data.color != color {
|
if color != &self.uniform_data.color {
|
||||||
unsafe {
|
unsafe {
|
||||||
gl.uniform_4_f32(
|
gl.uniform_4_f32(
|
||||||
Some(&self.uniform_data.color_location),
|
Some(&self.uniform_data.color_location),
|
||||||
|
|
@ -46,22 +73,11 @@ impl SolidProgram {
|
||||||
self.uniform_data.color = *color;
|
self.uniform_data.color = *color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub fn use_program(&mut self, gl: &glow::Context, color: &Color, transform: &Transformation) {
|
||||||
pub(crate) struct SolidUniformData {
|
unsafe {
|
||||||
pub color: Color,
|
gl.use_program(Some(self.program))
|
||||||
pub color_location: <Context as HasContext>::UniformLocation,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SolidUniformData {
|
|
||||||
fn new(gl: &Context, program: NativeProgram) -> Self {
|
|
||||||
Self {
|
|
||||||
color: Color::TRANSPARENT,
|
|
||||||
color_location: unsafe {
|
|
||||||
gl.get_uniform_location(program, "color")
|
|
||||||
}
|
|
||||||
.expect("Solid - Color uniform location."),
|
|
||||||
}
|
}
|
||||||
|
self.write_uniforms(gl, color, transform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! For creating a Gradient.
|
//! For creating a Gradient.
|
||||||
use iced_native::Color;
|
use iced_native::Color;
|
||||||
use crate::gradient::linear::Linear;
|
pub use crate::gradient::linear::Linear;
|
||||||
use crate::Point;
|
use crate::Point;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ pub struct Layer<'a> {
|
||||||
pub quads: Vec<Quad>,
|
pub quads: Vec<Quad>,
|
||||||
|
|
||||||
/// The triangle meshes of the [`Layer`].
|
/// The triangle meshes of the [`Layer`].
|
||||||
pub meshes: Meshes<'a>,
|
pub meshes: Vec<Mesh<'a>>,
|
||||||
|
|
||||||
/// The text of the [`Layer`].
|
/// The text of the [`Layer`].
|
||||||
pub text: Vec<Text<'a>>,
|
pub text: Vec<Text<'a>>,
|
||||||
|
|
@ -34,7 +34,7 @@ impl<'a> Layer<'a> {
|
||||||
Self {
|
Self {
|
||||||
bounds,
|
bounds,
|
||||||
quads: Vec::new(),
|
quads: Vec::new(),
|
||||||
meshes: Meshes(Vec::new()),
|
meshes: Vec::new(),
|
||||||
text: Vec::new(),
|
text: Vec::new(),
|
||||||
images: Vec::new(),
|
images: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +174,7 @@ impl<'a> Layer<'a> {
|
||||||
|
|
||||||
// Only draw visible content
|
// Only draw visible content
|
||||||
if let Some(clip_bounds) = layer.bounds.intersection(&bounds) {
|
if let Some(clip_bounds) = layer.bounds.intersection(&bounds) {
|
||||||
layer.meshes.0.push(
|
layer.meshes.push(
|
||||||
Mesh {
|
Mesh {
|
||||||
origin: Point::new(translation.x, translation.y),
|
origin: Point::new(translation.x, translation.y),
|
||||||
buffers,
|
buffers,
|
||||||
|
|
@ -335,20 +335,14 @@ unsafe impl bytemuck::Zeroable for Quad {}
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe impl bytemuck::Pod for Quad {}
|
unsafe impl bytemuck::Pod for Quad {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// Returns the number of total vertices & total indices of all [`Mesh`]es.
|
||||||
/// A collection of meshes.
|
pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> (usize, usize) {
|
||||||
pub struct Meshes<'a>(pub Vec<Mesh<'a>>);
|
meshes
|
||||||
|
.iter()
|
||||||
impl<'a> Meshes<'a> {
|
.map(|Mesh { buffers, .. }| {
|
||||||
/// Returns the number of total vertices & total indices of all [`Mesh`]es.
|
(buffers.vertices.len(), buffers.indices.len())
|
||||||
pub fn attribute_count(&self) -> (usize, usize) {
|
})
|
||||||
self.0
|
.fold((0, 0), |(total_v, total_i), (v, i)| {
|
||||||
.iter()
|
(total_v + v, total_i + i)
|
||||||
.map(|Mesh { buffers, .. }| {
|
})
|
||||||
(buffers.vertices.len(), buffers.indices.len())
|
|
||||||
})
|
|
||||||
.fold((0, 0), |(total_v, total_i), (v, i)| {
|
|
||||||
(total_v + v, total_i + i)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -19,12 +19,12 @@ mod text;
|
||||||
pub use cache::Cache;
|
pub use cache::Cache;
|
||||||
pub use cursor::Cursor;
|
pub use cursor::Cursor;
|
||||||
pub use event::Event;
|
pub use event::Event;
|
||||||
pub use fill::{Fill, FillRule, FillStyle};
|
pub use fill::{Fill, FillRule, Style};
|
||||||
pub use frame::Frame;
|
pub use frame::Frame;
|
||||||
pub use geometry::Geometry;
|
pub use geometry::Geometry;
|
||||||
pub use path::Path;
|
pub use path::Path;
|
||||||
pub use program::Program;
|
pub use program::Program;
|
||||||
pub use stroke::{LineCap, LineDash, LineJoin, Stroke, StrokeStyle};
|
pub use stroke::{LineCap, LineDash, LineJoin, Stroke};
|
||||||
pub use text::Text;
|
pub use text::Text;
|
||||||
|
|
||||||
use crate::{Backend, Primitive, Renderer};
|
use crate::{Backend, Primitive, Renderer};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ pub struct Fill<'a> {
|
||||||
/// The color or gradient of the fill.
|
/// The color or gradient of the fill.
|
||||||
///
|
///
|
||||||
/// By default, it is set to [`FillStyle::Solid`] `BLACK`.
|
/// By default, it is set to [`FillStyle::Solid`] `BLACK`.
|
||||||
pub style: FillStyle<'a>,
|
pub style: Style<'a>,
|
||||||
|
|
||||||
/// The fill rule defines how to determine what is inside and what is
|
/// The fill rule defines how to determine what is inside and what is
|
||||||
/// outside of a shape.
|
/// outside of a shape.
|
||||||
|
|
@ -24,7 +24,7 @@ pub struct Fill<'a> {
|
||||||
impl <'a> Default for Fill<'a> {
|
impl <'a> Default for Fill<'a> {
|
||||||
fn default() -> Fill<'a> {
|
fn default() -> Fill<'a> {
|
||||||
Fill {
|
Fill {
|
||||||
style: FillStyle::Solid(Color::BLACK),
|
style: Style::Solid(Color::BLACK),
|
||||||
rule: FillRule::NonZero,
|
rule: FillRule::NonZero,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ impl <'a> Default for Fill<'a> {
|
||||||
impl<'a> From<Color> for Fill<'a> {
|
impl<'a> From<Color> for Fill<'a> {
|
||||||
fn from(color: Color) -> Fill<'a> {
|
fn from(color: Color) -> Fill<'a> {
|
||||||
Fill {
|
Fill {
|
||||||
style: FillStyle::Solid(color),
|
style: Style::Solid(color),
|
||||||
..Fill::default()
|
..Fill::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -41,18 +41,18 @@ impl<'a> From<Color> for Fill<'a> {
|
||||||
|
|
||||||
/// The color or gradient of a [`Fill`].
|
/// The color or gradient of a [`Fill`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum FillStyle<'a> {
|
pub enum Style<'a> {
|
||||||
/// A solid color
|
/// A solid color
|
||||||
Solid(Color),
|
Solid(Color),
|
||||||
/// A color gradient
|
/// A color gradient
|
||||||
Gradient(&'a Gradient),
|
Gradient(&'a Gradient),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> Into<Shader> for FillStyle<'a> {
|
impl <'a> Into<Shader> for Style<'a> {
|
||||||
fn into(self) -> Shader {
|
fn into(self) -> Shader {
|
||||||
match self {
|
match self {
|
||||||
FillStyle::Solid(color) => Shader::Solid(color),
|
Style::Solid(color) => Shader::Solid(color),
|
||||||
FillStyle::Gradient(gradient) => gradient.clone().into()
|
Style::Gradient(gradient) => gradient.clone().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ pub struct Stroke<'a> {
|
||||||
/// The color or gradient of the stroke.
|
/// The color or gradient of the stroke.
|
||||||
///
|
///
|
||||||
/// By default, it is set to [`StrokeStyle::Solid`] `BLACK`.
|
/// By default, it is set to [`StrokeStyle::Solid`] `BLACK`.
|
||||||
pub style: StrokeStyle<'a>,
|
pub style: Style<'a>,
|
||||||
/// The distance between the two edges of the stroke.
|
/// The distance between the two edges of the stroke.
|
||||||
pub width: f32,
|
pub width: f32,
|
||||||
/// The shape to be used at the end of open subpaths when they are stroked.
|
/// The shape to be used at the end of open subpaths when they are stroked.
|
||||||
|
|
@ -24,7 +24,7 @@ impl<'a> Stroke<'a> {
|
||||||
/// Sets the color of the [`Stroke`].
|
/// Sets the color of the [`Stroke`].
|
||||||
pub fn with_color(self, color: Color) -> Self {
|
pub fn with_color(self, color: Color) -> Self {
|
||||||
Stroke {
|
Stroke {
|
||||||
style: StrokeStyle::Solid(color),
|
style: Style::Solid(color),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ impl<'a> Stroke<'a> {
|
||||||
impl<'a> Default for Stroke<'a> {
|
impl<'a> Default for Stroke<'a> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Stroke {
|
Stroke {
|
||||||
style: StrokeStyle::Solid(Color::BLACK),
|
style: Style::Solid(Color::BLACK),
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
line_cap: LineCap::default(),
|
line_cap: LineCap::default(),
|
||||||
line_join: LineJoin::default(),
|
line_join: LineJoin::default(),
|
||||||
|
|
@ -59,18 +59,18 @@ impl<'a> Default for Stroke<'a> {
|
||||||
|
|
||||||
/// The color or gradient of a [`Stroke`].
|
/// The color or gradient of a [`Stroke`].
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum StrokeStyle<'a> {
|
pub enum Style<'a> {
|
||||||
/// A solid color
|
/// A solid color
|
||||||
Solid(Color),
|
Solid(Color),
|
||||||
/// A color gradient
|
/// A color gradient
|
||||||
Gradient(&'a Gradient),
|
Gradient(&'a Gradient),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> Into<Shader> for StrokeStyle<'a> {
|
impl <'a> Into<Shader> for Style<'a> {
|
||||||
fn into(self) -> Shader {
|
fn into(self) -> Shader {
|
||||||
match self {
|
match self {
|
||||||
StrokeStyle::Solid(color) => Shader::Solid(color),
|
Style::Solid(color) => Shader::Solid(color),
|
||||||
StrokeStyle::Gradient(gradient) => gradient.clone().into()
|
Style::Gradient(gradient) => gradient.clone().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ impl Backend {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !layer.meshes.0.is_empty() {
|
if !layer.meshes.is_empty() {
|
||||||
let scaled = transformation
|
let scaled = transformation
|
||||||
* Transformation::scale(scale_factor, scale_factor);
|
* Transformation::scale(scale_factor, scale_factor);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,91 +1,124 @@
|
||||||
//! Utilities for static buffer operations.
|
//! Utilities for static buffer operations.
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
//128 triangles/indices
|
||||||
|
const DEFAULT_STATIC_BUFFER_COUNT: wgpu::BufferAddress = 128;
|
||||||
|
|
||||||
/// A generic buffer struct useful for items which have no alignment requirements
|
/// A generic buffer struct useful for items which have no alignment requirements
|
||||||
/// (e.g. Vertex, Index buffers) and are set once and never changed until destroyed.
|
/// (e.g. Vertex, Index buffers) and are set once and never changed until destroyed.
|
||||||
///
|
|
||||||
/// This buffer is mapped to the GPU on creation, so must be initialized with the correct capacity.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct StaticBuffer {
|
pub(crate) struct StaticBuffer<T> {
|
||||||
//stored sequentially per mesh iteration
|
//stored sequentially per mesh iteration; refers to the offset index in the GPU buffer
|
||||||
offsets: Vec<wgpu::BufferAddress>,
|
offsets: Vec<wgpu::BufferAddress>,
|
||||||
|
label: &'static str,
|
||||||
|
usages: wgpu::BufferUsages,
|
||||||
gpu: wgpu::Buffer,
|
gpu: wgpu::Buffer,
|
||||||
//the static size of the buffer
|
//the static size of the buffer
|
||||||
size: wgpu::BufferAddress,
|
size: wgpu::BufferAddress,
|
||||||
|
_data: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StaticBuffer {
|
impl<T: Pod + Zeroable> StaticBuffer<T> {
|
||||||
|
/// Initialize a new static buffer.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
size: u64,
|
usages: wgpu::BufferUsages,
|
||||||
usage: wgpu::BufferUsages,
|
|
||||||
total_offsets: usize,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let size = (mem::size_of::<T>() as u64) * DEFAULT_STATIC_BUFFER_COUNT;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
offsets: Vec::with_capacity(total_offsets),
|
offsets: Vec::new(),
|
||||||
gpu: device.create_buffer(&wgpu::BufferDescriptor {
|
label,
|
||||||
label: Some(label),
|
usages,
|
||||||
size,
|
gpu: Self::gpu_buffer(device, label, size, usages),
|
||||||
usage,
|
|
||||||
mapped_at_creation: true,
|
|
||||||
}),
|
|
||||||
size,
|
size,
|
||||||
|
_data: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves pending write operations & unmaps buffer from host memory.
|
fn gpu_buffer(
|
||||||
pub fn flush(&self) {
|
device: &wgpu::Device,
|
||||||
(&self.gpu).unmap();
|
label: &'static str,
|
||||||
|
size: wgpu::BufferAddress,
|
||||||
|
usage: wgpu::BufferUsages,
|
||||||
|
) -> wgpu::Buffer {
|
||||||
|
device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some(label),
|
||||||
|
size,
|
||||||
|
usage,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether or not the buffer needs to be recreated. This can happen whenever the mesh
|
/// Returns whether or not the buffer needs to be recreated. This can happen whenever mesh data
|
||||||
/// data is re-submitted.
|
/// changes & a redraw is requested.
|
||||||
pub fn needs_recreate(&self, new_size: usize) -> bool {
|
pub fn recreate_if_needed(
|
||||||
self.size != new_size as u64
|
&mut self,
|
||||||
}
|
device: &wgpu::Device,
|
||||||
|
new_count: usize,
|
||||||
|
) -> bool {
|
||||||
|
let size =
|
||||||
|
wgpu::BufferAddress::from((mem::size_of::<T>() * new_count) as u64);
|
||||||
|
|
||||||
/// Writes the current vertex data to the gpu buffer with a memcpy & stores its offset.
|
if self.size <= size {
|
||||||
pub fn write(&mut self, offset: u64, content: &[u8]) {
|
self.offsets.clear();
|
||||||
//offset has to be divisible by 8 for alignment reasons
|
self.size = size;
|
||||||
let actual_offset = if offset % 8 != 0 {
|
self.gpu = Self::gpu_buffer(device, self.label, size, self.usages);
|
||||||
offset + 4
|
true
|
||||||
} else {
|
} else {
|
||||||
offset
|
false
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut buffer = self
|
/// Writes the current vertex data to the gpu buffer if it is currently writable with a memcpy &
|
||||||
.gpu
|
/// stores its offset.
|
||||||
.slice(actual_offset..(actual_offset + content.len() as u64))
|
///
|
||||||
.get_mapped_range_mut();
|
/// This will return either the offset of the written bytes, or `None` if the GPU buffer is not
|
||||||
buffer.copy_from_slice(content);
|
/// currently writable.
|
||||||
self.offsets.push(actual_offset);
|
pub fn write(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
staging_belt: &mut wgpu::util::StagingBelt,
|
||||||
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
|
offset: u64,
|
||||||
|
content: &[T],
|
||||||
|
) -> u64 {
|
||||||
|
let bytes = bytemuck::cast_slice(content);
|
||||||
|
let bytes_size = bytes.len() as u64;
|
||||||
|
|
||||||
|
if let Some(buffer_size) = wgpu::BufferSize::new(bytes_size as u64) {
|
||||||
|
//offset has to be divisible by 8 for alignment reasons
|
||||||
|
let actual_offset = if offset % 8 != 0 { offset + 4 } else { offset };
|
||||||
|
|
||||||
|
let mut buffer = staging_belt.write_buffer(
|
||||||
|
encoder,
|
||||||
|
&self.gpu,
|
||||||
|
actual_offset,
|
||||||
|
buffer_size,
|
||||||
|
device,
|
||||||
|
);
|
||||||
|
|
||||||
|
buffer.copy_from_slice(bytes);
|
||||||
|
|
||||||
|
self.offsets.push(actual_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_size
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset_at(&self, index: usize) -> &wgpu::BufferAddress {
|
fn offset_at(&self, index: usize) -> &wgpu::BufferAddress {
|
||||||
self.offsets
|
self.offsets
|
||||||
.get(index)
|
.get(index)
|
||||||
.expect(&format!("Offset index {} is not in range.", index))
|
.expect("Offset at index does not exist.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the slice calculated from the offset stored at the given index.
|
/// Returns the slice calculated from the offset stored at the given index.
|
||||||
/// e.g. to calculate the slice for the 2nd mesh in the layer, this would be the offset at index
|
/// e.g. to calculate the slice for the 2nd mesh in the layer, this would be the offset at index
|
||||||
/// 1 that we stored earlier when writing.
|
/// 1 that we stored earlier when writing.
|
||||||
pub fn slice_from_index<T>(
|
pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> {
|
||||||
&self,
|
|
||||||
index: usize,
|
|
||||||
) -> wgpu::BufferSlice<'_> {
|
|
||||||
self.gpu.slice(self.offset_at(index)..)
|
self.gpu.slice(self.offset_at(index)..)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the current buffer doesn't exist & needs to be created, or if it's too small
|
|
||||||
/// for the new content.
|
|
||||||
pub(crate) fn needs_recreate(
|
|
||||||
buffer: &Option<StaticBuffer>,
|
|
||||||
new_size: usize,
|
|
||||||
) -> bool {
|
|
||||||
match buffer {
|
|
||||||
None => true,
|
|
||||||
Some(buf) => buf.needs_recreate(new_size),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ impl DynamicBufferType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO think about making cpu & gpu buffers optional
|
|
||||||
pub(crate) struct DynamicBuffer<T: ShaderType> {
|
pub(crate) struct DynamicBuffer<T: ShaderType> {
|
||||||
offsets: Vec<wgpu::DynamicOffset>,
|
offsets: Vec<wgpu::DynamicOffset>,
|
||||||
cpu: DynamicBufferType,
|
cpu: DynamicBufferType,
|
||||||
|
|
@ -183,7 +182,7 @@ impl<T: ShaderType + WriteInto> DynamicBuffer<T> {
|
||||||
let offset = self
|
let offset = self
|
||||||
.offsets
|
.offsets
|
||||||
.get(index)
|
.get(index)
|
||||||
.expect(&format!("Index {} not found in offsets.", index))
|
.expect("Index not found in offsets.")
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
offset
|
offset
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ use crate::{settings, Transformation};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
|
||||||
use iced_graphics::layer::Meshes;
|
use iced_graphics::layer::{attribute_count_of, Mesh};
|
||||||
use iced_graphics::shader::Shader;
|
use iced_graphics::shader::Shader;
|
||||||
use iced_graphics::Size;
|
use iced_graphics::Size;
|
||||||
|
|
||||||
use crate::buffers::buffer::{needs_recreate, StaticBuffer};
|
use crate::buffers::buffer::StaticBuffer;
|
||||||
use crate::triangle::gradient::GradientPipeline;
|
use crate::triangle::gradient::GradientPipeline;
|
||||||
use crate::triangle::solid::SolidPipeline;
|
use crate::triangle::solid::SolidPipeline;
|
||||||
pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
|
pub use iced_graphics::triangle::{Mesh2D, Vertex2D};
|
||||||
|
|
@ -20,10 +20,9 @@ mod solid;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Pipeline {
|
pub(crate) struct Pipeline {
|
||||||
blit: Option<msaa::Blit>,
|
blit: Option<msaa::Blit>,
|
||||||
// these are optional so we don't allocate any memory to the GPU if
|
vertex_buffer: StaticBuffer<Vertex2D>,
|
||||||
// application has no triangle meshes.
|
index_buffer: StaticBuffer<u32>,
|
||||||
vertex_buffer: Option<StaticBuffer>,
|
index_strides: Vec<u32>,
|
||||||
index_buffer: Option<StaticBuffer>,
|
|
||||||
pipelines: TrianglePipelines,
|
pipelines: TrianglePipelines,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,8 +68,17 @@ impl Pipeline {
|
||||||
) -> Pipeline {
|
) -> Pipeline {
|
||||||
Pipeline {
|
Pipeline {
|
||||||
blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
|
blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
|
||||||
vertex_buffer: None,
|
vertex_buffer: StaticBuffer::new(
|
||||||
index_buffer: None,
|
device,
|
||||||
|
"iced_wgpu::triangle vertex buffer",
|
||||||
|
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
),
|
||||||
|
index_buffer: StaticBuffer::new(
|
||||||
|
device,
|
||||||
|
"iced_wgpu::triangle vertex buffer",
|
||||||
|
wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
),
|
||||||
|
index_strides: Vec::new(),
|
||||||
pipelines: TrianglePipelines {
|
pipelines: TrianglePipelines {
|
||||||
solid: SolidPipeline::new(device, format, antialiasing),
|
solid: SolidPipeline::new(device, format, antialiasing),
|
||||||
gradient: GradientPipeline::new(device, format, antialiasing),
|
gradient: GradientPipeline::new(device, format, antialiasing),
|
||||||
|
|
@ -88,177 +96,152 @@ impl Pipeline {
|
||||||
target_size: Size<u32>,
|
target_size: Size<u32>,
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
meshes: &Meshes<'_>,
|
meshes: &[Mesh<'_>],
|
||||||
) {
|
) {
|
||||||
//count the total number of vertices & indices we need to handle
|
//count the total amount of vertices & indices we need to handle
|
||||||
let (total_vertices, total_indices) = meshes.attribute_count();
|
let (total_vertices, total_indices) = attribute_count_of(meshes);
|
||||||
|
|
||||||
//Only create buffers if they need to be re-sized or don't exist
|
// Then we ensure the current attribute buffers are big enough, resizing if necessary
|
||||||
if needs_recreate(&self.vertex_buffer, total_vertices) {
|
// with wgpu this means recreating the buffer.
|
||||||
//mapped to GPU at creation with total vertices
|
|
||||||
self.vertex_buffer = Some(StaticBuffer::new(
|
//We are not currently using the return value of these functions as we have no system in
|
||||||
|
//place to calculate mesh diff, or to know whether or not that would be more performant for
|
||||||
|
//the majority of use cases. Therefore we will write GPU data every frame (for now).
|
||||||
|
let _ = self.vertex_buffer.recreate_if_needed(device, total_vertices);
|
||||||
|
let _ = self.index_buffer.recreate_if_needed(device, total_indices);
|
||||||
|
|
||||||
|
//prepare dynamic buffers & data store for writing
|
||||||
|
self.index_strides.clear();
|
||||||
|
self.pipelines.clear();
|
||||||
|
|
||||||
|
let mut vertex_offset = 0;
|
||||||
|
let mut index_offset = 0;
|
||||||
|
|
||||||
|
for mesh in meshes {
|
||||||
|
let transform = transformation
|
||||||
|
* Transformation::translate(mesh.origin.x, mesh.origin.y);
|
||||||
|
|
||||||
|
//write to both buffers
|
||||||
|
let new_vertex_offset = self.vertex_buffer.write(
|
||||||
device,
|
device,
|
||||||
"iced_wgpu::triangle vertex buffer",
|
staging_belt,
|
||||||
//TODO: a more reasonable default to prevent frequent resizing calls
|
encoder,
|
||||||
// before this was 10_000
|
vertex_offset,
|
||||||
(std::mem::size_of::<Vertex2D>() * total_vertices) as u64,
|
&mesh.buffers.vertices,
|
||||||
wgpu::BufferUsages::VERTEX,
|
);
|
||||||
meshes.0.len(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
if needs_recreate(&self.index_buffer, total_indices) {
|
let new_index_offset = self.index_buffer.write(
|
||||||
//mapped to GPU at creation with total indices
|
|
||||||
self.index_buffer = Some(StaticBuffer::new(
|
|
||||||
device,
|
device,
|
||||||
"iced_wgpu::triangle index buffer",
|
staging_belt,
|
||||||
//TODO: a more reasonable default to prevent frequent resizing calls
|
encoder,
|
||||||
// before this was 10_000
|
index_offset,
|
||||||
(std::mem::size_of::<Vertex2D>() * total_indices) as u64,
|
&mesh.buffers.indices,
|
||||||
wgpu::BufferUsages::INDEX,
|
);
|
||||||
meshes.0.len(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(vertex_buffer) = &mut self.vertex_buffer {
|
vertex_offset = vertex_offset + new_vertex_offset;
|
||||||
if let Some(index_buffer) = &mut self.index_buffer {
|
index_offset = index_offset + new_index_offset;
|
||||||
let mut offset_v = 0;
|
|
||||||
let mut offset_i = 0;
|
|
||||||
//TODO: store this more efficiently
|
|
||||||
let mut indices_lengths = Vec::with_capacity(meshes.0.len());
|
|
||||||
|
|
||||||
//iterate through meshes to write all attribute data
|
self.index_strides.push(mesh.buffers.indices.len() as u32);
|
||||||
for mesh in meshes.0.iter() {
|
|
||||||
let transform = transformation
|
|
||||||
* Transformation::translate(
|
|
||||||
mesh.origin.x,
|
|
||||||
mesh.origin.y,
|
|
||||||
);
|
|
||||||
|
|
||||||
let vertices = bytemuck::cast_slice(&mesh.buffers.vertices);
|
//push uniform data to CPU buffers
|
||||||
let indices = bytemuck::cast_slice(&mesh.buffers.indices);
|
match mesh.shader {
|
||||||
|
Shader::Solid(color) => {
|
||||||
//TODO: it's (probably) more efficient to reduce this write command and
|
self.pipelines.solid.push(transform, color);
|
||||||
// iterate first and then upload
|
|
||||||
vertex_buffer.write(offset_v, vertices);
|
|
||||||
index_buffer.write(offset_i, indices);
|
|
||||||
|
|
||||||
offset_v += vertices.len() as u64;
|
|
||||||
offset_i += indices.len() as u64;
|
|
||||||
indices_lengths.push(mesh.buffers.indices.len());
|
|
||||||
|
|
||||||
match mesh.shader {
|
|
||||||
Shader::Solid(color) => {
|
|
||||||
self.pipelines.solid.push(transform, color);
|
|
||||||
}
|
|
||||||
Shader::Gradient(gradient) => {
|
|
||||||
self.pipelines.gradient.push(transform, gradient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Shader::Gradient(gradient) => {
|
||||||
|
self.pipelines.gradient.push(transform, gradient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//done writing to gpu buffer, unmap from host memory since we don't need it
|
//write uniform data to GPU
|
||||||
//anymore
|
self.pipelines.write(device, staging_belt, encoder);
|
||||||
vertex_buffer.flush();
|
|
||||||
index_buffer.flush();
|
|
||||||
|
|
||||||
//resize & memcpy uniforms from CPU buffers to GPU buffers for all pipelines
|
//configure the render pass now that the data is uploaded to the GPU
|
||||||
self.pipelines.write(device, staging_belt, encoder);
|
{
|
||||||
|
//configure antialiasing pass
|
||||||
|
let (attachment, resolve_target, load) = if let Some(blit) =
|
||||||
|
&mut self.blit
|
||||||
|
{
|
||||||
|
let (attachment, resolve_target) =
|
||||||
|
blit.targets(device, target_size.width, target_size.height);
|
||||||
|
|
||||||
//configure the render pass now that the data is uploaded to the GPU
|
(
|
||||||
{
|
attachment,
|
||||||
//configure antialiasing pass
|
Some(resolve_target),
|
||||||
let (attachment, resolve_target, load) =
|
wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
||||||
if let Some(blit) = &mut self.blit {
|
)
|
||||||
let (attachment, resolve_target) = blit.targets(
|
} else {
|
||||||
device,
|
(target, None, wgpu::LoadOp::Load)
|
||||||
target_size.width,
|
};
|
||||||
target_size.height,
|
|
||||||
);
|
|
||||||
|
|
||||||
(
|
let mut render_pass =
|
||||||
attachment,
|
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
Some(resolve_target),
|
label: Some("iced_wgpu::triangle render pass"),
|
||||||
wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
color_attachments: &[Some(
|
||||||
)
|
wgpu::RenderPassColorAttachment {
|
||||||
} else {
|
view: attachment,
|
||||||
(target, None, wgpu::LoadOp::Load)
|
resolve_target,
|
||||||
};
|
ops: wgpu::Operations { load, store: true },
|
||||||
|
|
||||||
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,
|
|
||||||
ops: wgpu::Operations { load, store: true },
|
|
||||||
},
|
|
||||||
)],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
},
|
},
|
||||||
);
|
)],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
|
||||||
//TODO: do this a better way; store it in the respective pipelines perhaps
|
//TODO I can't figure out a clean way to encapsulate these into their appropriate
|
||||||
// to be more readable
|
// structs without displeasing the borrow checker due to the lifetime requirements of
|
||||||
let mut num_solids = 0;
|
// render_pass & using a mutable reference to each pipeline in a loop...
|
||||||
let mut num_gradients = 0;
|
let mut num_solids = 0;
|
||||||
|
let mut num_gradients = 0;
|
||||||
|
|
||||||
//TODO: try to avoid this extra iteration if possible
|
for (index, mesh) in meshes.iter().enumerate() {
|
||||||
for index in 0..meshes.0.len() {
|
let clip_bounds = (mesh.clip_bounds * scale_factor).snap();
|
||||||
let clip_bounds =
|
|
||||||
(meshes.0[index].clip_bounds * scale_factor).snap();
|
|
||||||
|
|
||||||
render_pass.set_scissor_rect(
|
render_pass.set_scissor_rect(
|
||||||
clip_bounds.x,
|
clip_bounds.x,
|
||||||
clip_bounds.y,
|
clip_bounds.y,
|
||||||
clip_bounds.width,
|
clip_bounds.width,
|
||||||
clip_bounds.height,
|
clip_bounds.height,
|
||||||
);
|
);
|
||||||
|
|
||||||
match meshes.0[index].shader {
|
match mesh.shader {
|
||||||
Shader::Solid(_) => {
|
Shader::Solid(_) => {
|
||||||
self.pipelines.solid.configure_render_pass(
|
self.pipelines.solid.configure_render_pass(
|
||||||
&mut render_pass,
|
&mut render_pass,
|
||||||
num_solids,
|
num_solids,
|
||||||
);
|
|
||||||
num_solids += 1;
|
|
||||||
}
|
|
||||||
Shader::Gradient(_) => {
|
|
||||||
self.pipelines.gradient.configure_render_pass(
|
|
||||||
&mut render_pass,
|
|
||||||
num_gradients,
|
|
||||||
);
|
|
||||||
num_gradients += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render_pass.set_index_buffer(
|
|
||||||
index_buffer.slice_from_index::<u32>(index),
|
|
||||||
wgpu::IndexFormat::Uint32,
|
|
||||||
);
|
|
||||||
|
|
||||||
render_pass.set_vertex_buffer(
|
|
||||||
0,
|
|
||||||
vertex_buffer.slice_from_index::<Vertex2D>(index),
|
|
||||||
);
|
|
||||||
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..(indices_lengths[index] as u32),
|
|
||||||
0,
|
|
||||||
0..1,
|
|
||||||
);
|
);
|
||||||
|
num_solids += 1;
|
||||||
}
|
}
|
||||||
}
|
Shader::Gradient(_) => {
|
||||||
|
self.pipelines.gradient.configure_render_pass(
|
||||||
|
&mut render_pass,
|
||||||
|
num_gradients,
|
||||||
|
);
|
||||||
|
num_gradients += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render_pass.set_vertex_buffer(
|
||||||
|
0,
|
||||||
|
self.vertex_buffer.slice_from_index(index),
|
||||||
|
);
|
||||||
|
|
||||||
|
render_pass.set_index_buffer(
|
||||||
|
self.index_buffer.slice_from_index(index),
|
||||||
|
wgpu::IndexFormat::Uint32,
|
||||||
|
);
|
||||||
|
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..(self.index_strides[index] as u32),
|
||||||
|
0,
|
||||||
|
0..1,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(blit) = &mut self.blit {
|
if let Some(blit) = &mut self.blit {
|
||||||
blit.draw(encoder, target);
|
blit.draw(encoder, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
//cleanup
|
|
||||||
self.pipelines.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -253,13 +253,13 @@ impl GradientPipeline {
|
||||||
pub fn configure_render_pass<'a>(
|
pub fn configure_render_pass<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
render_pass: &mut wgpu::RenderPass<'a>,
|
render_pass: &mut wgpu::RenderPass<'a>,
|
||||||
index: usize,
|
count: usize,
|
||||||
) {
|
) {
|
||||||
render_pass.set_pipeline(&self.pipeline);
|
render_pass.set_pipeline(&self.pipeline);
|
||||||
render_pass.set_bind_group(
|
render_pass.set_bind_group(
|
||||||
0,
|
0,
|
||||||
&self.bind_group,
|
&self.bind_group,
|
||||||
&[self.uniform_buffer.offset_at_index(index)],
|
&[self.uniform_buffer.offset_at_index(count)],
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@ use encase::ShaderType;
|
||||||
use glam::Vec4;
|
use glam::Vec4;
|
||||||
use iced_graphics::Transformation;
|
use iced_graphics::Transformation;
|
||||||
|
|
||||||
pub(super) struct SolidPipeline {
|
pub struct SolidPipeline {
|
||||||
pipeline: wgpu::RenderPipeline,
|
pipeline: wgpu::RenderPipeline,
|
||||||
pub(super) buffer: DynamicBuffer<SolidUniforms>,
|
pub(crate) buffer: DynamicBuffer<SolidUniforms>,
|
||||||
bind_group_layout: wgpu::BindGroupLayout,
|
bind_group_layout: wgpu::BindGroupLayout,
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, ShaderType)]
|
#[derive(Debug, Clone, Copy, ShaderType)]
|
||||||
pub(super) struct SolidUniforms {
|
pub struct SolidUniforms {
|
||||||
transform: glam::Mat4,
|
transform: glam::Mat4,
|
||||||
color: Vec4,
|
color: Vec4,
|
||||||
}
|
}
|
||||||
|
|
@ -156,14 +156,13 @@ impl SolidPipeline {
|
||||||
pub fn configure_render_pass<'a>(
|
pub fn configure_render_pass<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
render_pass: &mut wgpu::RenderPass<'a>,
|
render_pass: &mut wgpu::RenderPass<'a>,
|
||||||
index: usize,
|
count: usize,
|
||||||
) {
|
) {
|
||||||
render_pass.set_pipeline(&self.pipeline);
|
render_pass.set_pipeline(&self.pipeline);
|
||||||
|
|
||||||
render_pass.set_bind_group(
|
render_pass.set_bind_group(
|
||||||
0,
|
0,
|
||||||
&self.bind_group,
|
&self.bind_group,
|
||||||
&[self.buffer.offset_at_index(index)],
|
&[self.buffer.offset_at_index(count)],
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue