new workspace
This commit is contained in:
221
wgui/src/renderer_vk/context.rs
Normal file
221
wgui/src/renderer_vk/context.rs
Normal file
@@ -0,0 +1,221 @@
|
||||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
|
||||
use cosmic_text::Buffer;
|
||||
use glam::{Mat4, Vec2, Vec3};
|
||||
|
||||
use crate::{
|
||||
drawing,
|
||||
gfx::{WGfx, cmd::GfxCommandBuffer},
|
||||
};
|
||||
|
||||
use super::{
|
||||
rect::{RectPipeline, RectRenderer},
|
||||
text::{
|
||||
DEFAULT_METRICS, FONT_SYSTEM, SWASH_CACHE, TextArea, TextBounds,
|
||||
text_atlas::{TextAtlas, TextPipeline},
|
||||
text_renderer::TextRenderer,
|
||||
},
|
||||
viewport::Viewport,
|
||||
};
|
||||
|
||||
struct RendererPass<'a> {
|
||||
submitted: bool,
|
||||
text_areas: Vec<TextArea<'a>>,
|
||||
text_renderer: TextRenderer,
|
||||
rect_renderer: RectRenderer,
|
||||
}
|
||||
|
||||
impl RendererPass<'_> {
|
||||
fn new(text_atlas: &mut TextAtlas, rect_pipeline: RectPipeline) -> anyhow::Result<Self> {
|
||||
let text_renderer = TextRenderer::new(text_atlas)?;
|
||||
let rect_renderer = RectRenderer::new(rect_pipeline)?;
|
||||
|
||||
Ok(Self {
|
||||
submitted: false,
|
||||
text_renderer,
|
||||
rect_renderer,
|
||||
text_areas: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn submit(
|
||||
&mut self,
|
||||
gfx: &Arc<WGfx>,
|
||||
viewport: &mut Viewport,
|
||||
cmd_buf: &mut GfxCommandBuffer,
|
||||
text_atlas: &mut TextAtlas,
|
||||
) -> anyhow::Result<()> {
|
||||
if self.submitted {
|
||||
return Ok(());
|
||||
}
|
||||
self.submitted = true;
|
||||
self.rect_renderer.render(gfx, viewport, cmd_buf)?;
|
||||
|
||||
{
|
||||
let mut font_system = FONT_SYSTEM.lock().unwrap();
|
||||
let mut swash_cache = SWASH_CACHE.lock().unwrap();
|
||||
|
||||
self.text_renderer.prepare(
|
||||
&mut font_system,
|
||||
text_atlas,
|
||||
viewport,
|
||||
std::mem::take(&mut self.text_areas),
|
||||
&mut swash_cache,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.text_renderer.render(text_atlas, viewport, cmd_buf)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
viewport: Viewport,
|
||||
text_atlas: TextAtlas,
|
||||
rect_pipeline: RectPipeline,
|
||||
text_pipeline: TextPipeline,
|
||||
pixel_scale: f32,
|
||||
pub dirty: bool,
|
||||
empty_text: Rc<RefCell<Buffer>>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(
|
||||
gfx: Arc<WGfx>,
|
||||
native_format: vulkano::format::Format,
|
||||
pixel_scale: f32,
|
||||
) -> anyhow::Result<Self> {
|
||||
let rect_pipeline = RectPipeline::new(gfx.clone(), native_format)?;
|
||||
let text_pipeline = TextPipeline::new(gfx.clone(), native_format)?;
|
||||
let viewport = Viewport::new(gfx.clone())?;
|
||||
let text_atlas = TextAtlas::new(text_pipeline.clone())?;
|
||||
|
||||
Ok(Self {
|
||||
viewport,
|
||||
text_atlas,
|
||||
rect_pipeline,
|
||||
text_pipeline,
|
||||
pixel_scale,
|
||||
dirty: true,
|
||||
empty_text: Rc::new(RefCell::new(Buffer::new_empty(DEFAULT_METRICS))),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn regen(&mut self) -> anyhow::Result<()> {
|
||||
self.text_atlas = TextAtlas::new(self.text_pipeline.clone())?;
|
||||
self.dirty = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_viewport(&mut self, resolution: [u32; 2], pixel_scale: f32) -> anyhow::Result<()> {
|
||||
if self.pixel_scale != pixel_scale {
|
||||
self.pixel_scale = pixel_scale;
|
||||
self.regen()?;
|
||||
}
|
||||
|
||||
if self.viewport.resolution() != resolution {
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
let size = Vec2::new(
|
||||
resolution[0] as f32 / pixel_scale,
|
||||
resolution[1] as f32 / pixel_scale,
|
||||
);
|
||||
|
||||
let fov = 0.4;
|
||||
let aspect_ratio = size.x / size.y;
|
||||
let projection = Mat4::perspective_rh(fov, aspect_ratio, 1.0, 100000.0);
|
||||
|
||||
let b = size.y / 2.0;
|
||||
let angle_half = fov / 2.0;
|
||||
let distance = (std::f32::consts::PI / 2.0 - angle_half).tan() * b;
|
||||
|
||||
let view = Mat4::look_at_rh(
|
||||
Vec3::new(size.x / 2.0, size.y / 2.0, distance),
|
||||
Vec3::new(size.x / 2.0, size.y / 2.0, 0.0),
|
||||
Vec3::new(0.0, 1.0, 0.0),
|
||||
);
|
||||
|
||||
let fin = projection * view;
|
||||
|
||||
self.viewport.update(resolution, &fin, pixel_scale)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_pass(&mut self, passes: &mut Vec<RendererPass>) -> anyhow::Result<()> {
|
||||
passes.push(RendererPass::new(
|
||||
&mut self.text_atlas,
|
||||
self.rect_pipeline.clone(),
|
||||
)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn submit_pass(
|
||||
&mut self,
|
||||
gfx: &Arc<WGfx>,
|
||||
cmd_buf: &mut GfxCommandBuffer,
|
||||
pass: &mut RendererPass,
|
||||
) -> anyhow::Result<()> {
|
||||
pass.submit(gfx, &mut self.viewport, cmd_buf, &mut self.text_atlas)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
gfx: &Arc<WGfx>,
|
||||
cmd_buf: &mut GfxCommandBuffer,
|
||||
primitives: &[drawing::RenderPrimitive],
|
||||
) -> anyhow::Result<()> {
|
||||
self.dirty = false;
|
||||
let mut passes = Vec::<RendererPass>::new();
|
||||
self.new_pass(&mut passes)?;
|
||||
|
||||
for primitive in primitives.iter() {
|
||||
let pass = passes.last_mut().unwrap(); // always safe
|
||||
|
||||
match &primitive.payload {
|
||||
drawing::PrimitivePayload::Rectangle(rectangle) => {
|
||||
pass.rect_renderer.add_rect(
|
||||
primitive.boundary,
|
||||
*rectangle,
|
||||
&primitive.transform,
|
||||
primitive.depth,
|
||||
);
|
||||
}
|
||||
drawing::PrimitivePayload::Text(text) => {
|
||||
pass.text_areas.push(TextArea {
|
||||
buffer: text.clone(),
|
||||
left: primitive.boundary.pos.x * self.pixel_scale,
|
||||
top: primitive.boundary.pos.y * self.pixel_scale,
|
||||
bounds: TextBounds::default(), //FIXME: just using boundary coords here doesn't work
|
||||
scale: self.pixel_scale,
|
||||
default_color: cosmic_text::Color::rgb(0, 0, 0),
|
||||
custom_glyphs: &[],
|
||||
depth: primitive.depth,
|
||||
transform: primitive.transform,
|
||||
});
|
||||
}
|
||||
drawing::PrimitivePayload::Sprite(sprites) => {
|
||||
pass.text_areas.push(TextArea {
|
||||
buffer: self.empty_text.clone(),
|
||||
left: primitive.boundary.pos.x * self.pixel_scale,
|
||||
top: primitive.boundary.pos.y * self.pixel_scale,
|
||||
bounds: TextBounds::default(),
|
||||
scale: self.pixel_scale,
|
||||
custom_glyphs: sprites.as_slice(),
|
||||
default_color: cosmic_text::Color::rgb(255, 0, 255),
|
||||
depth: primitive.depth,
|
||||
transform: primitive.transform,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pass = passes.last_mut().unwrap();
|
||||
self.submit_pass(gfx, cmd_buf, pass)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user