ruffle/render/wgpu/src/surface.rs

384 lines
15 KiB
Rust
Raw Normal View History

mod commands;
pub mod target;
use crate::backend::RenderTargetMode;
use crate::blend::ComplexBlend;
use crate::buffer_pool::TexturePool;
use crate::filters::FilterSource;
use crate::mesh::Mesh;
use crate::pixel_bender::{run_pixelbender_shader_impl, ShaderMode};
use crate::surface::commands::{chunk_blends, Chunk, CommandRenderer};
use crate::utils::{remove_srgb, supported_sample_count};
use crate::{ColorAdjustments, Descriptors, MaskState, Pipelines, Transforms, UniformBuffer};
use ruffle_render::commands::CommandList;
use ruffle_render::pixel_bender::{ImageInputTexture, PixelBenderShaderArgument};
use ruffle_render::quality::StageQuality;
use std::sync::Arc;
use target::CommandTarget;
use tracing::instrument;
2023-06-23 22:41:29 +00:00
use crate::utils::run_copy_pipeline;
pub use crate::surface::commands::LayerRef;
use self::commands::ChunkBlendMode;
#[derive(Debug)]
pub struct Surface {
size: wgpu::Extent3d,
quality: StageQuality,
sample_count: u32,
pipelines: Arc<Pipelines>,
format: wgpu::TextureFormat,
actual_surface_format: wgpu::TextureFormat,
}
impl Surface {
pub fn new(
descriptors: &Descriptors,
quality: StageQuality,
width: u32,
height: u32,
surface_format: wgpu::TextureFormat,
) -> Self {
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let frame_buffer_format = remove_srgb(surface_format);
let sample_count = supported_sample_count(
&descriptors.adapter,
quality.sample_count(),
frame_buffer_format,
);
let pipelines = descriptors.pipelines(sample_count, frame_buffer_format);
Self {
size,
quality,
sample_count,
pipelines,
format: frame_buffer_format,
actual_surface_format: surface_format,
}
}
#[allow(clippy::too_many_arguments)]
#[instrument(level = "debug", skip_all)]
pub fn draw_commands_and_copy_to<'frame, 'global: 'frame>(
&mut self,
frame_view: &wgpu::TextureView,
render_target_mode: RenderTargetMode,
descriptors: &'global Descriptors,
uniform_buffers: &'frame mut UniformBuffer<'global, Transforms>,
color_buffers: &'frame mut UniformBuffer<'global, ColorAdjustments>,
uniform_encoder: &'frame mut wgpu::CommandEncoder,
draw_encoder: &'frame mut wgpu::CommandEncoder,
meshes: &'global Vec<Mesh>,
commands: CommandList,
layer: LayerRef,
texture_pool: &mut TexturePool,
) {
let target = self.draw_commands(
render_target_mode,
descriptors,
meshes,
commands,
uniform_buffers,
color_buffers,
uniform_encoder,
draw_encoder,
layer,
texture_pool,
);
run_copy_pipeline(
descriptors,
self.format,
self.actual_surface_format,
self.size,
frame_view,
target.color_view(),
target.whole_frame_bind_group(descriptors),
target.globals(),
1,
draw_encoder,
);
}
#[allow(clippy::too_many_arguments)]
#[instrument(level = "debug", skip_all)]
pub fn draw_commands<'frame, 'global: 'frame>(
&mut self,
render_target_mode: RenderTargetMode,
descriptors: &'global Descriptors,
meshes: &'global Vec<Mesh>,
commands: CommandList,
uniform_buffers: &'frame mut UniformBuffer<'global, Transforms>,
color_buffers: &'frame mut UniformBuffer<'global, ColorAdjustments>,
uniform_encoder: &'frame mut wgpu::CommandEncoder,
draw_encoder: &'frame mut wgpu::CommandEncoder,
nearest_layer: LayerRef<'frame>,
texture_pool: &mut TexturePool,
) -> CommandTarget {
let target = CommandTarget::new(
2023-02-11 18:28:53 +00:00
descriptors,
texture_pool,
self.size,
self.format,
self.sample_count,
render_target_mode,
draw_encoder,
);
let mut num_masks = 0;
let mut mask_state = MaskState::NoMask;
let chunks = chunk_blends(
commands.commands,
descriptors,
uniform_buffers,
color_buffers,
uniform_encoder,
draw_encoder,
meshes,
self.quality,
target.width(),
target.height(),
match nearest_layer {
LayerRef::Current => LayerRef::Parent(&target),
layer => layer,
},
texture_pool,
);
for chunk in chunks {
match chunk {
Chunk::Draw(chunk, needs_stencil) => {
let mut render_pass =
draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: create_debug_label!(
"Chunked draw calls {}",
if needs_stencil {
"(with stencil)"
} else {
"(Stencilless)"
}
)
.as_deref(),
color_attachments: &[target.color_attachments()],
depth_stencil_attachment: if needs_stencil {
target.stencil_attachment(descriptors, texture_pool)
} else {
None
},
..Default::default()
});
render_pass.set_bind_group(0, target.globals().bind_group(), &[]);
let mut renderer = CommandRenderer::new(
&self.pipelines,
2023-02-11 18:28:53 +00:00
descriptors,
uniform_buffers,
color_buffers,
uniform_encoder,
render_pass,
num_masks,
mask_state,
needs_stencil,
);
for command in &chunk {
renderer.execute(command);
}
num_masks = renderer.num_masks();
mask_state = renderer.mask_state();
}
Chunk::Blend(texture, ChunkBlendMode::Shader(shader), needs_stencil) => {
assert!(
!needs_stencil,
"Shader blend should not need stencil buffer"
);
let parent_blend_buffer =
target.update_blend_buffer(descriptors, texture_pool, draw_encoder);
run_pixelbender_shader_impl(
descriptors,
shader,
ShaderMode::Filter,
&[
PixelBenderShaderArgument::ImageInput {
index: 0,
channels: 0xFF,
name: "background".to_string(),
texture: Some(ImageInputTexture::TextureRef(
parent_blend_buffer.texture(),
)),
},
PixelBenderShaderArgument::ImageInput {
index: 1,
channels: 0xff,
name: "foreground".to_string(),
texture: Some(ImageInputTexture::TextureRef(texture.texture())),
},
],
parent_blend_buffer.texture(),
draw_encoder,
target.color_attachments(),
target.sample_count(),
&FilterSource::for_entire_texture(texture.texture()),
)
.expect("Failed to run PixelBender blend mode");
}
Chunk::Blend(texture, ChunkBlendMode::Complex(blend_mode), needs_stencil) => {
let parent = match blend_mode {
ComplexBlend::Alpha | ComplexBlend::Erase => {
match nearest_layer {
LayerRef::None => {
// An Alpha or Erase with no Layer above it should be ignored
continue;
}
LayerRef::Current => &target,
LayerRef::Parent(layer) => layer,
}
}
_ => &target,
};
let parent_blend_buffer =
2023-02-11 18:28:53 +00:00
parent.update_blend_buffer(descriptors, texture_pool, draw_encoder);
let blend_bind_group =
descriptors
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
label: create_debug_label!(
"Complex blend binds {:?} {}",
blend_mode,
if needs_stencil {
"(with stencil)"
} else {
"(Stencilless)"
}
)
.as_deref(),
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),
),
},
],
});
let mut render_pass =
draw_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: create_debug_label!(
"Complex blend {:?} {}",
blend_mode,
if needs_stencil {
"(with stencil)"
} else {
"(Stencilless)"
}
)
.as_deref(),
color_attachments: &[target.color_attachments()],
depth_stencil_attachment: if needs_stencil {
target.stencil_attachment(descriptors, texture_pool)
} else {
None
},
..Default::default()
});
render_pass.set_bind_group(0, target.globals().bind_group(), &[]);
if needs_stencil {
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(
self.pipelines.complex_blends[blend_mode].pipeline_for(mask_state),
);
} else {
render_pass.set_pipeline(
self.pipelines.complex_blends[blend_mode].stencilless_pipeline(),
);
}
if descriptors.limits.max_push_constant_size > 0 {
render_pass.set_push_constants(
wgpu::ShaderStages::VERTEX,
0,
bytemuck::cast_slice(&[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],
],
}]),
);
render_pass.set_bind_group(1, &blend_bind_group, &[]);
} else {
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_pos.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);
}
}
}
// If nothing happened, ensure it's cleared so we don't operate on garbage data
target.ensure_cleared(draw_encoder);
target
}
pub fn quality(&self) -> StageQuality {
self.quality
}
pub fn sample_count(&self) -> u32 {
self.sample_count
}
pub fn size(&self) -> wgpu::Extent3d {
self.size
}
}