852 lines
30 KiB
Rust
852 lines
30 KiB
Rust
use crate::buffer_pool::{PoolEntry, TexturePool};
|
|
use crate::mesh::{BitmapBinds, DrawType, Mesh};
|
|
use crate::pipelines::{BlendType, ComplexBlend, TrivialBlend};
|
|
use crate::surface::{BlendBuffer, DepthBuffer, FrameBuffer, ResolveBuffer, Surface};
|
|
use crate::utils::create_buffer_with_data;
|
|
use crate::{
|
|
as_texture, ColorAdjustments, Descriptors, Globals, MaskState, Pipelines, Transforms,
|
|
UniformBuffer,
|
|
};
|
|
use once_cell::sync::OnceCell;
|
|
use ruffle_render::backend::ShapeHandle;
|
|
use ruffle_render::bitmap::BitmapHandle;
|
|
use ruffle_render::commands::{Command, CommandList};
|
|
use ruffle_render::matrix::Matrix;
|
|
use ruffle_render::transform::Transform;
|
|
use std::sync::Arc;
|
|
use swf::{BlendMode, Color};
|
|
|
|
pub struct CommandTarget {
|
|
frame_buffer: FrameBuffer,
|
|
blend_buffer: OnceCell<BlendBuffer>,
|
|
resolve_buffer: Option<ResolveBuffer>,
|
|
depth: OnceCell<DepthBuffer>,
|
|
globals: Arc<Globals>,
|
|
size: wgpu::Extent3d,
|
|
format: wgpu::TextureFormat,
|
|
sample_count: u32,
|
|
whole_frame_bind_group: OnceCell<(wgpu::Buffer, wgpu::BindGroup)>,
|
|
}
|
|
|
|
impl CommandTarget {
|
|
pub fn new(
|
|
descriptors: &Descriptors,
|
|
pool: &mut TexturePool,
|
|
size: wgpu::Extent3d,
|
|
format: wgpu::TextureFormat,
|
|
sample_count: u32,
|
|
) -> Self {
|
|
let frame_buffer = FrameBuffer::new(
|
|
&descriptors,
|
|
sample_count,
|
|
size,
|
|
format,
|
|
if sample_count > 1 {
|
|
wgpu::TextureUsages::RENDER_ATTACHMENT
|
|
} else {
|
|
wgpu::TextureUsages::RENDER_ATTACHMENT
|
|
| wgpu::TextureUsages::COPY_SRC
|
|
| wgpu::TextureUsages::TEXTURE_BINDING
|
|
},
|
|
pool,
|
|
);
|
|
|
|
let resolve_buffer = if sample_count > 1 {
|
|
Some(ResolveBuffer::new(
|
|
&descriptors,
|
|
size,
|
|
format,
|
|
wgpu::TextureUsages::COPY_SRC
|
|
| wgpu::TextureUsages::TEXTURE_BINDING
|
|
| wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
pool,
|
|
))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let globals = pool.get_globals(descriptors, size.width, size.height);
|
|
|
|
Self {
|
|
frame_buffer,
|
|
blend_buffer: OnceCell::new(),
|
|
resolve_buffer,
|
|
depth: OnceCell::new(),
|
|
globals,
|
|
size,
|
|
format,
|
|
sample_count,
|
|
whole_frame_bind_group: OnceCell::new(),
|
|
}
|
|
}
|
|
|
|
pub fn width(&self) -> u32 {
|
|
self.size.width
|
|
}
|
|
|
|
pub fn height(&self) -> u32 {
|
|
self.size.height
|
|
}
|
|
|
|
pub fn take_color_texture(self) -> PoolEntry<wgpu::Texture> {
|
|
self.resolve_buffer
|
|
.map(|b| b.take_texture())
|
|
.unwrap_or_else(|| self.frame_buffer.take_texture())
|
|
}
|
|
|
|
pub fn globals(&self) -> &Globals {
|
|
&self.globals
|
|
}
|
|
|
|
pub fn whole_frame_bind_group(&self, descriptors: &Descriptors) -> &wgpu::BindGroup {
|
|
&self
|
|
.whole_frame_bind_group
|
|
.get_or_init(|| {
|
|
let transform = Transforms {
|
|
world_matrix: [
|
|
[self.size.width as f32, 0.0, 0.0, 0.0],
|
|
[0.0, self.size.height as f32, 0.0, 0.0],
|
|
[0.0, 0.0, 1.0, 0.0],
|
|
[0.0, 0.0, 0.0, 1.0],
|
|
],
|
|
color_adjustments: ColorAdjustments {
|
|
mult_color: [1.0, 1.0, 1.0, 1.0],
|
|
add_color: [0.0, 0.0, 0.0, 0.0],
|
|
},
|
|
};
|
|
let transforms_buffer = create_buffer_with_data(
|
|
&descriptors.device,
|
|
bytemuck::cast_slice(&[transform]),
|
|
wgpu::BufferUsages::UNIFORM,
|
|
create_debug_label!("Whole-frame transforms buffer"),
|
|
);
|
|
let whole_frame_bind_group =
|
|
descriptors
|
|
.device
|
|
.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
layout: &descriptors.bind_layouts.transforms,
|
|
entries: &[wgpu::BindGroupEntry {
|
|
binding: 0,
|
|
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
|
buffer: &transforms_buffer,
|
|
offset: 0,
|
|
size: wgpu::BufferSize::new(
|
|
std::mem::size_of::<Transforms>() as u64
|
|
),
|
|
}),
|
|
}],
|
|
label: create_debug_label!("Whole-frame transforms bind group")
|
|
.as_deref(),
|
|
});
|
|
(transforms_buffer, whole_frame_bind_group)
|
|
})
|
|
.1
|
|
}
|
|
|
|
pub fn color_attachments(
|
|
&self,
|
|
clear: Option<wgpu::Color>,
|
|
) -> Option<wgpu::RenderPassColorAttachment> {
|
|
Some(wgpu::RenderPassColorAttachment {
|
|
view: &self.frame_buffer.view(),
|
|
resolve_target: self.resolve_buffer.as_ref().map(|b| b.view()),
|
|
ops: wgpu::Operations {
|
|
load: if let Some(color) = clear {
|
|
wgpu::LoadOp::Clear(color)
|
|
} else {
|
|
wgpu::LoadOp::Load
|
|
},
|
|
store: true,
|
|
},
|
|
})
|
|
}
|
|
|
|
pub fn depth_attachment(
|
|
&self,
|
|
descriptors: &Descriptors,
|
|
pool: &mut TexturePool,
|
|
clear: bool,
|
|
) -> Option<wgpu::RenderPassDepthStencilAttachment> {
|
|
let depth = self
|
|
.depth
|
|
.get_or_init(|| DepthBuffer::new(descriptors, self.sample_count, self.size, pool));
|
|
Some(wgpu::RenderPassDepthStencilAttachment {
|
|
view: depth.view(),
|
|
depth_ops: Some(wgpu::Operations {
|
|
load: if clear {
|
|
wgpu::LoadOp::Clear(0.0)
|
|
} else {
|
|
wgpu::LoadOp::Load
|
|
},
|
|
store: true,
|
|
}),
|
|
stencil_ops: Some(wgpu::Operations {
|
|
load: wgpu::LoadOp::Load,
|
|
store: true,
|
|
}),
|
|
})
|
|
}
|
|
|
|
pub fn update_blend_buffer(
|
|
&self,
|
|
descriptors: &Descriptors,
|
|
pool: &mut TexturePool,
|
|
encoder: &mut wgpu::CommandEncoder,
|
|
) -> &BlendBuffer {
|
|
let blend_buffer = self.blend_buffer.get_or_init(|| {
|
|
BlendBuffer::new(
|
|
&descriptors,
|
|
self.size,
|
|
self.format,
|
|
wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
|
pool,
|
|
)
|
|
});
|
|
encoder.copy_texture_to_texture(
|
|
wgpu::ImageCopyTextureBase {
|
|
texture: self
|
|
.resolve_buffer
|
|
.as_ref()
|
|
.map(|b| b.texture())
|
|
.unwrap_or_else(|| self.frame_buffer.texture()),
|
|
mip_level: 0,
|
|
origin: Default::default(),
|
|
aspect: Default::default(),
|
|
},
|
|
wgpu::ImageCopyTextureBase {
|
|
texture: blend_buffer.texture(),
|
|
mip_level: 0,
|
|
origin: Default::default(),
|
|
aspect: Default::default(),
|
|
},
|
|
self.frame_buffer.size(),
|
|
);
|
|
blend_buffer
|
|
}
|
|
|
|
pub fn color_view(&self) -> &wgpu::TextureView {
|
|
self.resolve_buffer
|
|
.as_ref()
|
|
.map(|b| b.view())
|
|
.unwrap_or_else(|| self.frame_buffer.view())
|
|
}
|
|
}
|
|
|
|
pub struct CommandRenderer<'pass, 'frame: 'pass, 'global: 'frame> {
|
|
pipelines: &'frame Pipelines,
|
|
meshes: &'global Vec<Mesh>,
|
|
descriptors: &'global Descriptors,
|
|
num_masks: u32,
|
|
mask_state: MaskState,
|
|
render_pass: wgpu::RenderPass<'pass>,
|
|
uniform_buffers: &'frame mut UniformBuffer<'global, Transforms>,
|
|
uniform_encoder: &'frame mut wgpu::CommandEncoder,
|
|
needs_depth: bool,
|
|
}
|
|
|
|
impl<'pass, 'frame: 'pass, 'global: 'frame> CommandRenderer<'pass, 'frame, 'global> {
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
pipelines: &'frame Pipelines,
|
|
meshes: &'global Vec<Mesh>,
|
|
descriptors: &'global Descriptors,
|
|
uniform_buffers: &'frame mut UniformBuffer<'global, Transforms>,
|
|
uniform_encoder: &'frame mut wgpu::CommandEncoder,
|
|
render_pass: wgpu::RenderPass<'pass>,
|
|
num_masks: u32,
|
|
mask_state: MaskState,
|
|
needs_depth: bool,
|
|
) -> Self {
|
|
Self {
|
|
pipelines,
|
|
meshes,
|
|
num_masks,
|
|
mask_state,
|
|
render_pass,
|
|
descriptors,
|
|
uniform_buffers,
|
|
uniform_encoder,
|
|
needs_depth,
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn execute(
|
|
pipelines: &'frame Pipelines,
|
|
target: &'pass CommandTarget,
|
|
meshes: &'global Vec<Mesh>,
|
|
descriptors: &'global Descriptors,
|
|
uniform_buffers: &'frame mut UniformBuffer<'global, Transforms>,
|
|
uniform_encoder: &'frame mut wgpu::CommandEncoder,
|
|
commands: &CommandList,
|
|
nearest_layer: &'pass CommandTarget,
|
|
clear_color: &mut Option<wgpu::Color>,
|
|
draw_encoder: &'pass mut wgpu::CommandEncoder,
|
|
texture_pool: &mut TexturePool,
|
|
) {
|
|
let mut first = true;
|
|
let mut num_masks = 0;
|
|
let mut mask_state = MaskState::NoMask;
|
|
let (chunks, needs_depth) = chunk_blends(
|
|
&commands.0,
|
|
descriptors,
|
|
uniform_buffers,
|
|
uniform_encoder,
|
|
draw_encoder,
|
|
meshes,
|
|
target.sample_count,
|
|
target.size.width,
|
|
target.size.height,
|
|
nearest_layer,
|
|
texture_pool,
|
|
);
|
|
|
|
for chunk in chunks {
|
|
match chunk {
|
|
Chunk::Draw(chunk) => {
|
|
let mut render_pass =
|
|
draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
label: None,
|
|
color_attachments: &[target.color_attachments(clear_color.take())],
|
|
depth_stencil_attachment: if needs_depth {
|
|
target.depth_attachment(&descriptors, texture_pool, first)
|
|
} else {
|
|
None
|
|
},
|
|
});
|
|
render_pass.set_bind_group(0, target.globals.bind_group(), &[]);
|
|
let mut renderer = CommandRenderer::new(
|
|
&pipelines,
|
|
&meshes,
|
|
&descriptors,
|
|
uniform_buffers,
|
|
uniform_encoder,
|
|
render_pass,
|
|
num_masks,
|
|
mask_state,
|
|
needs_depth,
|
|
);
|
|
|
|
for command in &chunk {
|
|
if needs_depth {
|
|
match renderer.mask_state {
|
|
MaskState::NoMask => {}
|
|
MaskState::DrawMaskStencil => {
|
|
renderer
|
|
.render_pass
|
|
.set_stencil_reference(renderer.num_masks - 1);
|
|
}
|
|
MaskState::DrawMaskedContent => {
|
|
renderer
|
|
.render_pass
|
|
.set_stencil_reference(renderer.num_masks);
|
|
}
|
|
MaskState::ClearMaskStencil => {
|
|
renderer
|
|
.render_pass
|
|
.set_stencil_reference(renderer.num_masks);
|
|
}
|
|
}
|
|
}
|
|
|
|
match command {
|
|
DrawCommand::RenderBitmap {
|
|
bitmap,
|
|
transform,
|
|
smoothing,
|
|
blend_mode,
|
|
} => {
|
|
renderer.render_bitmap(bitmap, &transform, *smoothing, *blend_mode)
|
|
}
|
|
DrawCommand::RenderTexture {
|
|
_texture,
|
|
binds,
|
|
transform,
|
|
blend_mode,
|
|
} => renderer.render_texture(&transform, binds, *blend_mode),
|
|
DrawCommand::RenderShape { shape, transform } => {
|
|
renderer.render_shape(*shape, &transform)
|
|
}
|
|
DrawCommand::DrawRect { color, matrix } => {
|
|
renderer.draw_rect(color, &matrix)
|
|
}
|
|
DrawCommand::PushMask => renderer.push_mask(),
|
|
DrawCommand::ActivateMask => renderer.activate_mask(),
|
|
DrawCommand::DeactivateMask => renderer.deactivate_mask(),
|
|
DrawCommand::PopMask => renderer.pop_mask(),
|
|
}
|
|
}
|
|
|
|
num_masks = renderer.num_masks;
|
|
mask_state = renderer.mask_state;
|
|
}
|
|
Chunk::Blend(texture, blend_mode) => {
|
|
let parent = match blend_mode {
|
|
ComplexBlend::Alpha | ComplexBlend::Erase => nearest_layer,
|
|
_ => target,
|
|
};
|
|
|
|
let parent_blend_buffer =
|
|
parent.update_blend_buffer(&descriptors, texture_pool, draw_encoder);
|
|
|
|
let texture_view = texture.create_view(&Default::default());
|
|
let blend_bind_group =
|
|
descriptors
|
|
.device
|
|
.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
label: None,
|
|
layout: &descriptors.bind_layouts.blend,
|
|
entries: &[
|
|
wgpu::BindGroupEntry {
|
|
binding: 0,
|
|
resource: wgpu::BindingResource::TextureView(
|
|
parent_blend_buffer.view(),
|
|
),
|
|
},
|
|
wgpu::BindGroupEntry {
|
|
binding: 1,
|
|
resource: wgpu::BindingResource::TextureView(&texture_view),
|
|
},
|
|
wgpu::BindGroupEntry {
|
|
binding: 2,
|
|
resource: wgpu::BindingResource::Sampler(
|
|
descriptors.bitmap_samplers.get_sampler(false, false),
|
|
),
|
|
},
|
|
wgpu::BindGroupEntry {
|
|
binding: 3,
|
|
resource: descriptors
|
|
.blend_buffer(blend_mode)
|
|
.as_entire_binding(),
|
|
},
|
|
],
|
|
});
|
|
|
|
let mut render_pass =
|
|
draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
label: None,
|
|
color_attachments: &[target.color_attachments(clear_color.take())],
|
|
depth_stencil_attachment: if needs_depth {
|
|
target.depth_attachment(descriptors, texture_pool, first)
|
|
} else {
|
|
None
|
|
},
|
|
});
|
|
render_pass.set_bind_group(0, target.globals.bind_group(), &[]);
|
|
|
|
if needs_depth {
|
|
match mask_state {
|
|
MaskState::NoMask => {}
|
|
MaskState::DrawMaskStencil => {
|
|
render_pass.set_stencil_reference(num_masks - 1);
|
|
}
|
|
MaskState::DrawMaskedContent => {
|
|
render_pass.set_stencil_reference(num_masks);
|
|
}
|
|
MaskState::ClearMaskStencil => {
|
|
render_pass.set_stencil_reference(num_masks);
|
|
}
|
|
}
|
|
render_pass.set_pipeline(pipelines.complex_blend.pipeline_for(mask_state));
|
|
} else {
|
|
render_pass.set_pipeline(pipelines.complex_blend.depthless_pipeline());
|
|
}
|
|
|
|
render_pass.set_bind_group(1, target.whole_frame_bind_group(descriptors), &[0]);
|
|
render_pass.set_bind_group(2, &blend_bind_group, &[]);
|
|
|
|
render_pass.set_vertex_buffer(0, descriptors.quad.vertices.slice(..));
|
|
render_pass.set_index_buffer(
|
|
descriptors.quad.indices.slice(..),
|
|
wgpu::IndexFormat::Uint32,
|
|
);
|
|
|
|
render_pass.draw_indexed(0..6, 0, 0..1);
|
|
drop(render_pass);
|
|
}
|
|
}
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
pub fn prep_color(&mut self) {
|
|
if self.needs_depth {
|
|
self.render_pass
|
|
.set_pipeline(self.pipelines.color.pipeline_for(self.mask_state));
|
|
} else {
|
|
self.render_pass
|
|
.set_pipeline(self.pipelines.color.depthless_pipeline());
|
|
}
|
|
}
|
|
|
|
pub fn prep_gradient(&mut self, bind_group: &'pass wgpu::BindGroup) {
|
|
if self.needs_depth {
|
|
self.render_pass
|
|
.set_pipeline(self.pipelines.gradient.pipeline_for(self.mask_state));
|
|
} else {
|
|
self.render_pass
|
|
.set_pipeline(self.pipelines.gradient.depthless_pipeline());
|
|
}
|
|
|
|
self.render_pass.set_bind_group(2, bind_group, &[]);
|
|
}
|
|
|
|
pub fn prep_bitmap(&mut self, bind_group: &'pass wgpu::BindGroup, blend_mode: TrivialBlend) {
|
|
if self.needs_depth {
|
|
self.render_pass
|
|
.set_pipeline(self.pipelines.bitmap[blend_mode].pipeline_for(self.mask_state));
|
|
} else {
|
|
self.render_pass
|
|
.set_pipeline(self.pipelines.bitmap[blend_mode].depthless_pipeline());
|
|
}
|
|
|
|
self.render_pass.set_bind_group(2, bind_group, &[]);
|
|
}
|
|
|
|
pub fn draw(
|
|
&mut self,
|
|
vertices: wgpu::BufferSlice<'pass>,
|
|
indices: wgpu::BufferSlice<'pass>,
|
|
num_indices: u32,
|
|
) {
|
|
self.render_pass.set_vertex_buffer(0, vertices);
|
|
self.render_pass
|
|
.set_index_buffer(indices, wgpu::IndexFormat::Uint32);
|
|
|
|
self.render_pass.draw_indexed(0..num_indices, 0, 0..1);
|
|
}
|
|
|
|
pub fn apply_transform(
|
|
&mut self,
|
|
matrix: &ruffle_render::matrix::Matrix,
|
|
color_adjustments: ColorAdjustments,
|
|
) {
|
|
let world_matrix = [
|
|
[matrix.a, matrix.b, 0.0, 0.0],
|
|
[matrix.c, matrix.d, 0.0, 0.0],
|
|
[0.0, 0.0, 1.0, 0.0],
|
|
[
|
|
matrix.tx.to_pixels() as f32,
|
|
matrix.ty.to_pixels() as f32,
|
|
0.0,
|
|
1.0,
|
|
],
|
|
];
|
|
|
|
self.uniform_buffers.write_uniforms(
|
|
&self.descriptors.device,
|
|
&self.descriptors.bind_layouts.transforms,
|
|
&mut self.uniform_encoder,
|
|
&mut self.render_pass,
|
|
1,
|
|
&Transforms {
|
|
world_matrix,
|
|
color_adjustments,
|
|
},
|
|
);
|
|
}
|
|
|
|
fn render_bitmap(
|
|
&mut self,
|
|
bitmap: &'frame BitmapHandle,
|
|
transform: &Transform,
|
|
smoothing: bool,
|
|
blend_mode: TrivialBlend,
|
|
) {
|
|
let texture = as_texture(bitmap);
|
|
|
|
self.apply_transform(
|
|
&(transform.matrix
|
|
* ruffle_render::matrix::Matrix {
|
|
a: texture.width as f32,
|
|
d: texture.height as f32,
|
|
..Default::default()
|
|
}),
|
|
ColorAdjustments::from(transform.color_transform),
|
|
);
|
|
let descriptors = self.descriptors;
|
|
let bind = texture.bind_group(
|
|
smoothing,
|
|
&descriptors.device,
|
|
&descriptors.bind_layouts.bitmap,
|
|
&descriptors.quad,
|
|
bitmap.clone(),
|
|
&descriptors.bitmap_samplers,
|
|
);
|
|
|
|
self.prep_bitmap(&bind.bind_group, blend_mode);
|
|
|
|
self.draw(
|
|
self.descriptors.quad.vertices.slice(..),
|
|
self.descriptors.quad.indices.slice(..),
|
|
6,
|
|
);
|
|
}
|
|
|
|
fn render_texture(
|
|
&mut self,
|
|
transform: &Transform,
|
|
bind_group: &'frame wgpu::BindGroup,
|
|
blend_mode: TrivialBlend,
|
|
) {
|
|
self.apply_transform(
|
|
&transform.matrix,
|
|
ColorAdjustments::from(transform.color_transform),
|
|
);
|
|
self.prep_bitmap(bind_group, blend_mode);
|
|
|
|
self.draw(
|
|
self.descriptors.quad.vertices.slice(..),
|
|
self.descriptors.quad.indices.slice(..),
|
|
6,
|
|
);
|
|
}
|
|
|
|
fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) {
|
|
self.apply_transform(
|
|
&transform.matrix,
|
|
ColorAdjustments::from(transform.color_transform),
|
|
);
|
|
|
|
let mesh = &self.meshes[shape.0];
|
|
for draw in &mesh.draws {
|
|
let num_indices = if self.mask_state != MaskState::DrawMaskStencil
|
|
&& self.mask_state != MaskState::ClearMaskStencil
|
|
{
|
|
draw.num_indices
|
|
} else {
|
|
// Omit strokes when drawing a mask stencil.
|
|
draw.num_mask_indices
|
|
};
|
|
if num_indices == 0 {
|
|
continue;
|
|
}
|
|
|
|
match &draw.draw_type {
|
|
DrawType::Color => {
|
|
self.prep_color();
|
|
}
|
|
DrawType::Gradient { bind_group, .. } => {
|
|
self.prep_gradient(bind_group);
|
|
}
|
|
DrawType::Bitmap { binds, .. } => {
|
|
self.prep_bitmap(&binds.bind_group, TrivialBlend::Normal);
|
|
}
|
|
}
|
|
|
|
self.draw(
|
|
draw.vertex_buffer.slice(..),
|
|
draw.index_buffer.slice(..),
|
|
num_indices,
|
|
);
|
|
}
|
|
}
|
|
|
|
fn draw_rect(&mut self, color: &Color, matrix: &ruffle_render::matrix::Matrix) {
|
|
self.apply_transform(
|
|
&matrix,
|
|
ColorAdjustments {
|
|
mult_color: [
|
|
f32::from(color.r) / 255.0,
|
|
f32::from(color.g) / 255.0,
|
|
f32::from(color.b) / 255.0,
|
|
f32::from(color.a) / 255.0,
|
|
],
|
|
add_color: [0.0, 0.0, 0.0, 0.0],
|
|
},
|
|
);
|
|
|
|
self.prep_color();
|
|
self.draw(
|
|
self.descriptors.quad.vertices.slice(..),
|
|
self.descriptors.quad.indices.slice(..),
|
|
6,
|
|
);
|
|
}
|
|
|
|
fn push_mask(&mut self) {
|
|
debug_assert!(
|
|
self.mask_state == MaskState::NoMask || self.mask_state == MaskState::DrawMaskedContent
|
|
);
|
|
self.num_masks += 1;
|
|
self.mask_state = MaskState::DrawMaskStencil;
|
|
self.render_pass.set_stencil_reference(self.num_masks - 1);
|
|
}
|
|
|
|
fn activate_mask(&mut self) {
|
|
debug_assert!(self.num_masks > 0 && self.mask_state == MaskState::DrawMaskStencil);
|
|
self.mask_state = MaskState::DrawMaskedContent;
|
|
self.render_pass.set_stencil_reference(self.num_masks);
|
|
}
|
|
|
|
fn deactivate_mask(&mut self) {
|
|
debug_assert!(self.num_masks > 0 && self.mask_state == MaskState::DrawMaskedContent);
|
|
self.mask_state = MaskState::ClearMaskStencil;
|
|
self.render_pass.set_stencil_reference(self.num_masks);
|
|
}
|
|
|
|
fn pop_mask(&mut self) {
|
|
debug_assert!(self.num_masks > 0 && self.mask_state == MaskState::ClearMaskStencil);
|
|
self.num_masks -= 1;
|
|
self.render_pass.set_stencil_reference(self.num_masks);
|
|
if self.num_masks == 0 {
|
|
self.mask_state = MaskState::NoMask;
|
|
} else {
|
|
self.mask_state = MaskState::DrawMaskedContent;
|
|
};
|
|
}
|
|
}
|
|
|
|
pub enum Chunk {
|
|
Draw(Vec<DrawCommand>),
|
|
Blend(PoolEntry<wgpu::Texture>, ComplexBlend),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum DrawCommand {
|
|
RenderBitmap {
|
|
bitmap: BitmapHandle,
|
|
transform: Transform,
|
|
smoothing: bool,
|
|
blend_mode: TrivialBlend,
|
|
},
|
|
RenderTexture {
|
|
_texture: PoolEntry<wgpu::Texture>,
|
|
binds: wgpu::BindGroup,
|
|
transform: Transform,
|
|
blend_mode: TrivialBlend,
|
|
},
|
|
RenderShape {
|
|
shape: ShapeHandle,
|
|
transform: Transform,
|
|
},
|
|
DrawRect {
|
|
color: Color,
|
|
matrix: Matrix,
|
|
},
|
|
PushMask,
|
|
ActivateMask,
|
|
DeactivateMask,
|
|
PopMask,
|
|
}
|
|
|
|
/// Replaces every blend with a RenderBitmap, with the subcommands rendered out to a temporary texture
|
|
/// Every complex blend will be its own item, but every other draw will be chunked together
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn chunk_blends<'a>(
|
|
commands: &[Command],
|
|
descriptors: &'a Descriptors,
|
|
uniform_buffers: &mut UniformBuffer<'a, Transforms>,
|
|
uniform_encoder: &mut wgpu::CommandEncoder,
|
|
draw_encoder: &mut wgpu::CommandEncoder,
|
|
meshes: &'a Vec<Mesh>,
|
|
sample_count: u32,
|
|
width: u32,
|
|
height: u32,
|
|
nearest_layer: &CommandTarget,
|
|
texture_pool: &mut TexturePool,
|
|
) -> (Vec<Chunk>, bool) {
|
|
let mut result = vec![];
|
|
let mut current = vec![];
|
|
let mut needs_depth = false;
|
|
|
|
for command in commands {
|
|
match command {
|
|
Command::Blend(commands, blend_mode) => {
|
|
let mut surface = Surface::new(
|
|
&descriptors,
|
|
sample_count,
|
|
width,
|
|
height,
|
|
wgpu::TextureFormat::Rgba8Unorm,
|
|
);
|
|
let target = surface.draw_commands(
|
|
Some(BlendType::from(*blend_mode).default_color()),
|
|
&descriptors,
|
|
&meshes,
|
|
commands,
|
|
uniform_buffers,
|
|
uniform_encoder,
|
|
draw_encoder,
|
|
if blend_mode == &BlendMode::Layer {
|
|
None
|
|
} else {
|
|
Some(nearest_layer)
|
|
},
|
|
texture_pool,
|
|
);
|
|
|
|
match BlendType::from(*blend_mode) {
|
|
BlendType::Trivial(blend_mode) => {
|
|
let transform = Transform {
|
|
matrix: Matrix::scale(target.width() as f32, target.height() as f32),
|
|
color_transform: Default::default(),
|
|
};
|
|
let texture = target.take_color_texture();
|
|
let binds = BitmapBinds::new(
|
|
&descriptors.device,
|
|
&descriptors.bind_layouts.bitmap,
|
|
descriptors.bitmap_samplers.get_sampler(false, false),
|
|
&descriptors.quad.texture_transforms,
|
|
texture.create_view(&Default::default()),
|
|
None,
|
|
);
|
|
current.push(DrawCommand::RenderTexture {
|
|
_texture: texture,
|
|
binds: binds.bind_group,
|
|
transform,
|
|
blend_mode,
|
|
})
|
|
}
|
|
BlendType::Complex(blend_mode) => {
|
|
if !current.is_empty() {
|
|
result.push(Chunk::Draw(std::mem::take(&mut current)));
|
|
}
|
|
result.push(Chunk::Blend(target.take_color_texture(), blend_mode));
|
|
}
|
|
}
|
|
}
|
|
Command::RenderBitmap {
|
|
bitmap,
|
|
transform,
|
|
smoothing,
|
|
} => current.push(DrawCommand::RenderBitmap {
|
|
bitmap: bitmap.to_owned(),
|
|
transform: transform.to_owned(),
|
|
smoothing: *smoothing,
|
|
blend_mode: TrivialBlend::Normal,
|
|
}),
|
|
Command::RenderShape { shape, transform } => current.push(DrawCommand::RenderShape {
|
|
shape: shape.to_owned(),
|
|
transform: transform.to_owned(),
|
|
}),
|
|
Command::DrawRect { color, matrix } => current.push(DrawCommand::DrawRect {
|
|
color: color.to_owned(),
|
|
matrix: matrix.to_owned(),
|
|
}),
|
|
Command::PushMask => {
|
|
needs_depth = true;
|
|
current.push(DrawCommand::PushMask);
|
|
}
|
|
Command::ActivateMask => {
|
|
needs_depth = true;
|
|
current.push(DrawCommand::ActivateMask);
|
|
}
|
|
Command::DeactivateMask => {
|
|
needs_depth = true;
|
|
current.push(DrawCommand::DeactivateMask);
|
|
}
|
|
Command::PopMask => {
|
|
needs_depth = true;
|
|
current.push(DrawCommand::PopMask);
|
|
}
|
|
}
|
|
}
|
|
|
|
if !current.is_empty() {
|
|
result.push(Chunk::Draw(current));
|
|
}
|
|
|
|
(result, needs_depth)
|
|
}
|