1use cvkg_core::Rect;
35use std::sync::Arc;
36
37pub use accesskit::{
40 ActionHandler, ActionRequest, ActivationHandler, DeactivationHandler,
41 Node, NodeId, Role, Tree, TreeId, TreeUpdate,
42};
43pub use accesskit_winit::Adapter as ShieldWallAdapter;
44
45
46#[repr(C)]
47#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
48pub struct Vertex {
49 pub position: [f32; 2],
50 pub uv: [f32; 2],
51 pub color: [f32; 4],
52 pub mode: u32,
53}
54
55impl Vertex {
56 const ATTRIBUTES: [wgpu::VertexAttribute; 4] = wgpu::vertex_attr_array![
57 0 => Float32x2,
58 1 => Float32x2,
59 2 => Float32x4,
60 3 => Uint32
61 ];
62
63 fn desc() -> wgpu::VertexBufferLayout<'static> {
64 wgpu::VertexBufferLayout {
65 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
66 step_mode: wgpu::VertexStepMode::Vertex,
67 attributes: &Self::ATTRIBUTES,
68 }
69 }
70}
71
72pub struct SurtrRenderer {
74 device: Arc<wgpu::Device>,
75 queue: Arc<wgpu::Queue>,
76 surface: wgpu::Surface<'static>,
77 config: wgpu::SurfaceConfiguration,
78 pipeline: wgpu::RenderPipeline,
79
80 bloom_extract_pipeline: wgpu::RenderPipeline,
82 blur_h_pipeline: wgpu::RenderPipeline,
83 blur_v_pipeline: wgpu::RenderPipeline,
84 composite_pipeline: wgpu::RenderPipeline,
85 blur_texture_a: wgpu::TextureView,
86 blur_texture_b: wgpu::TextureView,
87 blur_bind_group_a: wgpu::BindGroup,
88 blur_bind_group_b: wgpu::BindGroup,
89
90 font_system: cosmic_text::FontSystem,
92 swash_cache: cosmic_text::SwashCache,
93
94 dummy_bind_group: wgpu::BindGroup,
96
97 vertex_buffer: wgpu::Buffer,
99 index_buffer: wgpu::Buffer,
100 vertices: Vec<Vertex>,
101 indices: Vec<u16>,
102}
103
104const MAX_VERTICES: usize = 10000;
105const MAX_INDICES: usize = 15000;
106
107impl SurtrRenderer {
108 pub async fn forge(window: Arc<winit::window::Window>) -> Self {
110 let instance = wgpu::Instance::default();
111 let surface = instance.create_surface(window.clone()).unwrap();
112 let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
113 power_preference: wgpu::PowerPreference::HighPerformance,
114 compatible_surface: Some(&surface),
115 force_fallback_adapter: false,
116 }).await.expect("Failed to find a suitable GPU for Surtr");
117
118 let (device, queue) = adapter.request_device(
119 &wgpu::DeviceDescriptor {
120 label: Some("Surtr Forge"),
121 required_features: wgpu::Features::empty(),
122 required_limits: wgpu::Limits::default(),
123 },
124 None,
125 ).await.expect("Failed to create Surtr device");
126
127 let device = Arc::new(device);
128 let queue = Arc::new(queue);
129
130 let size = window.inner_size();
131 let config = surface.get_default_config(&adapter, size.width, size.height).unwrap();
132 surface.configure(&device, &config);
133
134 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
136 label: Some("Muspelheim Main Shader"),
137 source: wgpu::ShaderSource::Wgsl(include_str!("shaders.wgsl").into()),
138 });
139
140 let texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
142 entries: &[
143 wgpu::BindGroupLayoutEntry {
144 binding: 0,
145 visibility: wgpu::ShaderStages::FRAGMENT,
146 ty: wgpu::BindingType::Texture {
147 multisampled: false,
148 view_dimension: wgpu::TextureViewDimension::D2,
149 sample_type: wgpu::TextureSampleType::Float { filterable: true },
150 },
151 count: None,
152 },
153 wgpu::BindGroupLayoutEntry {
154 binding: 1,
155 visibility: wgpu::ShaderStages::FRAGMENT,
156 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
157 count: None,
158 },
159 ],
160 label: Some("Niflheim Texture Bind Group Layout"),
161 });
162
163 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
165 label: Some("Surtr Pipeline Layout"),
166 bind_group_layouts: &[&texture_bind_group_layout],
167 push_constant_ranges: &[],
168 });
169
170 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
171 label: Some("Surtr Main Pipeline"),
172 layout: Some(&pipeline_layout),
173 vertex: wgpu::VertexState {
174 module: &shader,
175 entry_point: "vs_main",
176 buffers: &[Vertex::desc()],
177 },
178 fragment: Some(wgpu::FragmentState {
179 module: &shader,
180 entry_point: "fs_main",
181 targets: &[Some(wgpu::ColorTargetState {
182 format: config.format,
183 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
184 write_mask: wgpu::ColorWrites::ALL,
185 })],
186 }),
187 primitive: wgpu::PrimitiveState::default(),
188 depth_stencil: None,
189 multisample: wgpu::MultisampleState::default(),
190 multiview: None,
191 });
192
193 let bloom_extract_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
195 label: Some("Muspelheim Bloom Extract"),
196 layout: Some(&pipeline_layout),
197 vertex: wgpu::VertexState {
198 module: &shader,
199 entry_point: "vs_fullscreen",
200 buffers: &[],
201 },
202 fragment: Some(wgpu::FragmentState {
203 module: &shader,
204 entry_point: "fs_bloom_extract",
205 targets: &[Some(wgpu::ColorTargetState {
206 format: config.format,
207 blend: None,
208 write_mask: wgpu::ColorWrites::ALL,
209 })],
210 }),
211 primitive: wgpu::PrimitiveState::default(),
212 depth_stencil: None,
213 multisample: wgpu::MultisampleState::default(),
214 multiview: None,
215 });
216
217 let blur_h_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
219 label: Some("Muspelheim Horizontal Blur"),
220 layout: Some(&pipeline_layout),
221 vertex: wgpu::VertexState {
222 module: &shader,
223 entry_point: "vs_fullscreen",
224 buffers: &[],
225 },
226 fragment: Some(wgpu::FragmentState {
227 module: &shader,
228 entry_point: "fs_blur_h",
229 targets: &[Some(wgpu::ColorTargetState {
230 format: config.format,
231 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
232 write_mask: wgpu::ColorWrites::ALL,
233 })],
234 }),
235 primitive: wgpu::PrimitiveState::default(),
236 depth_stencil: None,
237 multisample: wgpu::MultisampleState::default(),
238 multiview: None,
239 });
240
241 let blur_v_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
242 label: Some("Muspelheim Vertical Blur"),
243 layout: Some(&pipeline_layout),
244 vertex: wgpu::VertexState {
245 module: &shader,
246 entry_point: "vs_fullscreen",
247 buffers: &[],
248 },
249 fragment: Some(wgpu::FragmentState {
250 module: &shader,
251 entry_point: "fs_blur_v",
252 targets: &[Some(wgpu::ColorTargetState {
253 format: config.format,
254 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
255 write_mask: wgpu::ColorWrites::ALL,
256 })],
257 }),
258 primitive: wgpu::PrimitiveState::default(),
259 depth_stencil: None,
260 multisample: wgpu::MultisampleState::default(),
261 multiview: None,
262 });
263
264 let composite_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
266 label: Some("Muspelheim Composite"),
267 layout: Some(&pipeline_layout),
268 vertex: wgpu::VertexState {
269 module: &shader,
270 entry_point: "vs_fullscreen",
271 buffers: &[],
272 },
273 fragment: Some(wgpu::FragmentState {
274 module: &shader,
275 entry_point: "fs_composite",
276 targets: &[Some(wgpu::ColorTargetState {
277 format: config.format,
278 blend: Some(wgpu::BlendState {
280 color: wgpu::BlendComponent {
281 src_factor: wgpu::BlendFactor::One,
282 dst_factor: wgpu::BlendFactor::One,
283 operation: wgpu::BlendOperation::Add,
284 },
285 alpha: wgpu::BlendComponent {
286 src_factor: wgpu::BlendFactor::One,
287 dst_factor: wgpu::BlendFactor::One,
288 operation: wgpu::BlendOperation::Add,
289 },
290 }),
291 write_mask: wgpu::ColorWrites::ALL,
292 })],
293 }),
294 primitive: wgpu::PrimitiveState::default(),
295 depth_stencil: None,
296 multisample: wgpu::MultisampleState::default(),
297 multiview: None,
298 });
299
300 let blur_tex_desc = wgpu::TextureDescriptor {
302 label: Some("Muspelheim Intermediate"),
303 size: wgpu::Extent3d {
304 width: config.width,
305 height: config.height,
306 depth_or_array_layers: 1,
307 },
308 mip_level_count: 1,
309 sample_count: 1,
310 dimension: wgpu::TextureDimension::D2,
311 format: config.format,
312 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
313 view_formats: &[],
314 };
315 let blur_texture_a_obj = device.create_texture(&blur_tex_desc);
316 let blur_texture_b_obj = device.create_texture(&blur_tex_desc);
317 let blur_texture_a = blur_texture_a_obj.create_view(&wgpu::TextureViewDescriptor::default());
318 let blur_texture_b = blur_texture_b_obj.create_view(&wgpu::TextureViewDescriptor::default());
319
320 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
321 address_mode_u: wgpu::AddressMode::ClampToEdge,
322 address_mode_v: wgpu::AddressMode::ClampToEdge,
323 mag_filter: wgpu::FilterMode::Linear,
324 ..Default::default()
325 });
326
327 let blur_bind_group_a = device.create_bind_group(&wgpu::BindGroupDescriptor {
328 layout: &texture_bind_group_layout,
329 entries: &[
330 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&blur_texture_a) },
331 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler) },
332 ],
333 label: Some("Blur Bind Group A"),
334 });
335
336 let blur_bind_group_b = device.create_bind_group(&wgpu::BindGroupDescriptor {
337 layout: &texture_bind_group_layout,
338 entries: &[
339 wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(&blur_texture_b) },
340 wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler) },
341 ],
342 label: Some("Blur Bind Group B"),
343 });
344
345 let dummy_size = wgpu::Extent3d {
347 width: 1,
348 height: 1,
349 depth_or_array_layers: 1,
350 };
351 let dummy_texture = device.create_texture(&wgpu::TextureDescriptor {
352 label: Some("Niflheim Dummy Texture"),
353 size: dummy_size,
354 mip_level_count: 1,
355 sample_count: 1,
356 dimension: wgpu::TextureDimension::D2,
357 format: wgpu::TextureFormat::Rgba8UnormSrgb,
358 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
359 view_formats: &[],
360 });
361 queue.write_texture(
362 wgpu::ImageCopyTexture {
363 texture: &dummy_texture,
364 mip_level: 0,
365 origin: wgpu::Origin3d::ZERO,
366 aspect: wgpu::TextureAspect::All,
367 },
368 &[255, 255, 255, 255],
369 wgpu::ImageDataLayout {
370 offset: 0,
371 bytes_per_row: Some(4),
372 rows_per_image: Some(1),
373 },
374 dummy_size,
375 );
376
377 let dummy_view = dummy_texture.create_view(&wgpu::TextureViewDescriptor::default());
378 let dummy_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
379 address_mode_u: wgpu::AddressMode::ClampToEdge,
380 address_mode_v: wgpu::AddressMode::ClampToEdge,
381 address_mode_w: wgpu::AddressMode::ClampToEdge,
382 mag_filter: wgpu::FilterMode::Linear,
383 min_filter: wgpu::FilterMode::Nearest,
384 mipmap_filter: wgpu::FilterMode::Nearest,
385 ..Default::default()
386 });
387
388 let dummy_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
389 layout: &texture_bind_group_layout,
390 entries: &[
391 wgpu::BindGroupEntry {
392 binding: 0,
393 resource: wgpu::BindingResource::TextureView(&dummy_view),
394 },
395 wgpu::BindGroupEntry {
396 binding: 1,
397 resource: wgpu::BindingResource::Sampler(&dummy_sampler),
398 },
399 ],
400 label: Some("Niflheim Dummy Bind Group"),
401 });
402
403 let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
405 label: Some("Surtr Vertex Anvil"),
406 size: (MAX_VERTICES * std::mem::size_of::<Vertex>()) as u64,
407 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
408 mapped_at_creation: false,
409 });
410
411 let index_buffer = device.create_buffer(&wgpu::BufferDescriptor {
412 label: Some("Surtr Index Anvil"),
413 size: (MAX_INDICES * std::mem::size_of::<u16>()) as u64,
414 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
415 mapped_at_creation: false,
416 });
417
418
419 Self {
420 device,
421 queue,
422 surface,
423 config,
424 pipeline,
425 bloom_extract_pipeline,
426 blur_h_pipeline,
427 blur_v_pipeline,
428 composite_pipeline,
429 blur_texture_a,
430 blur_texture_b,
431 blur_bind_group_a,
432 blur_bind_group_b,
433 font_system: cosmic_text::FontSystem::new(),
434 swash_cache: cosmic_text::SwashCache::new(),
435 dummy_bind_group,
436 vertex_buffer,
437 index_buffer,
438 vertices: Vec::with_capacity(MAX_VERTICES),
439 indices: Vec::with_capacity(MAX_INDICES),
440 }
441 }
442
443 pub fn begin_frame(&mut self) -> wgpu::CommandEncoder {
445 self.vertices.clear();
446 self.indices.clear();
447 self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
448 label: Some("Surtr's Flaming Sword"),
449 })
450 }
451
452 pub fn end_frame(&mut self, mut encoder: wgpu::CommandEncoder) {
458 self.queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&self.vertices));
459 self.queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&self.indices));
460
461 let frame = self.surface.get_current_texture()
462 .expect("Surtr: failed to acquire surface texture");
463 let screen = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
464
465 {
467 let mut p = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
468 label: Some("Surtr P1 Base"),
469 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
470 view: &screen,
471 resolve_target: None,
472 ops: wgpu::Operations {
473 load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }), store: wgpu::StoreOp::Store,
475 },
476 })],
477 depth_stencil_attachment: None,
478 occlusion_query_set: None,
479 timestamp_writes: None,
480 });
481 if !self.indices.is_empty() {
482 p.set_pipeline(&self.pipeline);
483 p.set_bind_group(0, &self.dummy_bind_group, &[]);
484 p.set_vertex_buffer(0, self.vertex_buffer.slice(..));
485 p.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
486 p.draw_indexed(0..self.indices.len() as u32, 0, 0..1);
487 }
488 }
489
490 {
494 let mut p = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
495 label: Some("Surtr P2 Bloom Src"),
496 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
497 view: &self.blur_texture_a,
498 resolve_target: None,
499 ops: wgpu::Operations {
500 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
501 store: wgpu::StoreOp::Store,
502 },
503 })],
504 depth_stencil_attachment: None,
505 occlusion_query_set: None,
506 timestamp_writes: None,
507 });
508 if !self.indices.is_empty() {
509 p.set_pipeline(&self.pipeline);
510 p.set_bind_group(0, &self.dummy_bind_group, &[]);
511 p.set_vertex_buffer(0, self.vertex_buffer.slice(..));
512 p.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
513 p.draw_indexed(0..self.indices.len() as u32, 0, 0..1);
514 }
515 }
516
517 let blur_iters: u32 = 6;
520 for i in 0..blur_iters {
521 {
523 let label = format!("Surtr Blur H iter {}", i);
524 let mut p = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
525 label: Some(&label),
526 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
527 view: &self.blur_texture_b,
528 resolve_target: None,
529 ops: wgpu::Operations {
530 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
531 store: wgpu::StoreOp::Store,
532 },
533 })],
534 depth_stencil_attachment: None,
535 occlusion_query_set: None,
536 timestamp_writes: None,
537 });
538 p.set_pipeline(&self.blur_h_pipeline);
539 p.set_bind_group(0, &self.blur_bind_group_a, &[]);
540 p.draw(0..3, 0..1);
541 }
542 {
544 let label = format!("Surtr Blur V iter {}", i);
545 let mut p = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
546 label: Some(&label),
547 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
548 view: &self.blur_texture_a,
549 resolve_target: None,
550 ops: wgpu::Operations {
551 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
552 store: wgpu::StoreOp::Store,
553 },
554 })],
555 depth_stencil_attachment: None,
556 occlusion_query_set: None,
557 timestamp_writes: None,
558 });
559 p.set_pipeline(&self.blur_v_pipeline);
560 p.set_bind_group(0, &self.blur_bind_group_b, &[]);
561 p.draw(0..3, 0..1);
562 }
563 }
564
565 {
568 let mut p = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
569 label: Some("Surtr P7 Composite"),
570 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
571 view: &screen,
572 resolve_target: None,
573 ops: wgpu::Operations {
574 load: wgpu::LoadOp::Load,
575 store: wgpu::StoreOp::Store,
576 },
577 })],
578 depth_stencil_attachment: None,
579 occlusion_query_set: None,
580 timestamp_writes: None,
581 });
582 p.set_pipeline(&self.composite_pipeline);
583 p.set_bind_group(0, &self.blur_bind_group_a, &[]);
584 p.draw(0..3, 0..1);
585 }
586
587 self.queue.submit(Some(encoder.finish()));
588 frame.present();
589 }
590
591 pub fn fill_rect(&mut self, rect: Rect, color: [f32; 4], mode: u32) {
593 let base_idx = self.vertices.len() as u16;
594
595 let x1 = (rect.x / 400.0) - 1.0;
597 let y1 = 1.0 - (rect.y / 300.0);
598 let x2 = ((rect.x + rect.width) / 400.0) - 1.0;
599 let y2 = 1.0 - ((rect.y + rect.height) / 300.0);
600
601 self.vertices.push(Vertex { position: [x1, y1], uv: [0.0, 0.0], color, mode });
602 self.vertices.push(Vertex { position: [x2, y1], uv: [1.0, 0.0], color, mode });
603 self.vertices.push(Vertex { position: [x2, y2], uv: [1.0, 1.0], color, mode });
604 self.vertices.push(Vertex { position: [x1, y2], uv: [0.0, 1.0], color, mode });
605
606 self.indices.extend_from_slice(&[
607 base_idx, base_idx + 1, base_idx + 2,
608 base_idx, base_idx + 2, base_idx + 3,
609 ]);
610 }
611}