mirror of https://github.com/wayvr-org/wayvr.git
245 lines
5.7 KiB
Rust
245 lines
5.7 KiB
Rust
use std::{collections::HashMap, sync::Arc};
|
|
|
|
use cosmic_text::SubpixelBin;
|
|
use glam::Mat4;
|
|
use smallvec::smallvec;
|
|
use vulkano::{
|
|
buffer::{BufferContents, BufferUsage, Subbuffer},
|
|
command_buffer::CommandBufferUsage,
|
|
format::Format,
|
|
image::view::ImageView,
|
|
pipeline::graphics::{self, vertex_input::Vertex},
|
|
};
|
|
|
|
use crate::{
|
|
drawing::{Boundary, ImagePrimitive},
|
|
gfx::{
|
|
BLEND_ALPHA, WGfx,
|
|
cmd::GfxCommandBuffer,
|
|
pass::WGfxPass,
|
|
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
|
},
|
|
renderer_vk::{
|
|
model_buffer::ModelBuffer,
|
|
text::custom_glyph::{CustomGlyphData, RasterizeCustomGlyphRequest, RasterizedCustomGlyph},
|
|
},
|
|
};
|
|
|
|
use super::viewport::Viewport;
|
|
|
|
#[repr(C)]
|
|
#[derive(BufferContents, Vertex, Copy, Clone, Debug)]
|
|
pub struct ImageVertex {
|
|
#[format(R32_UINT)]
|
|
pub in_model_idx: u32,
|
|
#[format(R32_UINT)]
|
|
pub in_rect_dim: [u16; 2],
|
|
#[format(R32_UINT)]
|
|
pub in_border_color: u32,
|
|
#[format(R32_UINT)]
|
|
pub round_border: [u8; 4],
|
|
}
|
|
|
|
/// Cloneable pipeline & shaders to be shared between `RectRenderer` instances.
|
|
#[derive(Clone)]
|
|
pub struct ImagePipeline {
|
|
gfx: Arc<WGfx>,
|
|
pub(super) inner: Arc<WGfxPipeline<ImageVertex>>,
|
|
}
|
|
|
|
impl ImagePipeline {
|
|
pub fn new(gfx: Arc<WGfx>, format: Format) -> anyhow::Result<Self> {
|
|
let vert = vert_image::load(gfx.device.clone())?;
|
|
let frag = frag_image::load(gfx.device.clone())?;
|
|
|
|
let pipeline = gfx.create_pipeline::<ImageVertex>(
|
|
&vert,
|
|
&frag,
|
|
WPipelineCreateInfo::new(format)
|
|
.use_blend(BLEND_ALPHA)
|
|
.use_instanced()
|
|
.use_updatable_descriptors(smallvec![2]),
|
|
)?;
|
|
|
|
Ok(Self { gfx, inner: pipeline })
|
|
}
|
|
}
|
|
|
|
struct ImageVertexWithContent {
|
|
vert: ImageVertex,
|
|
content: CustomGlyphData,
|
|
content_key: usize, // identifies an image tag.
|
|
}
|
|
|
|
struct CachedPass {
|
|
content_id: usize,
|
|
vert_buffer: Subbuffer<[ImageVertex]>,
|
|
inner: WGfxPass<ImageVertex>,
|
|
res: [u32; 2],
|
|
}
|
|
|
|
pub struct ImageRenderer {
|
|
pipeline: ImagePipeline,
|
|
image_verts: Vec<ImageVertexWithContent>,
|
|
model_buffer: ModelBuffer,
|
|
cached_passes: HashMap<usize, CachedPass>,
|
|
}
|
|
|
|
impl ImageRenderer {
|
|
pub fn new(pipeline: ImagePipeline) -> anyhow::Result<Self> {
|
|
Ok(Self {
|
|
model_buffer: ModelBuffer::new(&pipeline.gfx)?,
|
|
pipeline,
|
|
image_verts: vec![],
|
|
cached_passes: HashMap::new(),
|
|
})
|
|
}
|
|
|
|
pub fn begin(&mut self) {
|
|
self.image_verts.clear();
|
|
self.model_buffer.begin();
|
|
}
|
|
|
|
pub fn add_image(&mut self, boundary: Boundary, image: ImagePrimitive, transform: &Mat4) {
|
|
let in_model_idx = self
|
|
.model_buffer
|
|
.register_pos_size(&boundary.pos, &boundary.size, transform);
|
|
|
|
self.image_verts.push(ImageVertexWithContent {
|
|
vert: ImageVertex {
|
|
in_model_idx,
|
|
in_rect_dim: [boundary.size.x as u16, boundary.size.y as u16],
|
|
in_border_color: cosmic_text::Color::from(image.border_color).0,
|
|
round_border: [
|
|
image.round_units,
|
|
(image.border) as u8,
|
|
0, // unused
|
|
0,
|
|
],
|
|
},
|
|
content: image.content,
|
|
content_key: image.content_key,
|
|
});
|
|
}
|
|
|
|
fn upload_image(
|
|
gfx: Arc<WGfx>,
|
|
res: [u32; 2],
|
|
img: &ImageVertexWithContent,
|
|
) -> anyhow::Result<Option<Arc<ImageView>>> {
|
|
let raster = match RasterizedCustomGlyph::try_from(&RasterizeCustomGlyphRequest {
|
|
data: img.content.clone(),
|
|
width: res[0] as _,
|
|
height: res[1] as _,
|
|
x_bin: SubpixelBin::Zero,
|
|
y_bin: SubpixelBin::Zero,
|
|
scale: 1.0, // unused
|
|
}) {
|
|
Some(x) => x,
|
|
None => {
|
|
log::error!("Unable to rasterize custom image");
|
|
return Ok(None);
|
|
}
|
|
};
|
|
let mut cmd_buf = gfx.create_xfer_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
|
|
|
let image = cmd_buf.upload_image(
|
|
raster.width as _,
|
|
raster.height as _,
|
|
Format::R8G8B8A8_UNORM,
|
|
&raster.data,
|
|
)?;
|
|
let image_view = ImageView::new_default(image)?;
|
|
|
|
cmd_buf.build_and_execute_now()?;
|
|
|
|
Ok(Some(image_view))
|
|
}
|
|
|
|
pub fn render(
|
|
&mut self,
|
|
gfx: &Arc<WGfx>,
|
|
viewport: &mut Viewport,
|
|
vk_scissor: &graphics::viewport::Scissor,
|
|
cmd_buf: &mut GfxCommandBuffer,
|
|
) -> anyhow::Result<()> {
|
|
let res = viewport.resolution();
|
|
self.model_buffer.upload(gfx)?;
|
|
|
|
for img in self.image_verts.iter() {
|
|
let pass = match self.cached_passes.get_mut(&img.content_key) {
|
|
Some(x) => {
|
|
if x.content_id != img.content.id || x.res != res {
|
|
// image changed
|
|
let Some(image_view) = Self::upload_image(self.pipeline.gfx.clone(), res, img)? else {
|
|
continue;
|
|
};
|
|
|
|
x.inner
|
|
.update_sampler(2, image_view, self.pipeline.gfx.texture_filter)?;
|
|
}
|
|
|
|
x
|
|
}
|
|
None => {
|
|
let vert_buffer = self.pipeline.gfx.empty_buffer(
|
|
BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST,
|
|
(std::mem::size_of::<ImageVertex>()) as _,
|
|
)?;
|
|
|
|
let Some(image_view) = Self::upload_image(self.pipeline.gfx.clone(), res, img)? else {
|
|
continue;
|
|
};
|
|
|
|
let set0 = viewport.get_image_descriptor(&self.pipeline);
|
|
let set1 = self.model_buffer.get_image_descriptor(&self.pipeline);
|
|
let set2 = self
|
|
.pipeline
|
|
.inner
|
|
.uniform_sampler(2, image_view, self.pipeline.gfx.texture_filter)?;
|
|
|
|
let pass = self.pipeline.inner.create_pass(
|
|
[res[0] as _, res[1] as _],
|
|
vert_buffer.clone(),
|
|
0..4,
|
|
0..self.image_verts.len() as _,
|
|
vec![set0, set1, set2],
|
|
vk_scissor,
|
|
)?;
|
|
|
|
self.cached_passes.insert(
|
|
img.content_key,
|
|
CachedPass {
|
|
content_id: img.content.id,
|
|
vert_buffer,
|
|
inner: pass,
|
|
res,
|
|
},
|
|
);
|
|
self.cached_passes.get_mut(&img.content_key).unwrap()
|
|
}
|
|
};
|
|
|
|
pass.vert_buffer.write()?[0..1].clone_from_slice(&[img.vert]);
|
|
|
|
cmd_buf.run_ref(&pass.inner)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub mod vert_image {
|
|
vulkano_shaders::shader! {
|
|
ty: "vertex",
|
|
path: "src/renderer_vk/shaders/image.vert",
|
|
}
|
|
}
|
|
|
|
pub mod frag_image {
|
|
vulkano_shaders::shader! {
|
|
ty: "fragment",
|
|
path: "src/renderer_vk/shaders/image.frag",
|
|
}
|
|
}
|