use crate::core::Rgba;
use crate::gpu::atlas::{AtlasPair, TextureAtlas};
use crate::gpu::blur::BlurState;
use crate::gpu::context::{Error, GpuContext};
use crate::gpu::globals::GlobalsSlots;
use crate::gpu::instance_buffer::InstanceBuffer;
use crate::gpu::pipelines::Pipelines;
use crate::gpu::scene::{Batch, PrimitiveKind, Scene};
use crate::gpu::text::GlyphTarget;
#[path = "renderer_blur.rs"]
mod blur_frame;
pub struct GpuRenderer {
pub context: GpuContext,
pipelines: Pipelines,
globals: GlobalsSlots,
blur: Option<BlurState>,
pub(crate) atlases: AtlasPair,
monochrome_bind_group: wgpu::BindGroup,
polychrome_bind_group: wgpu::BindGroup,
shadows: InstanceBuffer,
quads: InstanceBuffer,
underlines: InstanceBuffer,
monochrome_sprites: InstanceBuffer,
polychrome_sprites: InstanceBuffer,
}
impl GpuRenderer {
pub fn new(context: GpuContext) -> Result<Self, Error> {
let device = &context.device;
let pipelines = Pipelines::new(device, context.config.format)?;
let globals = GlobalsSlots::new(device, &pipelines.globals_layout, 1);
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
..Default::default()
});
let atlases = AtlasPair::gpu(device);
let atlas_bind_group = |atlas: &TextureAtlas, label: &str| {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some(label),
layout: &pipelines.atlas_layout,
entries: &[
wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::TextureView(atlas.view()) },
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler) },
],
})
};
let monochrome_bind_group = atlas_bind_group(&atlases.monochrome, "aura_mono_bind");
let polychrome_bind_group = atlas_bind_group(&atlases.polychrome, "aura_color_bind");
let layout = &pipelines.instances_layout;
Ok(Self {
shadows: InstanceBuffer::new(device, layout, "aura_shadows"),
quads: InstanceBuffer::new(device, layout, "aura_quads"),
underlines: InstanceBuffer::new(device, layout, "aura_underlines"),
monochrome_sprites: InstanceBuffer::new(device, layout, "aura_mono_sprites"),
polychrome_sprites: InstanceBuffer::new(device, layout, "aura_color_sprites"),
pipelines,
globals,
blur: None,
atlases,
monochrome_bind_group,
polychrome_bind_group,
context,
})
}
pub fn resize(&mut self, width: u32, height: u32) {
self.context.resize(width, height);
}
pub fn take_atlas_overflow(&mut self) -> bool {
self.atlases.take_overflow()
}
pub fn glyph_target(&mut self) -> GlyphTarget<'_> {
let queue = &self.context.queue;
self.atlases.target(Some(queue))
}
pub fn render(&mut self, scene: &Scene, scale: f32, clear: Rgba) -> Result<bool, Error> {
let frame = match self.context.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(frame) => frame,
wgpu::CurrentSurfaceTexture::Suboptimal(frame) => {
self.context.resize(self.context.config.width, self.context.config.height);
frame
}
wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded => return Ok(false),
wgpu::CurrentSurfaceTexture::Outdated | wgpu::CurrentSurfaceTexture::Lost => {
self.context.resize(self.context.config.width, self.context.config.height);
return Ok(false);
}
other => return Err(Error(format!("no se pudo adquirir el frame: {other:?}"))),
};
self.upload(scene, scale);
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
let encoder = self.encode(&view, scene, clear);
self.context.queue.submit(Some(encoder.finish()));
self.context.queue.present(frame);
Ok(true)
}
pub(crate) fn encode(&self, view: &wgpu::TextureView, scene: &Scene, clear: Rgba) -> wgpu::CommandEncoder {
let mut encoder =
self.context.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("aura_frame") });
if let Some(blur) = self.blur.as_ref().filter(|_| scene.has_blur()) {
self.encode_blurred(&mut encoder, blur, view, scene, clear);
return encoder;
}
{
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("aura_pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: clear.r as f64,
g: clear.g as f64,
b: clear.b as f64,
a: clear.a as f64,
}),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
self.draw_batches(&mut pass, &scene.batches);
}
encoder
}
pub(crate) fn upload(&mut self, scene: &Scene, scale: f32) {
let viewport = [self.context.config.width as f32 / scale, self.context.config.height as f32 / scale];
let (device, queue) = (&self.context.device, &self.context.queue);
self.globals.upload(device, queue, &self.pipelines.globals_layout, scene, viewport);
self.prepare_blur(scene, scale);
let (device, queue) = (&self.context.device, &self.context.queue);
let layout = &self.pipelines.instances_layout;
self.shadows.upload(device, queue, layout, &scene.shadows);
self.quads.upload(device, queue, layout, &scene.quads);
self.underlines.upload(device, queue, layout, &scene.underlines);
self.monochrome_sprites.upload(device, queue, layout, &scene.monochrome_sprites);
self.polychrome_sprites.upload(device, queue, layout, &scene.polychrome_sprites);
}
fn draw_batches(&self, pass: &mut wgpu::RenderPass<'_>, batches: &[Batch]) {
let mut bound = None;
for batch in batches {
if bound != Some(batch.transform) {
bound = Some(batch.transform);
pass.set_bind_group(
0,
self.globals.bind_group(),
&[(u64::from(batch.transform) * self.globals.stride()) as u32],
);
}
let (pipeline, instances, atlas) = match batch.kind {
PrimitiveKind::Blur => {
let Some(blur) = &self.blur else { continue };
(blur.composite(), blur.instances(), blur.blurred_bind())
}
PrimitiveKind::Shadow => (&self.pipelines.shadow, &self.shadows, &self.monochrome_bind_group),
PrimitiveKind::Quad => (&self.pipelines.quad, &self.quads, &self.monochrome_bind_group),
PrimitiveKind::Underline => (&self.pipelines.underline, &self.underlines, &self.monochrome_bind_group),
PrimitiveKind::Monochrome => {
(&self.pipelines.monochrome, &self.monochrome_sprites, &self.monochrome_bind_group)
}
PrimitiveKind::Polychrome => {
(&self.pipelines.polychrome, &self.polychrome_sprites, &self.polychrome_bind_group)
}
};
pass.set_pipeline(pipeline);
pass.set_bind_group(1, instances.bind_group(), &[]);
pass.set_bind_group(2, atlas, &[]);
pass.draw(0..4, batch.range.clone());
}
}
pub fn shrink_caches(&mut self) {
let (device, layout) = (&self.context.device, &self.pipelines.instances_layout);
self.shadows.shrink(device, layout);
self.quads.shrink(device, layout);
self.underlines.shrink(device, layout);
self.monochrome_sprites.shrink(device, layout);
self.polychrome_sprites.shrink(device, layout);
self.atlases.clear();
if let Some(blur) = &mut self.blur {
blur.release();
}
}
}