blade_renderer.rs

  1use super::{BladeBelt, BladeBeltDescriptor};
  2use crate::{PrimitiveBatch, Quad, Scene};
  3use bytemuck::{Pod, Zeroable};
  4
  5use blade_graphics as gpu;
  6use std::sync::Arc;
  7
  8const SURFACE_FRAME_COUNT: u32 = 3;
  9const MAX_FRAME_TIME_MS: u32 = 1000;
 10
 11#[repr(C)]
 12#[derive(Clone, Copy, Pod, Zeroable)]
 13struct GlobalParams {
 14    viewport_size: [f32; 2],
 15    pad: [u32; 2],
 16}
 17
 18#[derive(blade_macros::ShaderData)]
 19struct ShaderQuadsData {
 20    globals: GlobalParams,
 21    quads: gpu::BufferPiece,
 22}
 23
 24struct BladePipelines {
 25    quads: gpu::RenderPipeline,
 26}
 27
 28impl BladePipelines {
 29    fn new(gpu: &gpu::Context, surface_format: gpu::TextureFormat) -> Self {
 30        let shader = gpu.create_shader(gpu::ShaderDesc {
 31            source: include_str!("shaders.wgsl"),
 32        });
 33        shader.check_struct_size::<Quad>();
 34        let layout = <ShaderQuadsData as gpu::ShaderData>::layout();
 35        Self {
 36            quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
 37                name: "quads",
 38                data_layouts: &[&layout],
 39                vertex: shader.at("vs_quads"),
 40                primitive: gpu::PrimitiveState {
 41                    topology: gpu::PrimitiveTopology::TriangleStrip,
 42                    ..Default::default()
 43                },
 44                depth_stencil: None,
 45                fragment: shader.at("fs_quads"),
 46                color_targets: &[gpu::ColorTargetState {
 47                    format: surface_format,
 48                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
 49                    write_mask: gpu::ColorWrites::default(),
 50                }],
 51            }),
 52        }
 53    }
 54}
 55
 56pub struct BladeRenderer {
 57    gpu: Arc<gpu::Context>,
 58    command_encoder: gpu::CommandEncoder,
 59    last_sync_point: Option<gpu::SyncPoint>,
 60    pipelines: BladePipelines,
 61    instance_belt: BladeBelt,
 62    viewport_size: gpu::Extent,
 63}
 64
 65impl BladeRenderer {
 66    pub fn new(gpu: Arc<gpu::Context>, size: gpu::Extent) -> Self {
 67        let surface_format = gpu.resize(gpu::SurfaceConfig {
 68            size,
 69            usage: gpu::TextureUsage::TARGET,
 70            frame_count: SURFACE_FRAME_COUNT,
 71        });
 72        let command_encoder = gpu.create_command_encoder(gpu::CommandEncoderDesc {
 73            name: "main",
 74            buffer_count: 2,
 75        });
 76        let pipelines = BladePipelines::new(&gpu, surface_format);
 77        let instance_belt = BladeBelt::new(BladeBeltDescriptor {
 78            memory: gpu::Memory::Shared,
 79            min_chunk_size: 0x1000,
 80        });
 81        Self {
 82            gpu,
 83            command_encoder,
 84            last_sync_point: None,
 85            pipelines,
 86            instance_belt,
 87            viewport_size: size,
 88        }
 89    }
 90
 91    fn wait_for_gpu(&mut self) {
 92        if let Some(last_sp) = self.last_sync_point.take() {
 93            if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {
 94                panic!("GPU hung");
 95            }
 96        }
 97    }
 98
 99    pub fn destroy(&mut self) {
100        self.wait_for_gpu();
101        self.instance_belt.destroy(&self.gpu);
102        self.gpu.destroy_command_encoder(&mut self.command_encoder);
103    }
104
105    pub fn resize(&mut self, size: gpu::Extent) {
106        self.wait_for_gpu();
107        self.gpu.resize(gpu::SurfaceConfig {
108            size,
109            usage: gpu::TextureUsage::TARGET,
110            frame_count: SURFACE_FRAME_COUNT,
111        });
112        self.viewport_size = size;
113    }
114
115    pub fn draw(&mut self, scene: &Scene) {
116        let frame = self.gpu.acquire_frame();
117        self.command_encoder.start();
118        self.command_encoder.init_texture(frame.texture());
119
120        if let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
121            colors: &[gpu::RenderTarget {
122                view: frame.texture_view(),
123                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
124                finish_op: gpu::FinishOp::Store,
125            }],
126            depth_stencil: None,
127        }) {
128            for batch in scene.batches() {
129                match batch {
130                    PrimitiveBatch::Quads(quads) => {
131                        let instances = self.instance_belt.alloc_data(quads, &self.gpu);
132                        let mut encoder = pass.with(&self.pipelines.quads);
133                        encoder.bind(
134                            0,
135                            &ShaderQuadsData {
136                                globals: GlobalParams {
137                                    viewport_size: [
138                                        self.viewport_size.width as f32,
139                                        self.viewport_size.height as f32,
140                                    ],
141                                    pad: [0; 2],
142                                },
143                                quads: instances,
144                            },
145                        );
146                        encoder.draw(0, 4, 0, quads.len() as u32);
147                    }
148                    _ => continue,
149                }
150            }
151        }
152
153        self.command_encoder.present(frame);
154        let sync_point = self.gpu.submit(&mut self.command_encoder);
155        self.instance_belt.flush(&sync_point);
156        self.wait_for_gpu();
157        self.last_sync_point = Some(sync_point);
158    }
159}