feat(renderer): added RenderResources and Batches and filled out the Renderer trait for Renderer2D

This commit is contained in:
lisk77 2025-10-27 17:34:03 +01:00
parent 66c444371a
commit c2776e1bc4
7 changed files with 198 additions and 390 deletions

View file

@ -1,10 +1,9 @@
use wgpu::{BindGroupLayout, BufferUsages, Device};
use wgpu::util::DeviceExt;
use comet_resources::{Texture, Vertex};
use comet_log::*;
use wgpu::util::DeviceExt;
use wgpu::{BindGroupLayout, BufferUsages, Device};
pub struct DrawInfo {
name: String,
pub struct Batch {
label: String,
texture: wgpu::BindGroup,
vertex_data: Vec<Vertex>,
index_data: Vec<u16>,
@ -13,15 +12,15 @@ pub struct DrawInfo {
num_indices: u32,
}
impl DrawInfo {
impl Batch {
pub fn new(
name: String,
label: String,
device: &Device,
texture: &Texture,
texture_bind_group_layout: &BindGroupLayout,
texture_sampler: &wgpu::Sampler,
vertex_data: Vec<Vertex>,
index_data: Vec<u16>
index_data: Vec<u16>,
) -> Self {
let texture_bind = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &texture_bind_group_layout,
@ -35,11 +34,11 @@ impl DrawInfo {
resource: wgpu::BindingResource::Sampler(&texture_sampler),
},
],
label: Some(format!("{} Texture", name).as_str()),
label: Some(format!("{} Texture", label).as_str()),
});
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Vertex Buffer", &name).as_str()),
label: Some(format!("{} Vertex Buffer", &label).as_str()),
contents: bytemuck::cast_slice(&vertex_data),
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
});
@ -47,26 +46,22 @@ impl DrawInfo {
let num_indices = index_data.len() as u32;
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Index Buffer", &name).as_str()),
label: Some(format!("{} Index Buffer", &label).as_str()),
contents: bytemuck::cast_slice(&index_data),
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
});
Self {
name,
label,
texture: texture_bind,
vertex_data,
index_data,
vertex_buffer,
index_buffer,
num_indices
num_indices,
}
}
pub fn name(&self) -> &String {
&self.name
}
pub fn texture(&self) -> &wgpu::BindGroup {
&self.texture
}
@ -91,16 +86,26 @@ impl DrawInfo {
self.num_indices
}
pub fn update_vertex_buffer(&mut self, device: &Device, queue: &wgpu::Queue, vertex_data: Vec<Vertex>) {
pub fn update_vertex_buffer(
&mut self,
device: &Device,
queue: &wgpu::Queue,
vertex_data: Vec<Vertex>,
) {
let new_vertex_size = vertex_data.len() as u64 * size_of::<Vertex>() as u64;
match vertex_data == self.vertex_data {
true => {},
true => {}
false => {
match new_vertex_size > self.vertex_buffer.size() {
false => queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&vertex_data)),
false => queue.write_buffer(
&self.vertex_buffer,
0,
bytemuck::cast_slice(&vertex_data),
),
true => {
self.vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Vertex Buffer", self.name).as_str()),
self.vertex_buffer =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Vertex Buffer", self.label).as_str()),
contents: bytemuck::cast_slice(&vertex_data),
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
});
@ -111,16 +116,24 @@ impl DrawInfo {
}
}
pub fn update_index_buffer(&mut self, device: &Device, queue: &wgpu::Queue, index_data: Vec<u16>) {
pub fn update_index_buffer(
&mut self,
device: &Device,
queue: &wgpu::Queue,
index_data: Vec<u16>,
) {
let new_index_size = index_data.len() as u64 * size_of::<u16>() as u64;
match index_data == self.index_data {
true => {},
true => {}
false => {
match new_index_size > self.index_buffer.size() {
false => queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&index_data)),
false => {
queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&index_data))
}
true => {
self.index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Index Buffer", self.name).as_str()),
self.index_buffer =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Index Buffer", self.label).as_str()),
contents: bytemuck::cast_slice(&index_data),
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
});
@ -145,7 +158,7 @@ impl DrawInfo {
resource: wgpu::BindingResource::Sampler(&texture.sampler),
},
],
label: Some(format!("{} Texture Bind Group", self.name).as_str()),
label: Some(format!("{} Texture Bind Group", self.label).as_str()),
});
}
}

View file

@ -1,6 +1,7 @@
mod batch;
mod camera;
mod draw_info;
pub mod render_context;
mod render_group;
mod render_pass;
pub mod render_resources;
pub mod renderer;
pub mod renderer2d;

View file

@ -1,5 +1,6 @@
use crate::{batch::Batch, render_resources::RenderResources};
use comet_colors::Color;
use std::sync::Arc;
use std::{collections::HashMap, sync::Arc};
use winit::{dpi::PhysicalSize, window::Window};
pub struct RenderContext<'a> {
@ -10,6 +11,9 @@ pub struct RenderContext<'a> {
size: PhysicalSize<u32>,
scale_factor: f64,
clear_color: wgpu::Color,
render_pipelines: HashMap<String, wgpu::RenderPipeline>,
batches: HashMap<String, Batch>,
resources: RenderResources,
}
impl<'a> RenderContext<'a> {
@ -37,7 +41,7 @@ impl<'a> RenderContext<'a> {
required_limits: wgpu::Limits::default(),
memory_hints: Default::default(),
},
None, // Trace path
None,
))
.unwrap();
@ -77,6 +81,9 @@ impl<'a> RenderContext<'a> {
size,
scale_factor,
clear_color,
render_pipelines: HashMap::new(),
batches: HashMap::new(),
resources: RenderResources::new(),
}
}
@ -123,4 +130,20 @@ impl<'a> RenderContext<'a> {
pub fn clear_color(&self) -> wgpu::Color {
self.clear_color
}
pub fn get_pipeline(&self, label: String) -> Option<&wgpu::RenderPipeline> {
self.render_pipelines.get(&label)
}
pub fn get_batch(&self, label: String) -> Option<&Batch> {
self.batches.get(&label)
}
pub fn resources(&self) -> &RenderResources {
&self.resources
}
pub fn resources_mut(&mut self) -> &mut RenderResources {
&mut self.resources
}
}

View file

@ -1,4 +0,0 @@
pub struct RenderGroup {
pipeline: wgpu::RenderPipeline,
entities: Vec<u32>
}

View file

@ -1,338 +1,19 @@
use wgpu::{ShaderModule, BindGroup, BindGroupLayout, BufferUsages, Device, Queue, RenderPipeline, PipelineLayout, SurfaceConfiguration, TextureFormat};
use wgpu::util::DeviceExt;
use comet_resources::{Vertex, Texture};
use crate::render_context::RenderContext;
#[derive(Debug, Clone)]
pub enum RenderPassType {
Engine,
User
pub struct RenderPass {
pub name: String,
pub execute: Box<
dyn Fn(&mut RenderContext, &mut wgpu::CommandEncoder, &wgpu::TextureView) + Send + Sync,
>,
}
pub struct RenderPassInfo {
pass_name: String,
pass_type: RenderPassType,
texture_bind_group: BindGroup,
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
vertex_data: Vec<Vertex>,
index_data: Vec<u16>,
num_indices: u32,
pipeline: Option<RenderPipeline>
}
impl RenderPassInfo {
pub fn new_user_pass(
device: &Device,
pass_name: String,
texture_group_layout: &BindGroupLayout,
texture: &Texture,
shader: &ShaderModule,
vertex_data: Vec<Vertex>,
index_data: Vec<u16>,
pipeline_layout: &PipelineLayout,
config: &SurfaceConfiguration
impl RenderPass {
pub fn new(
name: String,
execute: Box<
dyn Fn(&mut RenderContext, &mut wgpu::CommandEncoder, &wgpu::TextureView) + Send + Sync,
>,
) -> Self {
let num_indices = index_data.len() as u32;
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Vertex Buffer", pass_name).as_str()),
contents: bytemuck::cast_slice(&vertex_data),
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Index Buffer", pass_name).as_str()),
contents: bytemuck::cast_slice(&index_data),
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
});
let texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &texture_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&texture.sampler),
},
],
label: Some(format!("{} Texture Bind Group", pass_name).as_str()),
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[Vertex::desc()],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: config.format,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
});
Self {
pass_name,
pass_type: RenderPassType::User,
texture_bind_group,
vertex_buffer,
index_buffer,
vertex_data,
index_data,
num_indices,
pipeline: Some(pipeline)
}
}
pub fn new_engine_pass(
device: &Device,
pass_name: String,
texture_group_layout: &BindGroupLayout,
texture: &Texture,
vertex_data: Vec<Vertex>,
index_data: Vec<u16>,
) -> Self {
let num_indices = index_data.len() as u32;
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Vertex Buffer", pass_name).as_str()),
contents: bytemuck::cast_slice(&vertex_data),
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Index Buffer", pass_name).as_str()),
contents: bytemuck::cast_slice(&index_data),
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
});
let texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &texture_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&texture.sampler),
},
],
label: Some(format!("{} Texture Bind Group", pass_name).as_str()),
});
Self {
pass_name,
pass_type: RenderPassType::Engine,
texture_bind_group,
vertex_buffer,
index_buffer,
vertex_data,
index_data,
num_indices,
pipeline: None
}
}
pub fn pass_name(&self) -> &str {
&self.pass_name
}
pub fn pass_type(&self) -> RenderPassType {
self.pass_type.clone()
}
pub fn set_shader(&mut self, device: &Device, config: &SurfaceConfiguration, pipeline_layout: &PipelineLayout, shader: &ShaderModule) {
self.pipeline = Some(device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[Vertex::desc()],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: config.format,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
}));
}
pub fn texture_bind_group(&self) -> &BindGroup {
&self.texture_bind_group
}
pub fn set_texture(&mut self, device: &Device, layout: &BindGroupLayout, texture: &Texture) {
self.texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&texture.sampler),
},
],
label: Some(format!("{} Texture Bind Group", self.pass_name).as_str()),
});
}
pub fn vertex_buffer(&self) -> &wgpu::Buffer {
&self.vertex_buffer
}
pub fn vertex_data(&self) -> &Vec<Vertex> {
&self.vertex_data
}
pub fn set_vertex_buffer(&mut self, device: &Device, queue: &Queue, vertex_data: Vec<Vertex>) {
let new_vertex_size = vertex_data.len() as u64 * size_of::<Vertex>() as u64;
match vertex_data == self.vertex_data {
true => {},
false => {
match new_vertex_size > self.vertex_buffer.size() {
false => queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&vertex_data)),
true => {
self.vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Vertex Buffer", self.pass_name).as_str()),
contents: bytemuck::cast_slice(&vertex_data),
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
});
}
}
self.vertex_data = vertex_data;
}
}
}
pub fn push_to_vertex_buffer(&mut self, device: &Device, vertex_data: &mut Vec<Vertex>) {
self.vertex_data.append(vertex_data);
self.vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Vertex Buffer", self.pass_name).as_str()),
contents: bytemuck::cast_slice(&vertex_data),
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
});
}
pub fn index_buffer(&self) -> &wgpu::Buffer {
&self.index_buffer
}
pub fn index_data(&self) -> &Vec<u16> {
&self.index_data
}
pub fn num_indices(&self) -> u32 {
self.num_indices
}
pub fn set_index_buffer(&mut self, device: &Device, queue: &Queue, index_data: Vec<u16>) {
let new_index_size = index_data.len() as u64 * size_of::<u16>() as u64;
match index_data == self.index_data {
true => {},
false => {
match new_index_size > self.index_buffer.size() {
false => queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&index_data)),
true => {
self.index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Index Buffer", self.pass_name).as_str()),
contents: bytemuck::cast_slice(&index_data),
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
});
}
}
self.num_indices = index_data.len() as u32;
self.index_data = index_data
}
}
}
pub fn push_to_index_buffer(&mut self, device: &Device, index_data: &mut Vec<u16>) {
self.index_data.append(index_data);
self.index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(format!("{} Index Buffer", self.pass_name).as_str()),
contents: bytemuck::cast_slice(&index_data),
usage: BufferUsages::INDEX | BufferUsages::COPY_DST,
});
self.num_indices = self.index_data.len() as u32;
}
pub fn pipeline(&self) -> Option<&RenderPipeline> {
self.pipeline.as_ref()
Self { name, execute }
}
}

View file

@ -0,0 +1,66 @@
use std::collections::HashMap;
pub struct RenderResources {
bind_groups: HashMap<String, Vec<wgpu::BindGroup>>,
bind_group_layouts: HashMap<String, Vec<wgpu::BindGroupLayout>>,
buffers: HashMap<String, Vec<wgpu::Buffer>>,
samplers: HashMap<String, wgpu::Sampler>,
}
impl RenderResources {
pub fn new() -> Self {
Self {
bind_groups: HashMap::new(),
bind_group_layouts: HashMap::new(),
buffers: HashMap::new(),
samplers: HashMap::new(),
}
}
pub fn get_bindgroups(&self, label: String) -> Option<&Vec<wgpu::BindGroup>> {
self.bind_groups.get(&label)
}
pub fn insert_bindgroup(&mut self, label: String, bind_group: wgpu::BindGroup) {
match self.bind_groups.get_mut(&label) {
None => {
self.bind_groups.insert(label, vec![bind_group]);
}
Some(v) => v.push(bind_group),
};
}
pub fn get_bind_group_layout(&self, label: String) -> Option<&Vec<wgpu::BindGroupLayout>> {
self.bind_group_layouts.get(&label)
}
pub fn insert_bind_group_layout(&mut self, label: String, layout: wgpu::BindGroupLayout) {
match self.bind_group_layouts.get_mut(&label) {
None => {
self.bind_group_layouts.insert(label, vec![layout]);
}
Some(v) => v.push(layout),
}
}
pub fn get_buffer(&self, label: String) -> Option<&Vec<wgpu::Buffer>> {
self.buffers.get(&label)
}
pub fn insert_buffer(&mut self, label: String, buffer: wgpu::Buffer) {
match self.buffers.get_mut(&label) {
None => {
self.buffers.insert(label, vec![buffer]);
}
Some(v) => v.push(buffer),
}
}
pub fn get_sampler(&self, label: String) -> Option<&wgpu::Sampler> {
self.samplers.get(&label)
}
pub fn insert_sampler(&mut self, label: String, sampler: wgpu::Sampler) {
self.samplers.insert(label, sampler);
}
}

View file

@ -1,5 +1,5 @@
use crate::renderer::Renderer;
use crate::{camera::CameraManager, render_context::RenderContext};
use crate::{camera::CameraManager, render_context::RenderContext, render_pass::RenderPass};
use comet_colors::Color;
use comet_resources::graphic_resource_manager::GraphicResourceManager;
use std::sync::Arc;
@ -9,6 +9,8 @@ pub struct Renderer2D<'a> {
render_context: RenderContext<'a>,
resource_manager: GraphicResourceManager,
camera_manager: CameraManager,
render_passes: Vec<RenderPass>,
last_frame_time: std::time::Instant,
delta_time: f32,
}
@ -18,6 +20,8 @@ impl<'a> Renderer for Renderer2D<'a> {
render_context: RenderContext::new(window, clear_color),
resource_manager: GraphicResourceManager::new(),
camera_manager: CameraManager::new(),
render_passes: Vec::new(),
last_frame_time: std::time::Instant::now(),
delta_time: 0.0,
}
}
@ -44,11 +48,35 @@ impl<'a> Renderer for Renderer2D<'a> {
}
fn update(&mut self) -> f32 {
todo!()
let now = std::time::Instant::now();
self.delta_time = now.duration_since(self.last_frame_time).as_secs_f32();
self.last_frame_time = now;
self.delta_time
}
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
todo!()
let output = self.render_context.surface().get_current_texture()?;
let output_view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder =
self.render_context
.device()
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
for pass in &self.render_passes {
(pass.execute)(&mut self.render_context, &mut encoder, &output_view);
}
self.render_context
.queue()
.submit(std::iter::once(encoder.finish()));
output.present();
Ok(())
}
}