Perform major code refactor

The rendering is moved from the renderer to the sprites. As a result,
WgpuRenderer is now more of a rendering context while the renderer in
itself is the context + the sprites
This commit is contained in:
Steins7 2022-10-22 00:21:41 +02:00
parent f025bef6c9
commit d5d36cfadc
15 changed files with 807 additions and 742 deletions

12
Cargo.lock generated
View File

@ -36,9 +36,9 @@ dependencies = [
[[package]] [[package]]
name = "approx" name = "approx"
version = "0.4.0" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [ dependencies = [
"num-traits", "num-traits",
] ]
@ -158,6 +158,7 @@ dependencies = [
"pollster", "pollster",
"raw-window-handle", "raw-window-handle",
"wgpu", "wgpu",
"wgpu-core",
"wgpu-hal", "wgpu-hal",
"wgpu-types", "wgpu-types",
"winit", "winit",
@ -190,10 +191,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]] [[package]]
name = "cgmath" name = "cgmath"
version = "0.18.0" version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
dependencies = [ dependencies = [
"approx", "approx",
"bytemuck",
"num-traits", "num-traits",
] ]
@ -1443,9 +1443,9 @@ dependencies = [
[[package]] [[package]]
name = "wgpu-core" name = "wgpu-core"
version = "0.13.1" version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266ca6be6004fd1b2a768023b1cb0afbf7af0cbffaba19af25c5792d44e74784" checksum = "89b92788dec9d0c1bed849a1b83f01b2ee12819bf04a79c90f68e4173f7b5ba2"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bit-vec", "bit-vec",

View File

@ -6,9 +6,9 @@ use raw_window_handle::HasRawWindowHandle;
use crate::{ use crate::{
io::{Key, Scroll}, io::{Key, Scroll},
sprite::{Sprite, TextureSprite, TextSprite, ShapeSprite}, sprite::{Sprite, TextureSprite, TextSprite, ShapeSprite},
texture::Texture, texture::TextureHandle,
utils::{Size, Pixel, Position, Color}, utils::{Size, Pixel, Position, Color},
renderer::WgpuRenderer, renderer::{WgpuRenderer, Texture},
}; };
use std::{ use std::{
@ -16,6 +16,7 @@ use std::{
cell::RefCell, cell::RefCell,
}; };
//--Application trait------------------------------------------------------------------------------- //--Application trait-------------------------------------------------------------------------------
pub trait Application<S> { pub trait Application<S> {
@ -28,13 +29,13 @@ pub trait Application<S> {
pub struct Canvas { pub struct Canvas {
renderer: WgpuRenderer, renderer: WgpuRenderer,
clear_color: Pixel, clear_color: Pixel,
default_texture: Texture, default_texture: TextureHandle,
} }
impl Canvas { impl Canvas {
pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size) pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size)
-> Result<Canvas, &'static str> -> Result<Canvas, &'static str>
{ {
let mut renderer = WgpuRenderer::create(window, size).await?; let mut renderer = WgpuRenderer::create(window, size).await?;
renderer.clear(Color::WHITE); renderer.clear(Color::WHITE);
@ -58,7 +59,8 @@ impl Canvas {
} }
}; };
Texture::create(renderer.create_gpu_texture(&rgba, img_size)) let texture = Texture::create(&renderer, &rgba, img_size);
TextureHandle::from_texture(texture)
}; };
Ok(Self { Ok(Self {
@ -71,10 +73,10 @@ impl Canvas {
//--Create functions-- //--Create functions--
pub fn create_texture_sprite(&mut self, size: Size) -> TextureSprite { pub fn create_texture_sprite(&mut self, size: Size) -> TextureSprite {
TextureSprite::create( TextureSprite::create(
self.default_texture.clone(), self.default_texture.clone(),
size, size,
&self.renderer,
self.renderer.create_texture_mesh(), self.renderer.create_texture_mesh(),
self.renderer.create_matrix_uniform(),
) )
} }
@ -86,18 +88,18 @@ impl Canvas {
unimplemented!(); unimplemented!();
} }
pub fn create_texture(&mut self, _size: Size, _background: Option<Pixel>) pub fn create_texture(&mut self, _size: Size, _background: Option<Pixel>)
-> Rc<RefCell<Texture>> -> Rc<RefCell<Texture>>
{ {
unimplemented!(); unimplemented!();
} }
pub fn create_texture_from_file(&mut self, pub fn create_texture_from_file(&mut self,
file: &'static str, file: &'static str,
_offset: Option<Position>, _offset: Option<Position>,
_size: Option<Size>, _size: Option<Size>,
_background: Option<Pixel>) _background: Option<Pixel>)
-> Result<Texture, &'static str> -> Result<TextureHandle, &'static str>
{ {
use image::{ use image::{
io::Reader, io::Reader,
@ -117,7 +119,8 @@ impl Canvas {
} }
}; };
Ok(Texture::create(self.renderer.create_gpu_texture(&rgba, img_size))) let texture = Texture::create(&self.renderer, &rgba, img_size);
Ok(TextureHandle::from_texture(texture))
} }
pub fn create_texture_from_bytes(&mut self, pub fn create_texture_from_bytes(&mut self,
@ -137,7 +140,7 @@ impl Canvas {
pub fn draw<S: Sprite>(&mut self, sprite: &mut S) { pub fn draw<S: Sprite>(&mut self, sprite: &mut S) {
//update texture //update texture
self.renderer.render(sprite); sprite.render(&mut self.renderer);
} }
pub fn set_clear_color(&mut self, color: Pixel) { pub fn set_clear_color(&mut self, color: Pixel) {

View File

@ -15,8 +15,8 @@ mod renderer;
use utils::Size; use utils::Size;
pub fn run_canvas<S, A>(title: &'static str, size: Size, _: A) -> ! pub fn run_canvas<S, A>(title: &'static str, size: Size, _: A) -> !
where where
S: 'static + Sized, S: 'static + Sized,
A: 'static + Application<S>, A: 'static + Application<S>,
{ {
@ -77,7 +77,7 @@ where
canvas.clear(); canvas.clear();
let _ = canvas.update(); let _ = canvas.update();
}, },
_ => () _ => ()
}; };
}); });

View File

@ -2,15 +2,15 @@
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use canvas::{ use canvas::{
Application, Application,
Canvas, Canvas,
texture::Texture, texture::TextureHandle,
sprite::TextureSprite, sprite::TextureSprite,
utils::Position, utils::Position,
}; };
use std::{ use std::{
time::Instant, time::Instant, arch::x86_64::_mm_sub_epi8,
}; };
fn setup_logger() -> Result<(), fern::InitError> { fn setup_logger() -> Result<(), fern::InitError> {
@ -32,15 +32,14 @@ fn setup_logger() -> Result<(), fern::InitError> {
message message
)) ))
}) })
.level(log::LevelFilter::Debug) .level(log::LevelFilter::Info)
.chain(std::io::stdout()) .chain(std::io::stdout())
.chain(fern::log_file("output.log")?) .chain(fern::log_file("output.log")?)
.apply()?; .apply()?; Ok(())
Ok(())
} }
struct ExampleState { struct ExampleState {
pub texture: Texture, pub texture: TextureHandle,
pub tex_sprite: TextureSprite, pub tex_sprite: TextureSprite,
pub sub_sprite: TextureSprite, pub sub_sprite: TextureSprite,
pub last_instant: Instant, pub last_instant: Instant,
@ -54,16 +53,14 @@ struct ExampleApp {}
impl Application<ExampleState> for ExampleApp { impl Application<ExampleState> for ExampleApp {
fn init(canvas: &mut Canvas) -> Result<ExampleState, &'static str> { fn init(canvas: &mut Canvas) -> Result<ExampleState, &'static str> {
use canvas::{ use canvas::utils::Size;
utils::{Size},
};
//// 20 x 20 sprite of a picture //// 20 x 20 sprite of a picture
let texture = canvas.create_texture_from_file("assets/camel.jpg", None, None, None) let texture = canvas.create_texture_from_file("assets/camel.jpg", None, None, None)
.unwrap(); .unwrap();
let mut tex_sprite = canvas.create_texture_sprite(Size {w: 1280, h: 720}); let mut tex_sprite = canvas.create_texture_sprite(Size {w: 1280, h: 720});
tex_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0}), 1.0); tex_sprite.set_texture(texture.clone(), Some(Position {x: 0, y: 0}), 1.0);
let mut sub_sprite = canvas.create_texture_sprite(Size {w: 200, h: 200}); let mut sub_sprite = canvas.create_texture_sprite(Size {w: 200, h: 200});
sub_sprite.set_texture(texture.clone(), Some(Position {x: 350, y: 0}), 1.0); sub_sprite.set_texture(texture.clone(), Some(Position {x: 350, y: 0}), 1.0);
@ -84,9 +81,7 @@ impl Application<ExampleState> for ExampleApp {
} }
fn tick(state: &mut ExampleState, canvas: &mut Canvas) -> Result<(), &'static str> { fn tick(state: &mut ExampleState, canvas: &mut Canvas) -> Result<(), &'static str> {
use canvas::{ use canvas::sprite::Sprite;
sprite::Sprite,
};
let now = Instant::now(); let now = Instant::now();
let elapsed = now.duration_since(state.last_instant).as_millis(); let elapsed = now.duration_since(state.last_instant).as_millis();
@ -99,10 +94,11 @@ impl Application<ExampleState> for ExampleApp {
state.last_pos.y += 1; state.last_pos.y += 1;
state.last_rot += 1.0; state.last_rot += 1.0;
state.tex_sprite.set_texture(state.texture.clone(), None, state.last_rot/100.0); state.tex_sprite.set_texture(state.texture.clone(), None, state.last_rot/100.0);
state.sub_sprite.set_texture(state.texture.clone(), state.sub_sprite.set_texture(state.texture.clone(),
Some(Position {x: state.last_offset, y: 0}), 1.0); Some(Position {x: state.last_offset, y: 0}), 1.0);
state.sub_sprite.set_position(state.last_pos); state.sub_sprite.set_position(state.last_pos);
state.sub_sprite.set_rotation(state.last_rot); state.sub_sprite.set_rotation(state.last_rot);
//state.sub_sprite.set_scale(state.last_rot/1000.0); //state.sub_sprite.set_scale(state.last_rot/1000.0);
// inputs // inputs
@ -136,7 +132,7 @@ impl Application<ExampleState> for ExampleApp {
//bas_sprite.set_color(Color::WHITE); //bas_sprite.set_color(Color::WHITE);
//canvas.draw(&bas_sprite); //canvas.draw(&bas_sprite);
//// scaled sprite of a manually drawed texture //// scaled sprite of a manually drawed texture
//let texture = canvas.create_texture(Size {w: 20, h: 20}, Some(Color::WHITE)); //let texture = canvas.create_texture(Size {w: 20, h: 20}, Some(Color::WHITE));
@ -158,8 +154,8 @@ impl Application<ExampleState> for ExampleApp {
//txt_sprite.set_alpha(255); //txt_sprite.set_alpha(255);
//txt_sprite.set_scale(0.5); //txt_sprite.set_scale(0.5);
//canvas.draw(&txt_sprite); //canvas.draw(&txt_sprite);
canvas.draw(&mut state.tex_sprite); canvas.draw(&mut state.tex_sprite);
canvas.draw(&mut state.sub_sprite); canvas.draw(&mut state.sub_sprite);
canvas.update(); canvas.update();

View File

@ -5,30 +5,33 @@ use raw_window_handle::HasRawWindowHandle;
use wgpu; use wgpu;
use crate::{ use crate::{
sprite::{Sprite, MATRIX_SIZE},
texture::{GpuTexture},
utils::{Pixel, Size}, utils::{Pixel, Size},
}; };
use std::{ pub mod utils;
rc::Rc, use utils::{TextureVertex, Mesh, GpuMesh};
cell::RefCell,
}; mod texture;
pub use texture::Texture as Texture;
mod uniform;
pub use uniform::Uniform as Uniform;
mod matrix;
pub use matrix::ModelMatrix as ModelMatrix;
//--Renderer struct--------------------------------------------------------------------------------- //--Renderer struct---------------------------------------------------------------------------------
pub struct WgpuRenderer { pub struct WgpuRenderer {
surface: wgpu::Surface, surface: wgpu::Surface,
device: wgpu::Device, pub device: wgpu::Device,
queue: wgpu::Queue, pub queue: wgpu::Queue,
config: wgpu::SurfaceConfiguration, pub config: wgpu::SurfaceConfiguration,
surface_size: Size, surface_size: Size,
output: Option<wgpu::SurfaceTexture>, pub output: Option<wgpu::SurfaceTexture>,
matrix_bind_group_layout: wgpu::BindGroupLayout, pub matrix_layout: wgpu::BindGroupLayout,
pub texture_layout: wgpu::BindGroupLayout,
texture_bind_group_layout: wgpu::BindGroupLayout,
texture_render_pipeline: wgpu::RenderPipeline,
#[allow(dead_code)] #[allow(dead_code)]
shape_render_pipeline: wgpu::RenderPipeline, shape_render_pipeline: wgpu::RenderPipeline,
@ -38,8 +41,8 @@ pub struct WgpuRenderer {
impl WgpuRenderer { impl WgpuRenderer {
pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size) pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size)
-> Result<Self, &'static str> -> Result<Self, &'static str>
{ {
if size.w == 0 || size.h == 0 { if size.w == 0 || size.h == 0 {
return Err("window has zero as at least one of its dimensions"); return Err("window has zero as at least one of its dimensions");
@ -92,118 +95,20 @@ impl WgpuRenderer {
let output = Some(surface.get_current_texture() let output = Some(surface.get_current_texture()
.map_err(|_| "Failed to create SurfaceTexture")?); .map_err(|_| "Failed to create SurfaceTexture")?);
let matrix_bind_group_layout = let matrix_layout = utils::new_matrix_layout(&device);
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let texture_layout = utils::new_texture_layout(&device);
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
],
label: Some("matrix_bind_group_layout"),
});
let texture_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
// This should match the filterable field of the
// corresponding Texture entry above.
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
label: Some("texture_bind_group_layout"),
});
let texture_render_pipeline = {
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("texture shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/texture.wgsl").into()),
});
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("texture render pipeline layout"),
bind_group_layouts: &[
&matrix_bind_group_layout,
&texture_bind_group_layout,
],
push_constant_ranges: &[],
});
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("texture render pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[
TextureVertex::desc(),
],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: config.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,//Some(wgpu::Face::Back),
// Setting this to anything other than Fill requires
// Features::NON_FILL_POLYGON_MODE
polygon_mode: wgpu::PolygonMode::Fill,
// Requires Features::DEPTH_CLIP_CONTROL
unclipped_depth: false,
// Requires Features::CONSERVATIVE_RASTERIZATION
conservative: false,
},
depth_stencil: None,
// multisampling, we don't need it
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
})
};
let shape_render_pipeline = { let shape_render_pipeline = {
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("shape shader"), label: Some("shape shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/shape.wgsl").into()), source: wgpu::ShaderSource::Wgsl(include_str!("sprite/shaders/shape.wgsl").into()),
}); });
let render_pipeline_layout = let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("shape render pipeline layout"), label: Some("shape render pipeline layout"),
bind_group_layouts: &[ bind_group_layouts: &[
&matrix_bind_group_layout, &matrix_layout,
], ],
push_constant_ranges: &[], push_constant_ranges: &[],
}); });
@ -230,24 +135,24 @@ impl WgpuRenderer {
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None, strip_index_format: None,
front_face: wgpu::FrontFace::Ccw, front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back), cull_mode: Some(wgpu::Face::Back),
// Setting this to anything other than Fill requires // Setting this to anything other than Fill requires
// Features::NON_FILL_POLYGON_MODE // Features::NON_FILL_POLYGON_MODE
polygon_mode: wgpu::PolygonMode::Fill, polygon_mode: wgpu::PolygonMode::Fill,
// Requires Features::DEPTH_CLIP_CONTROL // Requires Features::DEPTH_CLIP_CONTROL
unclipped_depth: false, unclipped_depth: false,
// Requires Features::CONSERVATIVE_RASTERIZATION // Requires Features::CONSERVATIVE_RASTERIZATION
conservative: false, conservative: false,
}, },
depth_stencil: None, depth_stencil: None,
// multisampling, we don't need it // multisampling, we don't need it
multisample: wgpu::MultisampleState { multisample: wgpu::MultisampleState {
count: 1, count: 1,
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview: None, multiview: None,
}) })
}; };
@ -275,88 +180,15 @@ impl WgpuRenderer {
config, config,
surface_size, surface_size,
output, output,
matrix_bind_group_layout, matrix_layout,
texture_bind_group_layout, texture_layout,
texture_render_pipeline,
shape_render_pipeline, shape_render_pipeline,
quad_mesh, quad_mesh,
}) })
} }
pub fn create_gpu_texture(&mut self, buf: &[u8], size: Size) -> GpuTexture {
GpuTexture::create(&self.device, &self.texture_bind_group_layout, buf, size)
}
pub fn create_texture_mesh(&self) -> GpuMesh<TextureVertex, 4, 6> { pub fn create_texture_mesh(&self) -> GpuMesh<TextureVertex, 4, 6> {
GpuMesh::create(&self.device, TEXTURE_QUAD) GpuMesh::create(&self.device, TEXTURE_QUAD)
}
pub fn create_matrix_uniform(&self) -> GpuUniform<MATRIX_SIZE> {
GpuUniform::create(&self.device, &self.matrix_bind_group_layout)
}
pub fn render<S: Sprite>(&mut self, sprite: &mut S) {
let output = match &self.output {
Some(out) => out,
None => {
self.create_output();
&self.output.as_ref().unwrap()
},
};
//TODO move that to output struct ?
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
match sprite.render_data() {
RenderData::Texture ((matrix, gpu_mesh, texture)) => {
let mut texture = texture.borrow_mut();
let matrix_bind_group = matrix.get_bind_group(&mut self.queue);
if texture.is_synced == false {
texture.update(&mut self.queue);
}
if gpu_mesh.is_synced() == false {
gpu_mesh.update(&mut self.queue);
}
let mut encoder = self.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load, store: true, },
})],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&self.texture_render_pipeline);
render_pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
render_pass.set_index_buffer(gpu_mesh.index_buffer.slice(..),
wgpu::IndexFormat::Uint16);
render_pass.set_bind_group(0, matrix_bind_group, &[]);
render_pass.set_bind_group(1, &texture.bind_group, &[]);
//render_pass.set_push_constants(
// wgpu::ShaderStages::VERTEX,
// 0,
// bytemuck::bytes_of(&(self.aspect_matrix))// * matrix.get_matrix()))
//);
render_pass.draw_indexed(0..gpu_mesh.index_number, 0, 0..1);
drop(render_pass);
self.queue.submit(std::iter::once(encoder.finish()));
},
_ => (),
}
} }
pub fn clear(&mut self, color: Pixel) { pub fn clear(&mut self, color: Pixel) {
@ -396,7 +228,7 @@ impl WgpuRenderer {
if size.w == 0 || size.h == 0 { if size.w == 0 || size.h == 0 {
panic!("window has zero as at least one of its dimensions"); panic!("window has zero as at least one of its dimensions");
} }
self.surface_size = size; self.surface_size = size;
//self.aspect_matrix = { //self.aspect_matrix = {
// use cgmath::{Basis3, Rotation3, Deg}; // use cgmath::{Basis3, Rotation3, Deg};
@ -421,7 +253,7 @@ impl WgpuRenderer {
} }
} }
fn create_output(&mut self) { pub fn create_output(&mut self) {
self.output = Some( self.output = Some(
self.surface.get_current_texture() self.surface.get_current_texture()
.map_err(|err| match err { .map_err(|err| match err {
@ -436,162 +268,7 @@ impl WgpuRenderer {
} }
} }
//--GpuMesh struct----------------------------------------------------------------------------------
pub struct GpuMesh<V, const V_NB: usize, const I_NB: usize>
where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
{
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
index_number: u32,
mesh: Mesh<V, V_NB, I_NB>,
is_synced: bool,
}
impl<V, const V_NB: usize, const I_NB: usize> GpuMesh<V, V_NB, I_NB>
where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
{
pub fn create(device: &wgpu::Device, mesh: Mesh<V, V_NB, I_NB>) -> Self {
use std::mem::size_of;
use wgpu_types::BufferUsages as Usages;
let vertex_buffer = device.create_buffer(
&wgpu_types::BufferDescriptor {
label: Some("Vertex Buffer"),
size: (V_NB * size_of::<V>()) as u64,
usage: Usages::VERTEX | Usages::COPY_DST,
mapped_at_creation: false,
}
);
let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor {
label: Some("Index Buffer"),
size: (I_NB * size_of::<u16>()) as u64,
usage: Usages::INDEX | Usages::COPY_DST,
mapped_at_creation: false,
}
);
Self {
vertex_buffer,
index_buffer,
index_number: mesh.indices.len() as u32,
mesh,
is_synced: true,
}
}
pub fn set_mesh(&mut self, mesh: Mesh<V, V_NB, I_NB>) {
self.mesh = mesh;
self.is_synced = false;
}
pub fn is_synced(&self) -> bool {
self.is_synced
}
pub fn update(&mut self, queue: &wgpu::Queue) {
queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&self.mesh.vertices));
queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&self.mesh.indices));
self.is_synced = true;
}
}
//--GpuUniform struct-------------------------------------------------------------------------------
pub struct GpuUniform<const S: usize> {
uniform: wgpu::Buffer,
bind_group: wgpu::BindGroup,
data: [u8; S],
is_synced: bool,
}
impl<const S: usize> GpuUniform<S> {
pub fn create(device: &wgpu::Device, layout: &wgpu::BindGroupLayout) -> Self {
let uniform = device.create_buffer(
&wgpu_types::BufferDescriptor {
label: Some("Uniform Buffer"),
size: S as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
}
);
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: uniform.as_entire_binding(),
}
],
label: Some("uniform_bind_group"),
});
Self {
uniform,
bind_group,
data: [0; S],
is_synced: false,
}
}
pub fn update(&mut self, data: [u8; S]) {
self.data = data;
self.is_synced = false;
}
pub fn get_bind_group(&mut self, queue: &wgpu::Queue) -> &wgpu::BindGroup {
if self.is_synced == false {
queue.write_buffer(&self.uniform, 0, &self.data);
self.is_synced = true;
}
&self.bind_group
}
}
pub enum RenderData<'a> {
Texture ((
&'a mut GpuUniform<MATRIX_SIZE>,
&'a mut GpuMesh<TextureVertex, 4, 6>,
&'a Rc<RefCell<GpuTexture>>,
)),
Shape (&'a GpuMesh<ColorVertex, 4, 6>),
}
//--Renderer struct utils--------------------------------------------------------------------------- //--Renderer struct utils---------------------------------------------------------------------------
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct TextureVertex {
pub position: [f32; 2],
pub tex_coords: [f32; 2],
}
impl TextureVertex {
const ATTRIBS: [wgpu::VertexAttribute; 2] =
wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2];
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<TextureVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &Self::ATTRIBS,
}
}
}
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct ColorVertex { pub struct ColorVertex {
@ -613,14 +290,6 @@ impl ColorVertex {
} }
} }
pub struct Mesh<V, const V_NB: usize, const I_NB: usize>
where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
{
pub vertices: [V; V_NB],
pub indices: [u16; I_NB],
}
pub const TEXTURE_QUAD: Mesh<TextureVertex, 4, 6> = Mesh { pub const TEXTURE_QUAD: Mesh<TextureVertex, 4, 6> = Mesh {
vertices: [ vertices: [
TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] }, TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] },

97
src/renderer/matrix.rs Normal file
View File

@ -0,0 +1,97 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use crate:: utils::Position;
use super::{Uniform, WgpuRenderer};
use cgmath::{
Deg,
Vector2,
Matrix3,
};
pub const MATRIX_SIZE: usize = 48;//std::mem::size_of::<Matrix3<f32>>();
//--ModelMatrix struct------------------------------------------------------------------------------
pub struct ModelMatrix {
position: Position,
rotation: Deg<f32>,
scale: f32,
uniform: Uniform<MATRIX_SIZE>,
is_synced: bool,
}
impl ModelMatrix {
pub fn new(renderer: &WgpuRenderer,
pos: Position,
rot: f32,
scale: f32)
-> Self
{
Self {
position: pos,
rotation: Deg (rot),
scale,
uniform: Uniform::create(renderer, &renderer.matrix_layout),
is_synced: false,
}
}
pub fn default(renderer: &WgpuRenderer) -> Self {
Self::new(renderer, Position::origin(), 0.0, 1.0)
}
pub fn set_position(&mut self, pos: Position) {
self.position = pos;
self.is_synced = false;
}
pub fn set_rotation(&mut self, rot: f32) {
self.rotation = Deg (rot);
self.is_synced = false;
}
pub fn set_scale(&mut self, scale: f32) {
self.scale = scale;
self.is_synced = false;
}
pub fn get_uniform(&mut self) -> &mut Uniform<MATRIX_SIZE> {
use cgmath::{Basis3, Rotation3};
if self.is_synced == false {
let pos_vec = Vector2 {
x: self.position.x as f32,
y: self.position.y as f32,
};
let rotation_mat = Matrix3::from(Basis3::from_angle_z(self.rotation));
let scale_mat = Matrix3::from_scale(self.scale);
let translation_mat = Matrix3::from_translation(pos_vec);
let aspect_mat = Matrix3::from_nonuniform_scale(1.0/1280.0, 1.0/720.0);
let matrix = aspect_mat * translation_mat * rotation_mat * scale_mat;
let mat_bytes: [u8; 36] = bytemuck::bytes_of(&matrix).try_into().unwrap();
let mut bytes = [0; 48];
for i in 0..12 {
bytes[i] = mat_bytes[i];
}
for i in 0..12 {
bytes[i+16] = mat_bytes[i+12];
}
for i in 0..12 {
bytes[i+32] = mat_bytes[i+24];
}
self.uniform.update(bytes);
self.is_synced = true;
}
&mut self.uniform
}
}

98
src/renderer/texture.rs Normal file
View File

@ -0,0 +1,98 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use crate::{
utils::{Pixel, Size},
renderer::WgpuRenderer,
};
pub struct Texture {
pub texture: wgpu::Texture,
pub bind_group: wgpu::BindGroup,
pub buffer: Vec<Pixel>,
pub size: Size,
pub is_synced: bool,
}
impl Texture {
pub fn create(renderer: &WgpuRenderer,
buffer: &[u8],
size: Size)
-> Self
{
let texture = renderer.device.create_texture(
&wgpu::TextureDescriptor {
label: None,
size: size.into(),
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
}
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = renderer.device.create_sampler(
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::Repeat,
address_mode_v: wgpu::AddressMode::Repeat,
address_mode_w: wgpu::AddressMode::Repeat,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
}
);
let bind_group = renderer.device.create_bind_group(
&wgpu::BindGroupDescriptor {
layout: &renderer.texture_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
}
],
label: Some("texture_bind_group"),
}
);
Self {
texture,
bind_group,
buffer: Vec::from(bytemuck::cast_slice(buffer)),
size,
is_synced: false,
}
}
pub fn update(&mut self, queue: &wgpu::Queue) {
queue.write_texture(
wgpu::ImageCopyTexture {
aspect: wgpu::TextureAspect::All,
texture: &self.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
bytemuck::cast_slice(&self.buffer),
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * self.size.w),
rows_per_image: std::num::NonZeroU32::new(self.size.h),
},
self.size.into(),
);
self.is_synced = true;
}
}

62
src/renderer/uniform.rs Normal file
View File

@ -0,0 +1,62 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use super::WgpuRenderer;
//--Uniform struct----------------------------------------------------------------------------------
pub struct Uniform<const S: usize> {
buffer: wgpu::Buffer,
bind_group: wgpu::BindGroup,
data: [u8; S],
is_synced: bool,
}
impl<const S: usize> Uniform<S> {
pub fn create(renderer: &WgpuRenderer, layout: &wgpu::BindGroupLayout) -> Self {
let buffer = renderer.device.create_buffer(
&wgpu_types::BufferDescriptor {
label: Some("Uniform Buffer"),
size: S as u64,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
}
);
let bind_group = renderer.device.create_bind_group(&wgpu::BindGroupDescriptor {
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}
],
label: Some("uniform_bind_group"),
});
Self {
buffer,
bind_group,
data: [0; S],
is_synced: false,
}
}
pub fn update(&mut self, data: [u8; S]) {
self.data = data;
self.is_synced = false;
}
pub fn get_bind_group(&mut self, queue: &wgpu::Queue) -> &wgpu::BindGroup {
if self.is_synced == false {
queue.write_buffer(&self.buffer, 0, &self.data);
self.is_synced = true;
}
&self.bind_group
}
}

164
src/renderer/utils.rs Normal file
View File

@ -0,0 +1,164 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
//--Bind group layouts------------------------------------------------------------------------------
/// Creates the layout bind_group used by all matrixes of the renderer
pub fn new_matrix_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
],
label: Some("matrix_bind_group_layout"),
})
}
/// Crates the layout bind group used by all textures of the renderer
pub fn new_texture_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
// This should match the filterable field of the
// corresponding Texture entry above.
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
label: Some("texture_bind_group_layout"),
})
}
//--TextureVertex struct----------------------------------------------------------------------------
//
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct TextureVertex {
pub position: [f32; 2],
pub tex_coords: [f32; 2],
}
impl TextureVertex {
const ATTRIBS: [wgpu::VertexAttribute; 2] =
wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2];
pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<TextureVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &Self::ATTRIBS,
}
}
}
//--Mesh struct-------------------------------------------------------------------------------------
pub struct Mesh<V, const V_NB: usize, const I_NB: usize>
where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
{
pub vertices: [V; V_NB],
pub indices: [u16; I_NB],
}
//--GpuMesh struct----------------------------------------------------------------------------------
pub struct GpuMesh<V, const V_NB: usize, const I_NB: usize>
where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
{
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
index_number: u32,
mesh: Mesh<V, V_NB, I_NB>,
is_synced: bool,
}
impl<V, const V_NB: usize, const I_NB: usize> GpuMesh<V, V_NB, I_NB>
where
V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable,
{
pub fn create(device: &wgpu::Device, mesh: Mesh<V, V_NB, I_NB>) -> Self {
use std::mem::size_of;
use wgpu_types::BufferUsages as Usages;
let vertex_buffer = device.create_buffer(
&wgpu_types::BufferDescriptor {
label: Some("Vertex Buffer"),
size: (V_NB * size_of::<V>()) as u64,
usage: Usages::VERTEX | Usages::COPY_DST,
mapped_at_creation: false,
}
);
let index_buffer = device.create_buffer( &wgpu_types::BufferDescriptor {
label: Some("Index Buffer"),
size: (I_NB * size_of::<u16>()) as u64,
usage: Usages::INDEX | Usages::COPY_DST,
mapped_at_creation: false,
}
);
Self {
vertex_buffer,
index_buffer,
index_number: mesh.indices.len() as u32,
mesh,
is_synced: true,
}
}
pub fn set_mesh(&mut self, mesh: Mesh<V, V_NB, I_NB>) {
self.mesh = mesh;
self.is_synced = false;
}
pub fn is_synced(&self) -> bool {
self.is_synced
}
pub fn update(&mut self, queue: &wgpu::Queue) {
queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&self.mesh.vertices));
queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&self.mesh.indices));
self.is_synced = true;
}
pub fn get_vertex_buffer_slice(&self) -> wgpu::BufferSlice {
self.vertex_buffer.slice(..)
}
pub fn get_index_buffer_slice(&self) -> wgpu::BufferSlice {
self.index_buffer.slice(..)
}
pub fn get_index_number(&self) -> u32 {
self.index_number
}
}

View File

@ -2,232 +2,24 @@
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use crate::{ use crate::{
renderer::{Mesh, GpuMesh, RenderData, TextureVertex, GpuUniform}, renderer::WgpuRenderer,
shape::Shape, shape::Shape,
texture::Texture,
utils::{Pixel, Position, Size}, utils::{Pixel, Position, Size},
}; };
use cgmath::{ use cgmath::Matrix4;
Deg,
Vector2, mod texture_sprite;
Matrix3, pub use texture_sprite::TextureSprite;
Matrix4,
};
//--Sprite trait------------------------------------------------------------------------------------ //--Sprite trait------------------------------------------------------------------------------------
pub trait Sprite { pub trait Sprite {
fn set_position(&mut self, pos: Position); fn set_position(&mut self, pos: Position);
fn set_rotation(&mut self, rot: f32); fn set_rotation(&mut self, rot: f32);
fn set_alpha(&mut self, alpha: u8); fn set_alpha(&mut self, alpha: u8);
fn set_scale(&mut self, scale: f32); fn set_scale(&mut self, scale: f32);
fn render_data(&mut self) -> RenderData; fn render(&mut self, renderer: &mut WgpuRenderer);
}
//--ModelMatrix struct------------------------------------------------------------------------------
pub struct ModelMatrix {
position: Position,
rotation: Deg<f32>,
scale: f32,
uniform: GpuUniform<MATRIX_SIZE>,
is_synced: bool,
}
impl ModelMatrix {
pub fn new(pos: Position,
rot: f32,
scale: f32,
uniform: GpuUniform<MATRIX_SIZE>)
-> Self
{
Self {
position: pos,
rotation: Deg (rot),
scale,
uniform,
is_synced: false,
}
}
pub fn default(uniform: GpuUniform<MATRIX_SIZE>) -> Self {
Self::new(Position::origin(), 0.0, 1.0, uniform)
}
pub fn set_position(&mut self, pos: Position) {
self.position = pos;
self.is_synced = false;
}
pub fn set_rotation(&mut self, rot: f32) {
self.rotation = Deg (rot);
self.is_synced = false;
}
pub fn set_scale(&mut self, scale: f32) {
self.scale = scale;
self.is_synced = false;
}
pub fn get_uniform(&mut self) -> &mut GpuUniform<MATRIX_SIZE> {
use cgmath::{Basis3, Rotation3};
if self.is_synced == false {
let pos_vec = Vector2 {
x: self.position.x as f32,
y: self.position.y as f32,
};
let rotation_mat = Matrix3::from(Basis3::from_angle_z(self.rotation));
let scale_mat = Matrix3::from_scale(self.scale);
let translation_mat = Matrix3::from_translation(pos_vec);
let aspect_mat = Matrix3::from_nonuniform_scale(1.0/1280.0, 1.0/720.0);
let matrix = aspect_mat * translation_mat * rotation_mat * scale_mat;
let mat_bytes: [u8; 36] = bytemuck::bytes_of(&matrix).try_into().unwrap();
let mut bytes = [0; 48];
for i in 0..12 {
bytes[i] = mat_bytes[i];
}
for i in 0..12 {
bytes[i+16] = mat_bytes[i+12];
}
for i in 0..12 {
bytes[i+32] = mat_bytes[i+24];
}
self.uniform.update(bytes);
self.is_synced = true;
}
&mut self.uniform
}
}
pub const MATRIX_SIZE: usize = 48;//std::mem::size_of::<Matrix3<f32>>();
//--TextureSprite struct----------------------------------------------------------------------------
pub struct TextureSprite {
matrix: ModelMatrix,
gpu_mesh: GpuMesh<TextureVertex, 4, 6>,
inner_size: Size, //TODO move to f32
texture: Texture,
texture_offset: Position,
texture_scale: f32,
}
impl TextureSprite {
pub fn create(texture: Texture,
size: Size,
gpu_mesh: GpuMesh<TextureVertex, 4, 6>,
matrix_uniform: GpuUniform<MATRIX_SIZE>)
-> Self
{
let mut sprite = Self {
matrix: ModelMatrix::default(matrix_uniform),
gpu_mesh,
inner_size: size,
texture: texture.clone(),
texture_offset: Position::origin(),
texture_scale: 1.0,
};
//generate proper mesh
//TODO improve that
sprite.set_texture(texture, None, 1.0);
sprite
}
pub fn set_texture(&mut self, texture: Texture, offset: Option<Position>, scale: f32) {
// update texture
self.texture = texture;
self.texture_scale = scale;
let size = self.texture.get_size();
// compute normalized coordinates
self.texture_offset = offset.unwrap_or(Position::origin());
let x_size = self.inner_size.w as f32 / size.w as f32 * scale;
let y_size = self.inner_size.h as f32 / size.h as f32 * scale;
let x_offset = self.texture_offset.x as f32 / size.w as f32;
let y_offset = self.texture_offset.y as f32 / size.h as f32;
// compute mesh size
let w = self.inner_size.w as f32;
let h = self.inner_size.h as f32;
// generate new sprite mesh
let mesh = Mesh {
vertices: [
TextureVertex {
position: [-w, -h],
tex_coords: [x_offset , y_offset + y_size],
},
TextureVertex {
position: [ w, -h],
tex_coords: [x_offset + x_size, y_offset + y_size],
},
TextureVertex {
position: [ w, h],
tex_coords: [x_offset + x_size, y_offset ],
},
TextureVertex {
position: [-w, h],
tex_coords: [x_offset , y_offset ],
},
],
indices: [
0, 1, 2,
0, 2, 3,
],
};
self.gpu_mesh.set_mesh(mesh);
}
pub fn set_pixel(&mut self, pos: Position, pix: Pixel) {
//TODO check pos ?
self.texture.set_pixel(self.texture_offset + pos, pix);
}
pub fn for_each<F: FnMut(&mut Pixel)>(&mut self, func: F) {
//TODO check pos ?
//TODO take scale into account
self.texture.for_each_in_area(func, self.texture_offset, self.inner_size);
}
}
impl Sprite for TextureSprite {
fn set_position(&mut self, pos: Position) {
self.matrix.set_position(pos);
}
fn set_rotation(&mut self, rot: f32) {
self.matrix.set_rotation(rot);
}
fn set_alpha(&mut self, _alpha: u8) {
unimplemented!();
}
fn set_scale(&mut self, scale: f32) {
self.matrix.set_scale(scale);
}
fn render_data(&mut self) -> RenderData {
RenderData::Texture ((
self.matrix.get_uniform(),
&mut self.gpu_mesh,
&self.texture.gpu_texture(),
))
}
} }
//--TextSprite struct------------------------------------------------------------------------------- //--TextSprite struct-------------------------------------------------------------------------------
@ -250,7 +42,7 @@ impl TextSprite {
} }
impl Sprite for TextSprite { impl Sprite for TextSprite {
fn set_position(&mut self, _pos: Position) { fn set_position(&mut self, _pos: Position) {
unimplemented!(); unimplemented!();
} }
@ -267,7 +59,7 @@ impl Sprite for TextSprite {
unimplemented!(); unimplemented!();
} }
fn render_data(&mut self) -> RenderData { fn render(&mut self, _renderer: &mut WgpuRenderer) {
todo!(); todo!();
} }
} }
@ -292,7 +84,7 @@ impl ShapeSprite {
} }
impl Sprite for ShapeSprite { impl Sprite for ShapeSprite {
fn set_position(&mut self, _pos: Position) { fn set_position(&mut self, _pos: Position) {
unimplemented!(); unimplemented!();
} }
@ -308,8 +100,8 @@ impl Sprite for ShapeSprite {
fn set_scale(&mut self, _scale: f32) { fn set_scale(&mut self, _scale: f32) {
unimplemented!(); unimplemented!();
} }
fn render_data(&mut self) -> RenderData { fn render(&mut self, _renderer: &mut WgpuRenderer) {
todo!(); todo!();
} }
} }

View File

@ -0,0 +1,271 @@
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use wgpu;
use crate::{
texture::TextureHandle,
renderer::{
utils::{TextureVertex, GpuMesh},
WgpuRenderer, ModelMatrix,
},
utils::{Size, Position, Pixel},
};
use super::Sprite;
use std::cell::RefCell;
thread_local!(static PIPELINE : RefCell<Option<wgpu::RenderPipeline>> = RefCell::new(None));
//--TextureSprite struct----------------------------------------------------------------------------
pub struct TextureSprite {
matrix: ModelMatrix,
gpu_mesh: GpuMesh<TextureVertex, 4, 6>,
inner_size: Size, //TODO move to f32
texture: TextureHandle,
texture_offset: Position,
texture_scale: f32,
}
impl TextureSprite {
pub fn create(texture: TextureHandle,
size: Size,
renderer: &WgpuRenderer,
gpu_mesh: GpuMesh<TextureVertex, 4, 6>)
-> Self
{
// initialize pipeline if needed
PIPELINE.with(|cell| {
if cell.borrow().is_none() {
cell.replace(Some(initialize_pipeline(renderer)));
}
});
let mut sprite = Self {
matrix: ModelMatrix::default(renderer),
gpu_mesh,
inner_size: size,
texture: texture.clone(),
texture_offset: Position::origin(),
texture_scale: 1.0,
};
//generate proper mesh
//TODO improve that
sprite.set_texture(texture, None, 1.0);
sprite
}
pub fn set_texture(&mut self, texture: TextureHandle, offset: Option<Position>, scale: f32) {
use crate::renderer::utils::Mesh;
// update texture
self.texture = texture;
self.texture_scale = scale;
let size = self.texture.get_size();
// compute normalized coordinates
self.texture_offset = offset.unwrap_or(Position::origin());
let x_size = self.inner_size.w as f32 / size.w as f32 * scale;
let y_size = self.inner_size.h as f32 / size.h as f32 * scale;
let x_offset = self.texture_offset.x as f32 / size.w as f32;
let y_offset = self.texture_offset.y as f32 / size.h as f32;
// compute mesh size
let w = self.inner_size.w as f32;
let h = self.inner_size.h as f32;
// generate new sprite mesh
let mesh = Mesh {
vertices: [
TextureVertex {
position: [-w, -h],
tex_coords: [x_offset , y_offset + y_size],
},
TextureVertex {
position: [ w, -h],
tex_coords: [x_offset + x_size, y_offset + y_size],
},
TextureVertex {
position: [ w, h],
tex_coords: [x_offset + x_size, y_offset ],
},
TextureVertex {
position: [-w, h],
tex_coords: [x_offset , y_offset ],
},
],
indices: [
0, 1, 2,
0, 2, 3,
],
};
self.gpu_mesh.set_mesh(mesh);
}
pub fn set_pixel(&mut self, pos: Position, pix: Pixel) {
//TODO check pos ?
self.texture.set_pixel(self.texture_offset + pos, pix);
}
pub fn for_each<F: FnMut(&mut Pixel)>(&mut self, func: F) {
//TODO check pos ?
//TODO take scale into account
self.texture.for_each_in_area(func, self.texture_offset, self.inner_size);
}
pub fn render(&mut self)
{
todo!();
}
}
fn initialize_pipeline(renderer: &WgpuRenderer) -> wgpu::RenderPipeline {
let shader = renderer.device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("texture shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/texture.wgsl").into()),
});
let render_pipeline_layout =
renderer.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("texture render pipeline layout"),
bind_group_layouts: &[
&renderer.matrix_layout,
&renderer.texture_layout,
],
push_constant_ranges: &[],
});
renderer.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("texture render pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[
TextureVertex::desc(),
],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: renderer.config.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,//Some(wgpu::Face::Back),
// Setting this to anything other than Fill requires
// Features::NON_FILL_POLYGON_MODE
polygon_mode: wgpu::PolygonMode::Fill,
// Requires Features::DEPTH_CLIP_CONTROL
unclipped_depth: false,
// Requires Features::CONSERVATIVE_RASTERIZATION
conservative: false,
},
depth_stencil: None,
// multisampling, we don't need it
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
})
}
impl Sprite for TextureSprite {
fn set_position(&mut self, pos: Position) {
self.matrix.set_position(pos);
}
fn set_rotation(&mut self, rot: f32) {
self.matrix.set_rotation(rot);
}
fn set_alpha(&mut self, _alpha: u8) {
unimplemented!();
}
fn set_scale(&mut self, scale: f32) {
self.matrix.set_scale(scale);
}
fn render(&mut self, renderer: &mut WgpuRenderer) {
let output = match &renderer.output {
Some(out) => out,
None => {
renderer.create_output();
&renderer.output.as_ref().unwrap()
},
};
//TODO move that to output struct ?
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
let texture = &self.texture.texture();
if texture.borrow().is_synced == false {
//TODO modify that
texture.borrow_mut().update(&mut renderer.queue);
}
if self.gpu_mesh.is_synced() == false {
self.gpu_mesh.update(&mut renderer.queue);
}
let mut encoder = renderer.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
PIPELINE.with(|pipeline| {
let pipeline = pipeline.borrow();
let texture_bind_group = &texture.borrow().bind_group;
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load, store: true, },
})],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(pipeline.as_ref().unwrap());
render_pass.set_vertex_buffer(0, self.gpu_mesh.get_vertex_buffer_slice());
render_pass.set_index_buffer(self.gpu_mesh.get_index_buffer_slice(),
wgpu::IndexFormat::Uint16);
render_pass.set_bind_group(0,
self.matrix.get_uniform().get_bind_group(&renderer.queue),
&[]);
render_pass.set_bind_group(1, texture_bind_group, &[]);
//render_pass.set_push_constants(
// wgpu::ShaderStages::VERTEX,
// 0,
// bytemuck::bytes_of(&(self.aspect_matrix))// * matrix.get_matrix()))
//);
render_pass.draw_indexed(0..self.gpu_mesh.get_index_number(), 0, 0..1);
drop(render_pass);
});
renderer.queue.submit(std::iter::once(encoder.finish()));
}
}

View File

@ -1,160 +1,73 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use crate::utils::{Pixel, Position, Size}; use crate::{
utils::{Pixel, Position, Size},
renderer::Texture,
};
use std::{ use std::{
rc::Rc, rc::Rc,
cell::RefCell, cell::RefCell,
}; };
//--GpuTexture struct------------------------------------------------------------------------------- //--TextureHandle struct----------------------------------------------------------------------------
pub struct GpuTexture {
pub texture: wgpu::Texture,
pub bind_group: wgpu::BindGroup,
pub buffer: Vec<Pixel>,
pub size: Size,
pub is_synced: bool,
}
impl GpuTexture {
pub fn create(device: &wgpu::Device,
layout: &wgpu::BindGroupLayout,
buffer: &[u8],
size: Size)
-> Self
{
let texture = device.create_texture(
&wgpu::TextureDescriptor {
label: None,
size: size.into(),
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
}
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler(
&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::Repeat,
address_mode_v: wgpu::AddressMode::Repeat,
address_mode_w: wgpu::AddressMode::Repeat,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
}
);
let bind_group = device.create_bind_group(
&wgpu::BindGroupDescriptor {
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
}
],
label: Some("texture_bind_group"),
}
);
Self {
texture,
bind_group,
buffer: Vec::from(bytemuck::cast_slice(buffer)),
size,
is_synced: false,
}
}
pub fn update(&mut self, queue: &wgpu::Queue) {
queue.write_texture(
wgpu::ImageCopyTexture {
aspect: wgpu::TextureAspect::All,
texture: &self.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
bytemuck::cast_slice(&self.buffer),
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * self.size.w),
rows_per_image: std::num::NonZeroU32::new(self.size.h),
},
self.size.into(),
);
self.is_synced = true;
}
}
//--Texture struct----------------------------------------------------------------------------------
#[derive(Clone)] #[derive(Clone)]
pub struct Texture { pub struct TextureHandle {
gpu_texture: Rc<RefCell<GpuTexture>>, texture: Rc<RefCell<Texture>>,
} }
impl Texture { impl TextureHandle {
pub fn create(gpu_texture: GpuTexture) -> Self { pub fn from_texture(texture: Texture) -> Self {
Self { Self {
gpu_texture: Rc::new(RefCell::new(gpu_texture)), texture: Rc::new(RefCell::new(texture)),
} }
} }
pub fn set_pixel(&mut self, pos: Position, pix: Pixel) { pub fn set_pixel(&mut self, pos: Position, pix: Pixel) {
//TODO check pos ? //TODO check pos ?
let mut gpu_texture = self.gpu_texture.borrow_mut(); let mut texture = self.texture.borrow_mut();
let width = gpu_texture.size.w; let width = texture.size.w;
gpu_texture.buffer[(pos.x + pos.y*width) as usize] = pix; texture.buffer[(pos.x + pos.y*width) as usize] = pix;
gpu_texture.is_synced = false; texture.is_synced = false;
} }
pub fn for_each<F: FnMut(&mut Pixel)>(&mut self, mut func: F) { pub fn for_each<F: FnMut(&mut Pixel)>(&mut self, mut func: F) {
let mut gpu_texture = self.gpu_texture.borrow_mut(); let mut texture = self.texture.borrow_mut();
for pix in &mut gpu_texture.buffer { for pix in &mut texture.buffer {
func(pix); func(pix);
} }
gpu_texture.is_synced = false; texture.is_synced = false;
} }
pub fn for_each_in_area<F: FnMut(&mut Pixel)>(&mut self, pub fn for_each_in_area<F: FnMut(&mut Pixel)>(&mut self,
mut func: F, mut func: F,
offset: Position, offset: Position,
size: Size) size: Size)
{ {
//TODO check offset and pos ? //TODO check offset and pos ?
let mut gpu_texture = self.gpu_texture.borrow_mut(); let mut texture = self.texture.borrow_mut();
let width = gpu_texture.size.w; let width = texture.size.w;
for x in offset.x..(offset.x + size.w) { for x in offset.x..(offset.x + size.w) {
for y in offset.y..(offset.y + size.h) { for y in offset.y..(offset.y + size.h) {
func(&mut gpu_texture.buffer[(x + y*width) as usize]); func(&mut texture.buffer[(x + y*width) as usize]);
} }
} }
gpu_texture.is_synced = false; texture.is_synced = false;
} }
pub fn gpu_texture(&self) -> &Rc<RefCell<GpuTexture>> { pub fn texture(&self) -> &Rc<RefCell<Texture>> {
//TODO improve that //TODO improve that
&self.gpu_texture &self.texture
} }
pub fn get_size(&self) -> Size { pub fn get_size(&self) -> Size {
self.gpu_texture.borrow().size self.texture.borrow().size
} }
} }