use super::{
WgpuLive2dRenderer,
buffers::WgpuMeshBuffers,
clipping::{WgpuClippingResources, WgpuMaskRenderTarget},
texture::{WgpuTexture, WgpuTransform},
};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum WgpuRenderError {
#[error("invalid texture index {texture_index}")]
InvalidTextureIndex { texture_index: i32 },
#[error("missing texture bind group {texture_index}")]
MissingTexture { texture_index: i32 },
#[error("missing drawable {drawable_index}")]
MissingDrawable { drawable_index: usize },
#[error("drawable {drawable_index} has clipping masks but no prepared clipping context")]
MissingClippingContext { drawable_index: usize },
#[error(
"drawable {drawable_index} uses {mask_count} clipping masks, but clipping is not implemented"
)]
UnsupportedClippingMasks {
drawable_index: usize,
mask_count: usize,
},
}
impl WgpuLive2dRenderer {
pub fn draw(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
texture_bind_groups: &[wgpu::BindGroup],
) -> Result<u32, WgpuRenderError> {
self.draw_with_bind_groups_and_transform(
render_pass,
mesh_buffers,
texture_bind_groups,
&self.identity_transform,
)
}
pub fn draw_with_bind_groups_and_transform(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
texture_bind_groups: &[wgpu::BindGroup],
transform: &WgpuTransform,
) -> Result<u32, WgpuRenderError> {
self.draw_with_bind_group_provider(render_pass, mesh_buffers, transform, |texture_index| {
texture_bind_group_at(texture_bind_groups, texture_index)
})
}
pub fn draw_with_textures(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
textures: &[WgpuTexture],
) -> Result<u32, WgpuRenderError> {
self.draw_with_textures_and_transform(
render_pass,
mesh_buffers,
textures,
&self.identity_transform,
)
}
pub fn draw_with_textures_and_transform(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
textures: &[WgpuTexture],
transform: &WgpuTransform,
) -> Result<u32, WgpuRenderError> {
self.draw_with_bind_group_provider(render_pass, mesh_buffers, transform, |texture_index| {
texture_bind_group_from_textures(textures, texture_index)
})
}
pub fn draw_with_textures_and_clipping(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
textures: &[WgpuTexture],
clipping_resources: &WgpuClippingResources,
mask_target: &WgpuMaskRenderTarget,
) -> Result<u32, WgpuRenderError> {
self.draw_with_textures_clipping_and_transform(
render_pass,
mesh_buffers,
textures,
clipping_resources,
mask_target,
&self.identity_transform,
)
}
pub fn draw_with_textures_clipping_and_transform(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
textures: &[WgpuTexture],
clipping_resources: &WgpuClippingResources,
mask_target: &WgpuMaskRenderTarget,
transform: &WgpuTransform,
) -> Result<u32, WgpuRenderError> {
self.draw_with_clipping_bind_group_provider(
render_pass,
mesh_buffers,
clipping_resources,
mask_target.bind_group(),
transform,
|texture_index| texture_bind_group_from_textures(textures, texture_index),
)
}
pub fn draw_masks(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
clipping_resources: &WgpuClippingResources,
texture_bind_groups: &[wgpu::BindGroup],
) -> Result<u32, WgpuRenderError> {
self.draw_masks_with_bind_group_provider(
render_pass,
mesh_buffers,
clipping_resources,
|texture_index| texture_bind_group_at(texture_bind_groups, texture_index),
)
}
pub fn draw_masks_with_textures(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
clipping_resources: &WgpuClippingResources,
textures: &[WgpuTexture],
) -> Result<u32, WgpuRenderError> {
self.draw_masks_with_bind_group_provider(
render_pass,
mesh_buffers,
clipping_resources,
|texture_index| texture_bind_group_from_textures(textures, texture_index),
)
}
fn draw_with_bind_group_provider<'a>(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
transform: &WgpuTransform,
mut bind_group_for_texture: impl FnMut(i32) -> Result<&'a wgpu::BindGroup, WgpuRenderError>,
) -> Result<u32, WgpuRenderError> {
let mut drawn = 0;
for &drawable_index in mesh_buffers.draw_order_indices() {
let drawable = mesh_buffers
.drawables()
.get(drawable_index)
.ok_or(WgpuRenderError::MissingDrawable { drawable_index })?;
if !drawable.is_visible() {
continue;
}
if !drawable.masks().is_empty() {
return Err(WgpuRenderError::UnsupportedClippingMasks {
drawable_index,
mask_count: drawable.masks().len(),
});
}
let texture_bind_group = bind_group_for_texture(drawable.texture_index())?;
render_pass.set_pipeline(self.pipeline_for_blend_mode(drawable.blend_mode()));
render_pass.set_bind_group(0, texture_bind_group, &[]);
render_pass.set_bind_group(1, transform.bind_group(), &[]);
render_pass.set_vertex_buffer(0, drawable.vertex_buffer().slice(..));
render_pass
.set_index_buffer(drawable.index_buffer().slice(..), wgpu::IndexFormat::Uint16);
render_pass.draw_indexed(0..drawable.index_count(), 0, 0..1);
drawn += 1;
}
Ok(drawn)
}
fn draw_masks_with_bind_group_provider<'a>(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
clipping_resources: &WgpuClippingResources,
mut bind_group_for_texture: impl FnMut(i32) -> Result<&'a wgpu::BindGroup, WgpuRenderError>,
) -> Result<u32, WgpuRenderError> {
let mut drawn = 0;
for context in clipping_resources.contexts() {
for &drawable_index in context.mask_drawable_indices() {
let drawable = mesh_buffers
.drawables()
.get(drawable_index)
.ok_or(WgpuRenderError::MissingDrawable { drawable_index })?;
if drawable.is_empty() {
continue;
}
let texture_bind_group = bind_group_for_texture(drawable.texture_index())?;
render_pass.set_pipeline(&self.mask_pipeline);
render_pass.set_bind_group(0, texture_bind_group, &[]);
render_pass.set_bind_group(1, context.mask_transform().bind_group(), &[]);
render_pass.set_bind_group(2, context.mask_params().bind_group(), &[]);
render_pass.set_vertex_buffer(0, drawable.vertex_buffer().slice(..));
render_pass
.set_index_buffer(drawable.index_buffer().slice(..), wgpu::IndexFormat::Uint16);
render_pass.draw_indexed(0..drawable.index_count(), 0, 0..1);
drawn += 1;
}
}
Ok(drawn)
}
fn draw_with_clipping_bind_group_provider<'a>(
&self,
render_pass: &mut wgpu::RenderPass<'_>,
mesh_buffers: &WgpuMeshBuffers,
clipping_resources: &WgpuClippingResources,
mask_bind_group: &wgpu::BindGroup,
transform: &WgpuTransform,
mut bind_group_for_texture: impl FnMut(i32) -> Result<&'a wgpu::BindGroup, WgpuRenderError>,
) -> Result<u32, WgpuRenderError> {
let mut drawn = 0;
for &drawable_index in mesh_buffers.draw_order_indices() {
let drawable = mesh_buffers
.drawables()
.get(drawable_index)
.ok_or(WgpuRenderError::MissingDrawable { drawable_index })?;
if !drawable.is_visible() {
continue;
}
let texture_bind_group = bind_group_for_texture(drawable.texture_index())?;
if drawable.masks().is_empty() {
render_pass.set_pipeline(self.pipeline_for_blend_mode(drawable.blend_mode()));
render_pass.set_bind_group(0, texture_bind_group, &[]);
render_pass.set_bind_group(1, transform.bind_group(), &[]);
} else {
let context = clipping_resources
.context_for_drawable(drawable_index)
.ok_or(WgpuRenderError::MissingClippingContext { drawable_index })?;
render_pass
.set_pipeline(self.masked_pipeline_for_blend_mode(drawable.blend_mode()));
render_pass.set_bind_group(0, texture_bind_group, &[]);
render_pass.set_bind_group(1, transform.bind_group(), &[]);
render_pass.set_bind_group(2, mask_bind_group, &[]);
render_pass.set_bind_group(3, context.clip_params().bind_group(), &[]);
}
render_pass.set_vertex_buffer(0, drawable.vertex_buffer().slice(..));
render_pass
.set_index_buffer(drawable.index_buffer().slice(..), wgpu::IndexFormat::Uint16);
render_pass.draw_indexed(0..drawable.index_count(), 0, 0..1);
drawn += 1;
}
Ok(drawn)
}
}
fn texture_bind_group_at(
texture_bind_groups: &[wgpu::BindGroup],
texture_index: i32,
) -> Result<&wgpu::BindGroup, WgpuRenderError> {
let texture_index_usize = usize::try_from(texture_index)
.map_err(|_| WgpuRenderError::InvalidTextureIndex { texture_index })?;
texture_bind_groups
.get(texture_index_usize)
.ok_or(WgpuRenderError::MissingTexture { texture_index })
}
fn texture_bind_group_from_textures(
textures: &[WgpuTexture],
texture_index: i32,
) -> Result<&wgpu::BindGroup, WgpuRenderError> {
let texture_index_usize = usize::try_from(texture_index)
.map_err(|_| WgpuRenderError::InvalidTextureIndex { texture_index })?;
textures
.get(texture_index_usize)
.map(WgpuTexture::bind_group)
.ok_or(WgpuRenderError::MissingTexture { texture_index })
}