Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wgpu: Implement blend modes #8262

Merged
merged 54 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
ebf7f3d
render: Replace PushBlendMode/PopBlendMode with Blend
Dinnerbone Sep 20, 2022
0d8047a
wgpu: Removed blendmode stuff
Dinnerbone Sep 20, 2022
7504dae
wgpu: Separated srgb out of Surface
Dinnerbone Sep 20, 2022
e43446b
wgpu: Frame no longer owns UniformBuffer
Dinnerbone Sep 20, 2022
a2a586c
wgpu: Simplify Surface to no longer be an enum as there's not really …
Dinnerbone Sep 20, 2022
616229b
wgpu: Globals should now belong to the Surface and doesn't need to be…
Dinnerbone Sep 20, 2022
85d71e5
wgpu: Perform command rendering recursively and chunk up blends. Blen…
Dinnerbone Sep 21, 2022
dc9c4cc
wgpu: Implement all blend modes
Dinnerbone Dec 18, 2022
2337ef5
wgpu: Always clear the first texture buffer to the desired color
Dinnerbone Dec 18, 2022
aa07f60
wgpu: Allow for different sized subcommand list targets, but don't us…
Dinnerbone Dec 18, 2022
32cb327
wgpu: For trivial blends (ie Normal), just draw them as bitmaps with …
Dinnerbone Dec 20, 2022
33c3924
wgpu: Workaround naga-vulkan bug with default position in switch stat…
Dinnerbone Dec 20, 2022
ae4409b
wgpu: Don't create blend buffers until they're needed
Dinnerbone Dec 20, 2022
a597e6a
tests: Added new output of bitmapdata_draw tests for github agents. S…
Dinnerbone Dec 20, 2022
d846f53
core: Remove blend mode unsupported message
Dinnerbone Dec 20, 2022
4b91528
tests: Add blendmode visual tests
Dinnerbone Dec 20, 2022
df74206
wgpu: Handle alpha correctly for add or subtract blendmodes
Dinnerbone Dec 21, 2022
7d34741
wgpu: Appease beta clippy
Dinnerbone Dec 21, 2022
1720d26
wgpu: Don't create depth buffers, or use any depth testing, if we are…
Dinnerbone Dec 21, 2022
b3ef530
wgpu: Optimise rendering blendables by queueing everything up in the …
Dinnerbone Dec 22, 2022
6a136fc
wgpu: Don't require VERTEX_WRITABLE_STORAGE which is unavailable on web
Dinnerbone Dec 22, 2022
1a8b87f
wgpu: Pool textures during the same frame, and drop whatever we don't…
Dinnerbone Dec 22, 2022
1613623
wgpu: Cache Globals for a total draw frame, don't remake it if it's g…
Dinnerbone Dec 22, 2022
e8b692a
wgpu: Made Multiply, Add and Subtract blend modes use bitmap+blend
Dinnerbone Dec 23, 2022
ffe9b5f
wgpu: Share the buffer pool throughout the entire frame
Dinnerbone Dec 23, 2022
13023e5
wgpu: Don't wait for the entire frame to be drawn if we're just captu…
Dinnerbone Dec 23, 2022
a6a88af
wgpu: Default msaa on mobiles to 2x, same as webgl backend
Dinnerbone Dec 23, 2022
1a834b1
wgpu: Removed some needless copies in command iteration
Dinnerbone Dec 23, 2022
a6f3c7c
tests: New multiply blend mode image
Dinnerbone Dec 23, 2022
aaa1c5c
wgpu: Multiply can't be trivial blend mode because of 0 alpha
Dinnerbone Dec 23, 2022
92ab825
wgpu: Try harder to find render passes where we don't need depth
Dinnerbone Dec 23, 2022
fd26461
wgpu: Simplify blend_buffers map creation
Dinnerbone Dec 23, 2022
4679e11
wgpu: Revert using same pool for whole frame - breaks bitmapdata.draw…
Dinnerbone Dec 23, 2022
1f07803
wgpu: Temporarily disable msaa on phones until wgpu upgrade
Dinnerbone Dec 23, 2022
4b2a7b0
wgpu: Add some more debug labels
Dinnerbone Dec 23, 2022
ef12363
wgpu: Add more debug labels within render passes
Dinnerbone Dec 23, 2022
55bd8af
wgpu: Split up blend modes into their own shaders for performance
Dinnerbone Dec 23, 2022
31c447a
wgpu: Share texture pool across all frames, and ensure targets are cl…
Dinnerbone Dec 23, 2022
a5975ac
wgpu: Reduce gradient.colors uniform memory by 4x
Dinnerbone Dec 24, 2022
c998888
wgpu: Reduce gradient.ratios uniform memory by 4x
Dinnerbone Dec 24, 2022
2b0d2d8
wgpu: Appease beta clippy. again.
Dinnerbone Dec 24, 2022
dba9cf2
wgpu: Clean up gradient shaders by bringing out common shared source …
Dinnerbone Dec 24, 2022
2753094
wgpu: Split up gradient shader based on type and repeat
Dinnerbone Dec 25, 2022
754c814
wgpu: Clear out texture pool when resized
Dinnerbone Dec 27, 2022
2eca4f6
wgpu: Refactor surface command target to its own file
Dinnerbone Dec 27, 2022
dcce43a
wgpu: Move command renderer to surface/commands.rs
Dinnerbone Dec 27, 2022
63915ae
wgpu: Inlined CommandRenderer::execute into Surface::draw_commands
Dinnerbone Dec 27, 2022
39c1d95
wgpu: Moved DrawCommand handling to CommandRenderer::execute
Dinnerbone Dec 27, 2022
02f8435
wgpu: Define the sizes of uniforms upfront instead of at each call
Dinnerbone Dec 27, 2022
7552c83
wgpu: Moved ColorAdjustments into its own bind group, and reuse Color…
Dinnerbone Dec 27, 2022
0703f5f
render: Make CommandList a struct instead of a tuple
Dinnerbone Dec 27, 2022
48d3ffa
wgpu: Cleaned up some buffer binding api
Dinnerbone Dec 28, 2022
7bece49
wgpu: Don't load or store the depth side of the depth buffer, we only…
Dinnerbone Dec 28, 2022
5619304
wgpu: Use frame-temporary buffer for offscreen rendering
Dinnerbone Jan 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions core/src/bitmap/bitmap_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1000,11 +1000,10 @@ impl<'gc> BitmapData<'gc> {
let mut transform_stack = ruffle_render::transform::TransformStack::new();
transform_stack.push(&transform);
let handle = self.bitmap_handle(context.renderer).unwrap();
let mut commands = CommandList::new();

let mut render_context = RenderContext {
renderer: context.renderer,
commands: &mut commands,
commands: CommandList::new(),
gc_context: context.gc_context,
ui: context.ui,
library: &context.library,
Expand All @@ -1016,7 +1015,6 @@ impl<'gc> BitmapData<'gc> {
};

// Make the screen opacity match the opacity of this bitmap
render_context.commands.push_blend_mode(blend_mode);
match &mut source {
IBitmapDrawable::BitmapData(data) => {
// if try_write fails,
Expand All @@ -1037,10 +1035,17 @@ impl<'gc> BitmapData<'gc> {
object.render_self(&mut render_context);
}
}
render_context.commands.pop_blend_mode();

self.update_dirty_texture(&mut render_context);

let commands = if blend_mode == BlendMode::Normal {
render_context.commands
} else {
let mut commands = CommandList::new();
commands.blend(&render_context.commands, blend_mode);
commands
};

let image = context.renderer.render_offscreen(
handle,
bitmapdata_width,
Expand Down
2 changes: 1 addition & 1 deletion core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ pub struct RenderContext<'a, 'gc, 'gc_context> {
pub renderer: &'a mut dyn RenderBackend,

/// The command list, used by the display objects to draw themselves.
pub commands: &'a mut CommandList,
pub commands: CommandList,

/// The GC MutationContext, used to perform any GcCell writes
/// that must occur during rendering.
Expand Down
20 changes: 9 additions & 11 deletions core/src/display_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,6 @@ impl<'gc> DisplayObjectBase<'gc> {
}

fn set_blend_mode(&mut self, value: BlendMode) {
if value != BlendMode::Normal {
log::warn!(
"Blend mode '{}' is unsupported and will not render correctly.",
value
);
}
self.blend_mode = value;
}

Expand Down Expand Up @@ -512,9 +506,11 @@ pub fn render_base<'gc>(this: DisplayObject<'gc>, context: &mut RenderContext<'_
}
context.transform_stack.push(this.base().transform());
let blend_mode = this.blend_mode();
if blend_mode != BlendMode::Normal {
context.commands.push_blend_mode(this.blend_mode());
}
let original_commands = if blend_mode != BlendMode::Normal {
Some(std::mem::take(&mut context.commands))
} else {
None
};

let scroll_rect_matrix = if let Some(rect) = this.scroll_rect() {
let cur_transform = context.transform_stack.transform();
Expand Down Expand Up @@ -590,8 +586,10 @@ pub fn render_base<'gc>(this: DisplayObject<'gc>, context: &mut RenderContext<'_
context.allow_mask = true;
context.commands.pop_mask();
}
if blend_mode != BlendMode::Normal {
context.commands.pop_blend_mode();

if let Some(original_commands) = original_commands {
let sub_commands = std::mem::replace(&mut context.commands, original_commands);
context.commands.blend(&sub_commands, blend_mode);
}

if scroll_rect_matrix.is_some() {
Expand Down
7 changes: 4 additions & 3 deletions core/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1450,16 +1450,15 @@ impl Player {
pub fn render(&mut self) {
let (renderer, ui, transform_stack) =
(&mut self.renderer, &mut self.ui, &mut self.transform_stack);
let mut commands = CommandList::new();
let mut background_color = Color::WHITE;

self.gc_arena.borrow().mutate(|gc_context, gc_root| {
let commands = self.gc_arena.borrow().mutate(|gc_context, gc_root| {
let root_data = gc_root.data.read();
let stage = root_data.stage;

let mut render_context = RenderContext {
renderer: renderer.deref_mut(),
commands: &mut commands,
commands: CommandList::new(),
gc_context,
ui: ui.deref_mut(),
library: &root_data.library,
Expand All @@ -1478,6 +1477,8 @@ impl Player {
} else {
Color::from_rgba(0)
};

render_context.commands
});

renderer.submit_frame(background_color, commands);
Expand Down
2 changes: 1 addition & 1 deletion exporter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ fn take_screenshot(
.map_err(|e| anyhow!(e.to_string()))?;
let player = PlayerBuilder::new()
.with_renderer(
WgpuRenderBackend::new(descriptors, target).map_err(|e| anyhow!(e.to_string()))?,
WgpuRenderBackend::new(descriptors, target, 4).map_err(|e| anyhow!(e.to_string()))?,
)
.with_movie(movie)
.with_viewport_dimensions(width, height, size.scale)
Expand Down
1 change: 1 addition & 0 deletions render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lyon = { version = "1.0.1", optional = true }
thiserror = "1.0"
wasm-bindgen = { version = "=0.2.83", optional = true }
gc-arena = { git = "https://github.com/ruffle-rs/gc-arena" }
enum-map = "2.4.2"

[dependencies.jpeg-decoder]
version = "0.3.0"
Expand Down
34 changes: 20 additions & 14 deletions render/canvas/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,22 @@ impl WebCanvasRenderBackend {

self.mask_state = MaskState::DrawContent;
}

fn push_blend_mode(&mut self, blend: BlendMode) {
if Some(&blend) != self.blend_modes.last() {
self.apply_blend_mode(blend);
}
self.blend_modes.push(blend);
}

fn pop_blend_mode(&mut self) {
let old = self.blend_modes.pop();
// We should never pop our base 'BlendMode::Normal'
let current = *self.blend_modes.last().unwrap();
if old != Some(current) {
self.apply_blend_mode(current);
}
}
}

impl RenderBackend for WebCanvasRenderBackend {
Expand Down Expand Up @@ -750,20 +766,10 @@ impl<'a> CommandHandler<'a> for WebCanvasRenderBackend {
}
}

fn push_blend_mode(&mut self, blend: BlendMode) {
if Some(&blend) != self.blend_modes.last() {
self.apply_blend_mode(blend);
}
self.blend_modes.push(blend);
}

fn pop_blend_mode(&mut self) {
let old = self.blend_modes.pop();
// We should never pop our base 'BlendMode::Normal'
let current = *self.blend_modes.last().unwrap();
if old != Some(current) {
self.apply_blend_mode(current);
}
fn blend(&mut self, commands: &'a CommandList, blend: BlendMode) {
self.push_blend_mode(blend);
commands.execute(self);
self.pop_blend_mode();
}
}

Expand Down
42 changes: 19 additions & 23 deletions render/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,21 @@ pub trait CommandHandler<'a> {
fn deactivate_mask(&mut self);
fn pop_mask(&mut self);

fn push_blend_mode(&mut self, blend: BlendMode);
fn pop_blend_mode(&mut self);
fn blend(&mut self, commands: &'a CommandList, blend_mode: BlendMode);
}

#[derive(Debug, Default)]
pub struct CommandList(Vec<Command>);
#[derive(Debug, Default, Clone)]
pub struct CommandList {
pub commands: Vec<Command>,
}

impl CommandList {
pub fn new() -> Self {
Self::default()
}

pub fn execute<'a>(&'a self, handler: &mut impl CommandHandler<'a>) {
for command in &self.0 {
for command in &self.commands {
match command {
Command::RenderBitmap {
bitmap,
Expand All @@ -41,62 +42,58 @@ impl CommandList {
Command::ActivateMask => handler.activate_mask(),
Command::DeactivateMask => handler.deactivate_mask(),
Command::PopMask => handler.pop_mask(),
Command::PushBlendMode(blend) => handler.push_blend_mode(*blend),
Command::PopBlendMode => handler.pop_blend_mode(),
Command::Blend(commands, blend_mode) => handler.blend(commands, *blend_mode),
}
}
}
}

impl<'a> CommandHandler<'a> for CommandList {
fn render_bitmap(&mut self, bitmap: &'a BitmapHandle, transform: &Transform, smoothing: bool) {
self.0.push(Command::RenderBitmap {
self.commands.push(Command::RenderBitmap {
bitmap: bitmap.clone(),
transform: transform.clone(),
smoothing,
});
}

fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) {
self.0.push(Command::RenderShape {
self.commands.push(Command::RenderShape {
shape,
transform: transform.clone(),
});
}

fn draw_rect(&mut self, color: Color, matrix: &Matrix) {
self.0.push(Command::DrawRect {
self.commands.push(Command::DrawRect {
color,
matrix: *matrix,
});
}

fn push_mask(&mut self) {
self.0.push(Command::PushMask);
self.commands.push(Command::PushMask);
}

fn activate_mask(&mut self) {
self.0.push(Command::ActivateMask);
self.commands.push(Command::ActivateMask);
}

fn deactivate_mask(&mut self) {
self.0.push(Command::DeactivateMask);
self.commands.push(Command::DeactivateMask);
}

fn pop_mask(&mut self) {
self.0.push(Command::PopMask);
}

fn push_blend_mode(&mut self, blend: BlendMode) {
self.0.push(Command::PushBlendMode(blend));
self.commands.push(Command::PopMask);
}

fn pop_blend_mode(&mut self) {
self.0.push(Command::PopBlendMode);
fn blend(&mut self, commands: &'a CommandList, blend_mode: BlendMode) {
self.commands
.push(Command::Blend(commands.to_owned(), blend_mode));
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum Command {
RenderBitmap {
bitmap: BitmapHandle,
Expand All @@ -115,6 +112,5 @@ pub enum Command {
ActivateMask,
DeactivateMask,
PopMask,
PushBlendMode(BlendMode),
PopBlendMode,
Blend(CommandList, BlendMode),
}
3 changes: 2 additions & 1 deletion render/src/tessellator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::bitmap::BitmapSource;
use crate::shape_utils::{DistilledShape, DrawCommand, DrawPath};
use enum_map::Enum;
use lyon::path::Path;
use lyon::tessellation::{
self,
Expand Down Expand Up @@ -440,7 +441,7 @@ impl StrokeVertexConstructor<Vertex> for RuffleVertexCtor {
}
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Enum)]
pub enum GradientType {
Linear,
Radial,
Expand Down
34 changes: 20 additions & 14 deletions render/webgl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,22 @@ impl WebGlRenderBackend {
);
}
}

fn push_blend_mode(&mut self, blend: BlendMode) {
if self.blend_modes.last() != Some(&blend) {
self.apply_blend_mode(blend);
}
self.blend_modes.push(blend);
}

fn pop_blend_mode(&mut self) {
let old = self.blend_modes.pop();
// We never pop our base 'BlendMode::Normal'
let current = *self.blend_modes.last().unwrap();
if old != Some(current) {
self.apply_blend_mode(current);
}
}
}

impl RenderBackend for WebGlRenderBackend {
Expand Down Expand Up @@ -1401,20 +1417,10 @@ impl<'a> CommandHandler<'a> for WebGlRenderBackend {
self.mask_state_dirty = true;
}

fn push_blend_mode(&mut self, blend: BlendMode) {
if self.blend_modes.last() != Some(&blend) {
self.apply_blend_mode(blend);
}
self.blend_modes.push(blend);
}

fn pop_blend_mode(&mut self) {
let old = self.blend_modes.pop();
// We never pop our base 'BlendMode::Normal'
let current = *self.blend_modes.last().unwrap();
if old != Some(current) {
self.apply_blend_mode(current);
}
fn blend(&mut self, commands: &'a CommandList, blend: BlendMode) {
self.push_blend_mode(blend);
commands.execute(self);
self.pop_blend_mode();
}
}

Expand Down
10 changes: 6 additions & 4 deletions render/wgpu/shaders/bitmap.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ struct VertexOutput {
@location(0) uv: vec2<f32>,
};

@group(2) @binding(0) var<uniform> textureTransforms: TextureTransforms;
@group(2) @binding(1) var texture: texture_2d<f32>;
@group(2) @binding(2) var texture_sampler: sampler;
@group(2) @binding(0) var<uniform> colorTransforms: ColorTransforms;

@group(3) @binding(0) var<uniform> textureTransforms: TextureTransforms;
@group(3) @binding(1) var texture: texture_2d<f32>;
@group(3) @binding(2) var texture_sampler: sampler;

@vertex
fn main_vertex(in: VertexInput) -> VertexOutput {
Expand All @@ -24,7 +26,7 @@ fn main_fragment(in: VertexOutput) -> @location(0) vec4<f32> {
// Unmultiply alpha, apply color transform, remultiply alpha.
if( color.a > 0.0 ) {
color = vec4<f32>(color.rgb / color.a, color.a);
color = color * transforms.mult_color + transforms.add_color;
color = color * colorTransforms.mult_color + colorTransforms.add_color;
let alpha = clamp(color.a, 0.0, 1.0);
color = vec4<f32>(color.rgb * alpha, alpha);
}
Expand Down
Loading