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}