2020-04-21 13:32:50 +00:00
|
|
|
use lyon::tessellation::{
|
|
|
|
self,
|
|
|
|
geometry_builder::{BuffersBuilder, FillVertexConstructor, VertexBuffers},
|
2021-01-04 20:45:16 +00:00
|
|
|
FillTessellator, FillVertex, StrokeTessellator, StrokeVertex, StrokeVertexConstructor,
|
2020-04-21 13:32:50 +00:00
|
|
|
};
|
|
|
|
use ruffle_core::backend::render::swf::{self, FillStyle};
|
|
|
|
use ruffle_core::backend::render::{
|
2021-01-05 21:03:39 +00:00
|
|
|
srgb_to_linear, Bitmap, BitmapFormat, BitmapHandle, BitmapInfo, Color, MovieLibrary,
|
2020-05-20 22:02:42 +00:00
|
|
|
RenderBackend, ShapeHandle, Transform,
|
2020-04-21 13:32:50 +00:00
|
|
|
};
|
2020-04-17 10:14:31 +00:00
|
|
|
use ruffle_core::shape_utils::{DistilledShape, DrawPath};
|
2020-12-15 07:34:48 +00:00
|
|
|
use std::borrow::Cow;
|
2020-05-20 22:02:42 +00:00
|
|
|
use swf::{CharacterId, DefineBitsLossless, Glyph, GradientInterpolation};
|
2020-12-25 21:42:14 +00:00
|
|
|
use target::TextureTarget;
|
2020-04-21 13:32:50 +00:00
|
|
|
|
|
|
|
use bytemuck::{Pod, Zeroable};
|
|
|
|
use futures::executor::block_on;
|
2020-04-27 15:53:42 +00:00
|
|
|
use raw_window_handle::HasRawWindowHandle;
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-04-29 10:25:37 +00:00
|
|
|
use crate::pipelines::Pipelines;
|
|
|
|
use crate::shapes::{Draw, DrawType, GradientUniforms, IncompleteDrawType, Mesh};
|
2020-05-04 20:59:06 +00:00
|
|
|
use crate::target::{RenderTarget, RenderTargetFrame, SwapChainTarget};
|
2020-04-29 10:59:55 +00:00
|
|
|
use crate::utils::{
|
2020-10-16 15:46:40 +00:00
|
|
|
create_buffer_with_data, format_list, get_backend_names, gradient_spread_mode_index,
|
|
|
|
ruffle_path_to_lyon_path, swf_bitmap_to_gl_matrix, swf_to_gl_matrix,
|
2020-04-29 10:59:55 +00:00
|
|
|
};
|
2020-10-14 18:25:55 +00:00
|
|
|
use enum_map::Enum;
|
2020-04-30 14:33:41 +00:00
|
|
|
use ruffle_core::color_transform::ColorTransform;
|
2020-04-29 10:25:37 +00:00
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
type Error = Box<dyn std::error::Error>;
|
|
|
|
|
2020-04-28 14:10:45 +00:00
|
|
|
#[macro_use]
|
2020-04-29 10:25:37 +00:00
|
|
|
mod utils;
|
2020-04-28 14:10:45 +00:00
|
|
|
|
2020-10-15 22:55:19 +00:00
|
|
|
mod bitmaps;
|
2020-10-16 15:46:40 +00:00
|
|
|
mod globals;
|
2020-04-28 14:10:45 +00:00
|
|
|
mod pipelines;
|
2020-04-29 10:25:37 +00:00
|
|
|
mod shapes;
|
2020-05-04 21:33:45 +00:00
|
|
|
pub mod target;
|
2020-04-27 20:42:59 +00:00
|
|
|
|
2020-10-10 20:16:32 +00:00
|
|
|
#[cfg(feature = "clap")]
|
|
|
|
pub mod clap;
|
|
|
|
|
2020-10-15 22:55:19 +00:00
|
|
|
use crate::bitmaps::BitmapSamplers;
|
2020-10-16 15:46:40 +00:00
|
|
|
use crate::globals::Globals;
|
2021-01-05 21:03:39 +00:00
|
|
|
use ruffle_core::swf::Matrix;
|
2020-11-01 21:07:27 +00:00
|
|
|
use std::collections::HashMap;
|
2020-10-14 20:17:04 +00:00
|
|
|
use std::path::Path;
|
2020-08-29 12:55:21 +00:00
|
|
|
pub use wgpu;
|
|
|
|
|
2020-10-15 23:23:36 +00:00
|
|
|
pub struct Descriptors {
|
|
|
|
pub device: wgpu::Device,
|
|
|
|
queue: wgpu::Queue,
|
2020-10-16 15:46:40 +00:00
|
|
|
globals: Globals,
|
2020-10-15 23:23:36 +00:00
|
|
|
pipelines: Pipelines,
|
|
|
|
bitmap_samplers: BitmapSamplers,
|
|
|
|
msaa_sample_count: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Descriptors {
|
|
|
|
pub fn new(device: wgpu::Device, queue: wgpu::Queue) -> Result<Self, Error> {
|
|
|
|
// TODO: Allow this to be set from command line/settings file.
|
|
|
|
let msaa_sample_count = 4;
|
|
|
|
|
|
|
|
let bitmap_samplers = BitmapSamplers::new(&device);
|
2020-10-16 15:46:40 +00:00
|
|
|
let globals = Globals::new(&device);
|
|
|
|
let pipelines = Pipelines::new(
|
|
|
|
&device,
|
|
|
|
msaa_sample_count,
|
|
|
|
bitmap_samplers.layout(),
|
|
|
|
globals.layout(),
|
|
|
|
)?;
|
2020-10-15 23:23:36 +00:00
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
device,
|
|
|
|
queue,
|
2020-10-16 15:46:40 +00:00
|
|
|
globals,
|
2020-10-15 23:23:36 +00:00
|
|
|
pipelines,
|
|
|
|
bitmap_samplers,
|
|
|
|
msaa_sample_count,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:59:06 +00:00
|
|
|
pub struct WgpuRenderBackend<T: RenderTarget> {
|
2020-10-15 23:23:36 +00:00
|
|
|
descriptors: Descriptors,
|
2020-05-04 20:59:06 +00:00
|
|
|
target: T,
|
2020-05-01 02:56:07 +00:00
|
|
|
frame_buffer_view: wgpu::TextureView,
|
2020-04-21 13:32:50 +00:00
|
|
|
depth_texture_view: wgpu::TextureView,
|
2020-12-26 00:43:03 +00:00
|
|
|
current_frame: Option<Frame<'static, T>>,
|
2020-04-21 13:32:50 +00:00
|
|
|
meshes: Vec<Mesh>,
|
2020-10-14 18:25:55 +00:00
|
|
|
mask_state: MaskState,
|
2020-12-30 23:35:43 +00:00
|
|
|
textures: Vec<Texture>,
|
2020-04-28 20:48:17 +00:00
|
|
|
num_masks: u32,
|
2020-04-29 10:06:02 +00:00
|
|
|
quad_vbo: wgpu::Buffer,
|
|
|
|
quad_ibo: wgpu::Buffer,
|
|
|
|
quad_tex_transforms: wgpu::Buffer,
|
2020-11-01 18:43:15 +00:00
|
|
|
bitmap_registry: HashMap<BitmapHandle, Bitmap>,
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
#[allow(dead_code)]
|
|
|
|
struct Frame<'a, T: RenderTarget> {
|
|
|
|
frame_data: Box<(wgpu::CommandEncoder, T::Frame)>,
|
|
|
|
|
|
|
|
// TODO: This is a self-reference to the above, so we
|
|
|
|
// use some unsafe to cast the lifetime away. We know this
|
|
|
|
// is safe because the anpve data should live for the
|
|
|
|
// entire frame and is boxed to have a stable address.
|
|
|
|
// We could clean this up later by adjusting the
|
|
|
|
// RenderBackend interface to return a Frame object.
|
|
|
|
render_pass: wgpu::RenderPass<'a>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, T: RenderTarget> Frame<'static, T> {
|
|
|
|
// Get a reference to the render pass with the proper lifetime.
|
|
|
|
fn get(&mut self) -> &mut Frame<'a, T> {
|
|
|
|
unsafe { std::mem::transmute::<_, &mut Frame<'a, T>>(self) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-14 18:25:55 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Enum)]
|
|
|
|
pub enum MaskState {
|
|
|
|
NoMask,
|
|
|
|
DrawMaskStencil,
|
|
|
|
DrawMaskedContent,
|
|
|
|
ClearMaskStencil,
|
|
|
|
}
|
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
#[repr(C)]
|
2021-02-17 04:34:12 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
|
2020-04-21 13:32:50 +00:00
|
|
|
struct Transforms {
|
|
|
|
world_matrix: [[f32; 4]; 4],
|
|
|
|
}
|
|
|
|
|
|
|
|
#[repr(C)]
|
2021-02-17 04:34:12 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
|
2020-04-21 13:32:50 +00:00
|
|
|
struct TextureTransforms {
|
|
|
|
u_matrix: [[f32; 4]; 4],
|
|
|
|
}
|
|
|
|
|
|
|
|
#[repr(C)]
|
2021-02-17 04:34:12 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
|
2020-04-21 13:32:50 +00:00
|
|
|
struct ColorAdjustments {
|
|
|
|
mult_color: [f32; 4],
|
|
|
|
add_color: [f32; 4],
|
|
|
|
}
|
|
|
|
|
2020-04-30 14:40:08 +00:00
|
|
|
impl From<ColorTransform> for ColorAdjustments {
|
|
|
|
fn from(transform: ColorTransform) -> Self {
|
|
|
|
Self {
|
|
|
|
mult_color: [
|
|
|
|
transform.r_mult,
|
|
|
|
transform.g_mult,
|
|
|
|
transform.b_mult,
|
|
|
|
transform.a_mult,
|
|
|
|
],
|
|
|
|
add_color: [
|
|
|
|
transform.r_add,
|
|
|
|
transform.g_add,
|
|
|
|
transform.b_add,
|
|
|
|
transform.a_add,
|
|
|
|
],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
#[repr(C)]
|
2021-02-17 04:34:12 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
|
2021-02-12 13:03:17 +00:00
|
|
|
struct GpuVertex {
|
2020-04-21 13:32:50 +00:00
|
|
|
position: [f32; 2],
|
|
|
|
color: [f32; 4],
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:59:06 +00:00
|
|
|
impl WgpuRenderBackend<SwapChainTarget> {
|
2020-08-29 12:55:21 +00:00
|
|
|
pub fn for_window<W: HasRawWindowHandle>(
|
|
|
|
window: &W,
|
|
|
|
size: (u32, u32),
|
|
|
|
backend: wgpu::BackendBit,
|
2020-08-29 13:01:44 +00:00
|
|
|
power_preference: wgpu::PowerPreference,
|
2020-10-14 20:17:04 +00:00
|
|
|
trace_path: Option<&Path>,
|
2020-08-29 12:55:21 +00:00
|
|
|
) -> Result<Self, Error> {
|
|
|
|
if wgpu::BackendBit::SECONDARY.contains(backend) {
|
|
|
|
log::warn!(
|
|
|
|
"{} graphics backend support may not be fully supported.",
|
|
|
|
format_list(&get_backend_names(backend), "and")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let instance = wgpu::Instance::new(backend);
|
2020-08-27 10:32:41 +00:00
|
|
|
let surface = unsafe { instance.create_surface(window) };
|
2020-12-25 21:42:14 +00:00
|
|
|
let descriptors = Self::build_descriptors(
|
|
|
|
backend,
|
|
|
|
instance,
|
|
|
|
Some(&surface),
|
2020-08-29 13:01:44 +00:00
|
|
|
power_preference,
|
2020-10-14 20:17:04 +00:00
|
|
|
trace_path,
|
2020-12-25 21:42:14 +00:00
|
|
|
)?;
|
2020-10-15 23:23:36 +00:00
|
|
|
let target = SwapChainTarget::new(surface, size, &descriptors.device);
|
|
|
|
Self::new(descriptors, target)
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-12-25 21:42:14 +00:00
|
|
|
impl WgpuRenderBackend<TextureTarget> {
|
|
|
|
pub fn for_offscreen(
|
|
|
|
size: (u32, u32),
|
|
|
|
backend: wgpu::BackendBit,
|
|
|
|
power_preference: wgpu::PowerPreference,
|
|
|
|
trace_path: Option<&Path>,
|
|
|
|
) -> Result<Self, Error> {
|
|
|
|
if wgpu::BackendBit::SECONDARY.contains(backend) {
|
|
|
|
log::warn!(
|
|
|
|
"{} graphics backend support may not be fully supported.",
|
|
|
|
format_list(&get_backend_names(backend), "and")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let instance = wgpu::Instance::new(backend);
|
|
|
|
let descriptors =
|
|
|
|
Self::build_descriptors(backend, instance, None, power_preference, trace_path)?;
|
|
|
|
let target = TextureTarget::new(&descriptors.device, size);
|
|
|
|
Self::new(descriptors, target)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:59:06 +00:00
|
|
|
impl<T: RenderTarget> WgpuRenderBackend<T> {
|
2020-10-16 15:46:40 +00:00
|
|
|
pub fn new(mut descriptors: Descriptors, target: T) -> Result<Self, Error> {
|
2020-05-01 02:56:07 +00:00
|
|
|
let extent = wgpu::Extent3d {
|
2020-05-04 20:59:06 +00:00
|
|
|
width: target.width(),
|
|
|
|
height: target.height(),
|
2020-05-01 02:56:07 +00:00
|
|
|
depth: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
let frame_buffer_label = create_debug_label!("Framebuffer texture");
|
2020-10-15 23:23:36 +00:00
|
|
|
let frame_buffer = descriptors.device.create_texture(&wgpu::TextureDescriptor {
|
2020-05-01 02:56:07 +00:00
|
|
|
label: frame_buffer_label.as_deref(),
|
|
|
|
size: extent,
|
|
|
|
mip_level_count: 1,
|
2020-10-15 23:23:36 +00:00
|
|
|
sample_count: descriptors.msaa_sample_count,
|
2020-05-01 02:56:07 +00:00
|
|
|
dimension: wgpu::TextureDimension::D2,
|
2020-05-04 20:59:06 +00:00
|
|
|
format: target.format(),
|
2020-12-04 18:47:20 +00:00
|
|
|
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
|
2020-05-01 02:56:07 +00:00
|
|
|
});
|
2020-08-27 10:32:41 +00:00
|
|
|
let frame_buffer_view = frame_buffer.create_view(&Default::default());
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-04-27 20:42:59 +00:00
|
|
|
let depth_label = create_debug_label!("Depth texture");
|
2020-10-15 23:23:36 +00:00
|
|
|
let depth_texture = descriptors.device.create_texture(&wgpu::TextureDescriptor {
|
2020-04-27 20:42:59 +00:00
|
|
|
label: depth_label.as_deref(),
|
2020-05-01 02:56:07 +00:00
|
|
|
size: extent,
|
2020-04-21 13:32:50 +00:00
|
|
|
mip_level_count: 1,
|
2020-10-15 23:23:36 +00:00
|
|
|
sample_count: descriptors.msaa_sample_count,
|
2020-04-21 13:32:50 +00:00
|
|
|
dimension: wgpu::TextureDimension::D2,
|
2020-04-28 20:48:17 +00:00
|
|
|
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
2020-12-04 18:47:20 +00:00
|
|
|
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
|
2020-04-21 13:32:50 +00:00
|
|
|
});
|
|
|
|
|
2020-08-27 10:32:41 +00:00
|
|
|
let depth_texture_view = depth_texture.create_view(&Default::default());
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-10-15 23:23:36 +00:00
|
|
|
let (quad_vbo, quad_ibo, quad_tex_transforms) = create_quad_buffers(&descriptors.device);
|
2020-04-29 10:06:02 +00:00
|
|
|
|
2020-10-16 15:46:40 +00:00
|
|
|
descriptors
|
|
|
|
.globals
|
|
|
|
.set_resolution(target.width(), target.height());
|
2020-05-04 20:59:06 +00:00
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
Ok(Self {
|
2020-10-15 23:23:36 +00:00
|
|
|
descriptors,
|
2020-05-04 20:59:06 +00:00
|
|
|
target,
|
2020-05-01 02:56:07 +00:00
|
|
|
frame_buffer_view,
|
2020-04-21 13:32:50 +00:00
|
|
|
depth_texture_view,
|
|
|
|
current_frame: None,
|
|
|
|
meshes: Vec::new(),
|
|
|
|
textures: Vec::new(),
|
2020-10-14 18:25:55 +00:00
|
|
|
|
2020-04-28 20:48:17 +00:00
|
|
|
num_masks: 0,
|
2020-10-14 18:25:55 +00:00
|
|
|
mask_state: MaskState::NoMask,
|
|
|
|
|
2020-04-29 10:06:02 +00:00
|
|
|
quad_vbo,
|
|
|
|
quad_ibo,
|
|
|
|
quad_tex_transforms,
|
2020-10-19 00:25:23 +00:00
|
|
|
bitmap_registry: HashMap::new(),
|
2020-04-21 13:32:50 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-25 21:42:14 +00:00
|
|
|
pub fn build_descriptors(
|
|
|
|
backend: wgpu::BackendBit,
|
|
|
|
instance: wgpu::Instance,
|
|
|
|
surface: Option<&wgpu::Surface>,
|
|
|
|
power_preference: wgpu::PowerPreference,
|
|
|
|
trace_path: Option<&Path>,
|
|
|
|
) -> Result<Descriptors, Error> {
|
|
|
|
let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
|
|
|
power_preference,
|
|
|
|
compatible_surface: surface,
|
|
|
|
}))
|
|
|
|
.ok_or_else(|| {
|
|
|
|
let names = get_backend_names(backend);
|
|
|
|
if names.is_empty() {
|
|
|
|
"Ruffle requires hardware acceleration, but no compatible graphics device was found (no backend provided?)".to_string()
|
|
|
|
} else {
|
|
|
|
format!("Ruffle requires hardware acceleration, but no compatible graphics device was found supporting {}", format_list(&names, "or"))
|
|
|
|
}
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let (device, queue) = block_on(adapter.request_device(
|
|
|
|
&wgpu::DeviceDescriptor {
|
|
|
|
label: None,
|
|
|
|
features: wgpu::Features::PUSH_CONSTANTS,
|
|
|
|
limits: wgpu::Limits {
|
|
|
|
max_push_constant_size: (std::mem::size_of::<Transforms>()
|
|
|
|
+ std::mem::size_of::<ColorAdjustments>())
|
|
|
|
as u32,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
trace_path,
|
|
|
|
))?;
|
|
|
|
Descriptors::new(device, queue)
|
|
|
|
}
|
|
|
|
|
2020-10-15 23:23:36 +00:00
|
|
|
pub fn descriptors(self) -> Descriptors {
|
|
|
|
self.descriptors
|
|
|
|
}
|
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
#[allow(clippy::cognitive_complexity)]
|
2020-12-30 23:35:43 +00:00
|
|
|
fn register_shape_internal(
|
|
|
|
&mut self,
|
|
|
|
shape: DistilledShape,
|
|
|
|
library: Option<&MovieLibrary<'_>>,
|
|
|
|
) -> Mesh {
|
2020-04-21 13:32:50 +00:00
|
|
|
use lyon::tessellation::{FillOptions, StrokeOptions};
|
|
|
|
|
|
|
|
let mut draws = Vec::new();
|
|
|
|
|
|
|
|
let mut fill_tess = FillTessellator::new();
|
|
|
|
let mut stroke_tess = StrokeTessellator::new();
|
2021-01-04 23:45:40 +00:00
|
|
|
let mut lyon_mesh: VertexBuffers<_, u32> = VertexBuffers::new();
|
2020-04-21 13:32:50 +00:00
|
|
|
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
fn flush_draw(
|
2020-04-27 20:42:59 +00:00
|
|
|
shape_id: CharacterId,
|
2020-04-21 13:32:50 +00:00
|
|
|
draw: IncompleteDrawType,
|
|
|
|
draws: &mut Vec<Draw>,
|
2021-02-12 13:03:17 +00:00
|
|
|
lyon_mesh: &mut VertexBuffers<GpuVertex, u32>,
|
2020-04-21 13:32:50 +00:00
|
|
|
device: &wgpu::Device,
|
2020-04-28 14:34:07 +00:00
|
|
|
pipelines: &Pipelines,
|
2020-04-21 13:32:50 +00:00
|
|
|
) {
|
2020-05-01 03:02:11 +00:00
|
|
|
if lyon_mesh.vertices.is_empty() || lyon_mesh.indices.len() < 3 {
|
2020-04-21 13:32:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-03-19 11:22:06 +00:00
|
|
|
let vertex_buffer = create_buffer_with_data(
|
2020-04-27 20:42:59 +00:00
|
|
|
device,
|
2020-04-21 13:32:50 +00:00
|
|
|
bytemuck::cast_slice(&lyon_mesh.vertices),
|
|
|
|
wgpu::BufferUsage::VERTEX,
|
2020-04-27 20:42:59 +00:00
|
|
|
create_debug_label!("Shape {} ({}) vbo", shape_id, draw.name()),
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
|
2021-03-19 11:22:06 +00:00
|
|
|
let index_buffer = create_buffer_with_data(
|
2020-04-27 20:42:59 +00:00
|
|
|
device,
|
2020-04-21 13:32:50 +00:00
|
|
|
bytemuck::cast_slice(&lyon_mesh.indices),
|
|
|
|
wgpu::BufferUsage::INDEX,
|
2020-04-27 20:42:59 +00:00
|
|
|
create_debug_label!("Shape {} ({}) ibo", shape_id, draw.name()),
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
|
2021-03-19 11:22:06 +00:00
|
|
|
let index_count = lyon_mesh.indices.len() as u32;
|
2020-04-27 20:42:59 +00:00
|
|
|
let draw_id = draws.len();
|
|
|
|
|
2021-03-19 11:22:06 +00:00
|
|
|
draws.push(match draw {
|
|
|
|
IncompleteDrawType::Color => Draw {
|
|
|
|
draw_type: DrawType::Color,
|
|
|
|
vertex_buffer,
|
|
|
|
index_buffer,
|
|
|
|
index_count,
|
|
|
|
},
|
|
|
|
IncompleteDrawType::Gradient {
|
|
|
|
texture_transform,
|
|
|
|
gradient,
|
|
|
|
} => {
|
|
|
|
let tex_transforms_ubo = create_buffer_with_data(
|
|
|
|
device,
|
|
|
|
bytemuck::cast_slice(&[texture_transform]),
|
|
|
|
wgpu::BufferUsage::UNIFORM,
|
|
|
|
create_debug_label!(
|
|
|
|
"Shape {} draw {} textransforms ubo transfer buffer",
|
|
|
|
shape_id,
|
|
|
|
draw_id
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
let gradient_ubo = create_buffer_with_data(
|
|
|
|
device,
|
|
|
|
bytemuck::cast_slice(&[gradient]),
|
|
|
|
wgpu::BufferUsage::STORAGE,
|
|
|
|
create_debug_label!(
|
|
|
|
"Shape {} draw {} gradient ubo transfer buffer",
|
|
|
|
shape_id,
|
|
|
|
draw_id
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
let bind_group_label =
|
|
|
|
create_debug_label!("Shape {} (gradient) draw {} bindgroup", shape_id, draw_id);
|
|
|
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
|
|
layout: &pipelines.gradient_layout,
|
|
|
|
entries: &[
|
|
|
|
wgpu::BindGroupEntry {
|
|
|
|
binding: 0,
|
|
|
|
resource: wgpu::BindingResource::Buffer {
|
|
|
|
buffer: &tex_transforms_ubo,
|
|
|
|
offset: 0,
|
|
|
|
size: wgpu::BufferSize::new(
|
|
|
|
std::mem::size_of::<TextureTransforms>() as u64
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wgpu::BindGroupEntry {
|
|
|
|
binding: 1,
|
|
|
|
resource: wgpu::BindingResource::Buffer {
|
|
|
|
buffer: &gradient_ubo,
|
|
|
|
offset: 0,
|
|
|
|
size: wgpu::BufferSize::new(
|
|
|
|
std::mem::size_of::<GradientUniforms>() as u64
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
label: bind_group_label.as_deref(),
|
|
|
|
});
|
|
|
|
|
|
|
|
Draw {
|
|
|
|
draw_type: DrawType::Gradient {
|
|
|
|
texture_transforms: tex_transforms_ubo,
|
|
|
|
gradient: gradient_ubo,
|
|
|
|
bind_group,
|
|
|
|
},
|
|
|
|
vertex_buffer,
|
|
|
|
index_buffer,
|
|
|
|
index_count,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
IncompleteDrawType::Bitmap {
|
|
|
|
texture_transform,
|
|
|
|
is_smoothed,
|
|
|
|
is_repeating,
|
|
|
|
texture_view,
|
|
|
|
} => {
|
|
|
|
let tex_transforms_ubo = create_buffer_with_data(
|
|
|
|
device,
|
|
|
|
bytemuck::cast_slice(&[texture_transform]),
|
|
|
|
wgpu::BufferUsage::UNIFORM,
|
|
|
|
create_debug_label!(
|
|
|
|
"Shape {} draw {} textransforms ubo transfer buffer",
|
|
|
|
shape_id,
|
|
|
|
draw_id
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
let bind_group_label =
|
|
|
|
create_debug_label!("Shape {} (bitmap) draw {} bindgroup", shape_id, draw_id);
|
|
|
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
|
|
layout: &pipelines.bitmap_layout,
|
|
|
|
entries: &[
|
|
|
|
wgpu::BindGroupEntry {
|
|
|
|
binding: 0,
|
|
|
|
resource: wgpu::BindingResource::Buffer {
|
|
|
|
buffer: &tex_transforms_ubo,
|
|
|
|
offset: 0,
|
|
|
|
size: wgpu::BufferSize::new(
|
|
|
|
std::mem::size_of::<TextureTransforms>() as u64
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wgpu::BindGroupEntry {
|
|
|
|
binding: 1,
|
|
|
|
resource: wgpu::BindingResource::TextureView(&texture_view),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
label: bind_group_label.as_deref(),
|
|
|
|
});
|
|
|
|
|
|
|
|
Draw {
|
|
|
|
draw_type: DrawType::Bitmap {
|
|
|
|
texture_transforms: tex_transforms_ubo,
|
|
|
|
texture_view,
|
|
|
|
is_smoothed,
|
|
|
|
is_repeating,
|
|
|
|
bind_group,
|
|
|
|
},
|
|
|
|
vertex_buffer,
|
|
|
|
index_buffer,
|
|
|
|
index_count,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2020-04-21 13:32:50 +00:00
|
|
|
|
|
|
|
*lyon_mesh = VertexBuffers::new();
|
|
|
|
}
|
|
|
|
|
2020-04-17 10:14:31 +00:00
|
|
|
for path in shape.paths {
|
2020-04-21 13:32:50 +00:00
|
|
|
match path {
|
|
|
|
DrawPath::Fill { style, commands } => match style {
|
|
|
|
FillStyle::Color(color) => {
|
|
|
|
let 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,
|
|
|
|
];
|
|
|
|
|
|
|
|
let mut buffers_builder =
|
|
|
|
BuffersBuilder::new(&mut lyon_mesh, RuffleVertexCtor { color });
|
|
|
|
|
|
|
|
if let Err(e) = fill_tess.tessellate_path(
|
|
|
|
&ruffle_path_to_lyon_path(commands, true),
|
|
|
|
&FillOptions::even_odd(),
|
|
|
|
&mut buffers_builder,
|
|
|
|
) {
|
|
|
|
// This may just be a degenerate path; skip it.
|
|
|
|
log::error!("Tessellation failure: {:?}", e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FillStyle::LinearGradient(gradient) => {
|
|
|
|
flush_draw(
|
2020-04-27 20:42:59 +00:00
|
|
|
shape.id,
|
2020-04-21 13:32:50 +00:00
|
|
|
IncompleteDrawType::Color,
|
|
|
|
&mut draws,
|
|
|
|
&mut lyon_mesh,
|
2020-10-15 23:23:36 +00:00
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.pipelines,
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut buffers_builder = BuffersBuilder::new(
|
|
|
|
&mut lyon_mesh,
|
|
|
|
RuffleVertexCtor {
|
|
|
|
color: [1.0, 1.0, 1.0, 1.0],
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Err(e) = fill_tess.tessellate_path(
|
|
|
|
&ruffle_path_to_lyon_path(commands, true),
|
|
|
|
&FillOptions::even_odd(),
|
|
|
|
&mut buffers_builder,
|
|
|
|
) {
|
|
|
|
// This may just be a degenerate path; skip it.
|
|
|
|
log::error!("Tessellation failure: {:?}", e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-20 22:02:42 +00:00
|
|
|
let uniforms = swf_gradient_to_uniforms(0, gradient, 0.0);
|
2020-05-19 16:50:37 +00:00
|
|
|
let matrix = swf_to_gl_matrix(gradient.matrix);
|
2020-04-21 13:32:50 +00:00
|
|
|
|
|
|
|
flush_draw(
|
2020-04-27 20:42:59 +00:00
|
|
|
shape.id,
|
2020-04-21 13:32:50 +00:00
|
|
|
IncompleteDrawType::Gradient {
|
|
|
|
texture_transform: matrix,
|
|
|
|
gradient: uniforms,
|
|
|
|
},
|
|
|
|
&mut draws,
|
|
|
|
&mut lyon_mesh,
|
2020-10-15 23:23:36 +00:00
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.pipelines,
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
FillStyle::RadialGradient(gradient) => {
|
|
|
|
flush_draw(
|
2020-04-27 20:42:59 +00:00
|
|
|
shape.id,
|
2020-04-21 13:32:50 +00:00
|
|
|
IncompleteDrawType::Color,
|
|
|
|
&mut draws,
|
|
|
|
&mut lyon_mesh,
|
2020-10-15 23:23:36 +00:00
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.pipelines,
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut buffers_builder = BuffersBuilder::new(
|
|
|
|
&mut lyon_mesh,
|
|
|
|
RuffleVertexCtor {
|
|
|
|
color: [1.0, 1.0, 1.0, 1.0],
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Err(e) = fill_tess.tessellate_path(
|
|
|
|
&ruffle_path_to_lyon_path(commands, true),
|
|
|
|
&FillOptions::even_odd(),
|
|
|
|
&mut buffers_builder,
|
|
|
|
) {
|
|
|
|
// This may just be a degenerate path; skip it.
|
|
|
|
log::error!("Tessellation failure: {:?}", e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-20 22:02:42 +00:00
|
|
|
let uniforms = swf_gradient_to_uniforms(1, gradient, 0.0);
|
2020-05-19 16:50:37 +00:00
|
|
|
let matrix = swf_to_gl_matrix(gradient.matrix);
|
2020-04-21 13:32:50 +00:00
|
|
|
|
|
|
|
flush_draw(
|
2020-04-27 20:42:59 +00:00
|
|
|
shape.id,
|
2020-04-21 13:32:50 +00:00
|
|
|
IncompleteDrawType::Gradient {
|
|
|
|
texture_transform: matrix,
|
|
|
|
gradient: uniforms,
|
|
|
|
},
|
|
|
|
&mut draws,
|
|
|
|
&mut lyon_mesh,
|
2020-10-15 23:23:36 +00:00
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.pipelines,
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
FillStyle::FocalGradient {
|
|
|
|
gradient,
|
|
|
|
focal_point,
|
|
|
|
} => {
|
|
|
|
flush_draw(
|
2020-04-27 20:42:59 +00:00
|
|
|
shape.id,
|
2020-04-21 13:32:50 +00:00
|
|
|
IncompleteDrawType::Color,
|
|
|
|
&mut draws,
|
|
|
|
&mut lyon_mesh,
|
2020-10-15 23:23:36 +00:00
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.pipelines,
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut buffers_builder = BuffersBuilder::new(
|
|
|
|
&mut lyon_mesh,
|
|
|
|
RuffleVertexCtor {
|
|
|
|
color: [1.0, 1.0, 1.0, 1.0],
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Err(e) = fill_tess.tessellate_path(
|
|
|
|
&ruffle_path_to_lyon_path(commands, true),
|
|
|
|
&FillOptions::even_odd(),
|
|
|
|
&mut buffers_builder,
|
|
|
|
) {
|
|
|
|
// This may just be a degenerate path; skip it.
|
|
|
|
log::error!("Tessellation failure: {:?}", e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-20 22:02:42 +00:00
|
|
|
let uniforms = swf_gradient_to_uniforms(2, gradient, *focal_point);
|
2020-05-19 16:50:37 +00:00
|
|
|
let matrix = swf_to_gl_matrix(gradient.matrix);
|
2020-04-21 13:32:50 +00:00
|
|
|
|
|
|
|
flush_draw(
|
2020-04-27 20:42:59 +00:00
|
|
|
shape.id,
|
2020-04-21 13:32:50 +00:00
|
|
|
IncompleteDrawType::Gradient {
|
|
|
|
texture_transform: matrix,
|
|
|
|
gradient: uniforms,
|
|
|
|
},
|
|
|
|
&mut draws,
|
|
|
|
&mut lyon_mesh,
|
2020-10-15 23:23:36 +00:00
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.pipelines,
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
FillStyle::Bitmap {
|
|
|
|
id,
|
|
|
|
matrix,
|
|
|
|
is_smoothed,
|
|
|
|
is_repeating,
|
|
|
|
} => {
|
|
|
|
flush_draw(
|
2020-04-27 20:42:59 +00:00
|
|
|
shape.id,
|
2020-04-21 13:32:50 +00:00
|
|
|
IncompleteDrawType::Color,
|
|
|
|
&mut draws,
|
|
|
|
&mut lyon_mesh,
|
2020-10-15 23:23:36 +00:00
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.pipelines,
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut buffers_builder = BuffersBuilder::new(
|
|
|
|
&mut lyon_mesh,
|
|
|
|
RuffleVertexCtor {
|
|
|
|
color: [1.0, 1.0, 1.0, 1.0],
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
if let Err(e) = fill_tess.tessellate_path(
|
|
|
|
&ruffle_path_to_lyon_path(commands, true),
|
|
|
|
&FillOptions::even_odd(),
|
|
|
|
&mut buffers_builder,
|
|
|
|
) {
|
|
|
|
// This may just be a degenerate path; skip it.
|
|
|
|
log::error!("Tessellation failure: {:?}", e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-12-30 23:35:43 +00:00
|
|
|
if let Some(texture) = library
|
|
|
|
.and_then(|lib| lib.get_bitmap(*id))
|
|
|
|
.and_then(|bitmap| self.textures.get(bitmap.bitmap_handle().0))
|
2020-05-05 22:28:28 +00:00
|
|
|
{
|
2020-12-30 23:35:43 +00:00
|
|
|
let texture_view = texture.texture.create_view(&Default::default());
|
|
|
|
|
|
|
|
flush_draw(
|
|
|
|
shape.id,
|
|
|
|
IncompleteDrawType::Bitmap {
|
|
|
|
texture_transform: swf_bitmap_to_gl_matrix(
|
|
|
|
*matrix,
|
|
|
|
texture.width,
|
|
|
|
texture.height,
|
|
|
|
),
|
|
|
|
is_smoothed: *is_smoothed,
|
|
|
|
is_repeating: *is_repeating,
|
|
|
|
texture_view,
|
|
|
|
},
|
|
|
|
&mut draws,
|
|
|
|
&mut lyon_mesh,
|
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.pipelines,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
log::error!("Couldn't fill shape with unknown bitmap {}", id);
|
|
|
|
}
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
DrawPath::Stroke {
|
|
|
|
style,
|
|
|
|
commands,
|
|
|
|
is_closed,
|
|
|
|
} => {
|
|
|
|
let color = [
|
|
|
|
f32::from(style.color.r) / 255.0,
|
|
|
|
f32::from(style.color.g) / 255.0,
|
|
|
|
f32::from(style.color.b) / 255.0,
|
|
|
|
f32::from(style.color.a) / 255.0,
|
|
|
|
];
|
|
|
|
|
|
|
|
let mut buffers_builder =
|
|
|
|
BuffersBuilder::new(&mut lyon_mesh, RuffleVertexCtor { color });
|
|
|
|
|
|
|
|
// TODO(Herschel): 0 width indicates "hairline".
|
|
|
|
let width = if style.width.to_pixels() >= 1.0 {
|
|
|
|
style.width.to_pixels() as f32
|
|
|
|
} else {
|
|
|
|
1.0
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut options = StrokeOptions::default()
|
|
|
|
.with_line_width(width)
|
|
|
|
.with_start_cap(match style.start_cap {
|
|
|
|
swf::LineCapStyle::None => tessellation::LineCap::Butt,
|
|
|
|
swf::LineCapStyle::Round => tessellation::LineCap::Round,
|
|
|
|
swf::LineCapStyle::Square => tessellation::LineCap::Square,
|
|
|
|
})
|
|
|
|
.with_end_cap(match style.end_cap {
|
|
|
|
swf::LineCapStyle::None => tessellation::LineCap::Butt,
|
|
|
|
swf::LineCapStyle::Round => tessellation::LineCap::Round,
|
|
|
|
swf::LineCapStyle::Square => tessellation::LineCap::Square,
|
|
|
|
});
|
|
|
|
|
2020-11-20 08:01:43 +00:00
|
|
|
let line_join = match style.join_style {
|
|
|
|
swf::LineJoinStyle::Round => tessellation::LineJoin::Round,
|
|
|
|
swf::LineJoinStyle::Bevel => tessellation::LineJoin::Bevel,
|
|
|
|
swf::LineJoinStyle::Miter(limit) => {
|
|
|
|
// Avoid lyon assert with small miter limits.
|
|
|
|
if limit >= StrokeOptions::MINIMUM_MITER_LIMIT {
|
|
|
|
options = options.with_miter_limit(limit);
|
|
|
|
tessellation::LineJoin::MiterClip
|
|
|
|
} else {
|
|
|
|
tessellation::LineJoin::Bevel
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
options = options.with_line_join(line_join);
|
2020-04-21 13:32:50 +00:00
|
|
|
|
|
|
|
if let Err(e) = stroke_tess.tessellate_path(
|
|
|
|
&ruffle_path_to_lyon_path(commands, is_closed),
|
|
|
|
&options,
|
|
|
|
&mut buffers_builder,
|
|
|
|
) {
|
|
|
|
// This may just be a degenerate path; skip it.
|
|
|
|
log::error!("Tessellation failure: {:?}", e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
flush_draw(
|
2020-04-27 20:42:59 +00:00
|
|
|
shape.id,
|
2020-04-21 13:32:50 +00:00
|
|
|
IncompleteDrawType::Color,
|
|
|
|
&mut draws,
|
|
|
|
&mut lyon_mesh,
|
2020-10-15 23:23:36 +00:00
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.pipelines,
|
2020-04-21 13:32:50 +00:00
|
|
|
);
|
|
|
|
|
2020-05-19 11:03:23 +00:00
|
|
|
Mesh {
|
2020-04-21 13:32:50 +00:00
|
|
|
draws,
|
2020-04-27 20:42:59 +00:00
|
|
|
shape_id: shape.id,
|
2020-05-19 11:03:23 +00:00
|
|
|
}
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2020-12-30 23:35:43 +00:00
|
|
|
fn register_bitmap(&mut self, bitmap: Bitmap, debug_str: &str) -> BitmapInfo {
|
2020-04-29 10:58:29 +00:00
|
|
|
let extent = wgpu::Extent3d {
|
|
|
|
width: bitmap.width,
|
|
|
|
height: bitmap.height,
|
|
|
|
depth: 1,
|
|
|
|
};
|
|
|
|
|
2020-12-15 07:34:48 +00:00
|
|
|
let data: Cow<[u8]> = match &bitmap.data {
|
|
|
|
BitmapFormat::Rgba(data) => Cow::Borrowed(data),
|
2020-04-29 10:58:29 +00:00
|
|
|
BitmapFormat::Rgb(data) => {
|
|
|
|
// Expand to RGBA.
|
|
|
|
let mut as_rgba =
|
|
|
|
Vec::with_capacity(extent.width as usize * extent.height as usize * 4);
|
|
|
|
for i in (0..data.len()).step_by(3) {
|
|
|
|
as_rgba.push(data[i]);
|
|
|
|
as_rgba.push(data[i + 1]);
|
|
|
|
as_rgba.push(data[i + 2]);
|
|
|
|
as_rgba.push(255);
|
|
|
|
}
|
2020-12-15 07:34:48 +00:00
|
|
|
Cow::Owned(as_rgba)
|
2020-04-29 10:58:29 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-12-30 23:35:43 +00:00
|
|
|
let texture_label = create_debug_label!("{} Texture", debug_str);
|
2020-10-15 23:23:36 +00:00
|
|
|
let texture = self
|
|
|
|
.descriptors
|
|
|
|
.device
|
|
|
|
.create_texture(&wgpu::TextureDescriptor {
|
|
|
|
label: texture_label.as_deref(),
|
|
|
|
size: extent,
|
|
|
|
mip_level_count: 1,
|
|
|
|
sample_count: 1,
|
|
|
|
dimension: wgpu::TextureDimension::D2,
|
|
|
|
format: wgpu::TextureFormat::Rgba8Unorm,
|
|
|
|
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
|
|
|
|
});
|
2020-04-29 10:58:29 +00:00
|
|
|
|
2020-10-15 23:23:36 +00:00
|
|
|
self.descriptors.queue.write_texture(
|
2020-08-27 10:32:41 +00:00
|
|
|
wgpu::TextureCopyView {
|
|
|
|
texture: &texture,
|
|
|
|
mip_level: 0,
|
|
|
|
origin: Default::default(),
|
|
|
|
},
|
2020-04-29 10:58:29 +00:00
|
|
|
&data,
|
2020-08-27 10:32:41 +00:00
|
|
|
wgpu::TextureDataLayout {
|
2020-04-29 10:58:29 +00:00
|
|
|
offset: 0,
|
|
|
|
bytes_per_row: 4 * extent.width,
|
|
|
|
rows_per_image: 0,
|
|
|
|
},
|
|
|
|
extent,
|
|
|
|
);
|
|
|
|
|
|
|
|
let handle = BitmapHandle(self.textures.len());
|
2020-12-15 07:34:48 +00:00
|
|
|
let width = bitmap.width;
|
|
|
|
let height = bitmap.height;
|
|
|
|
|
2020-12-26 03:19:42 +00:00
|
|
|
// Make bind group for bitmap quad.
|
|
|
|
let texture_view = texture.create_view(&Default::default());
|
|
|
|
let bind_group = self
|
|
|
|
.descriptors
|
|
|
|
.device
|
|
|
|
.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
|
|
layout: &self.descriptors.pipelines.bitmap_layout,
|
|
|
|
entries: &[
|
|
|
|
wgpu::BindGroupEntry {
|
|
|
|
binding: 0,
|
|
|
|
resource: wgpu::BindingResource::Buffer {
|
|
|
|
buffer: &self.quad_tex_transforms,
|
|
|
|
offset: 0,
|
|
|
|
size: wgpu::BufferSize::new(
|
|
|
|
std::mem::size_of::<TextureTransforms>() as u64
|
|
|
|
),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wgpu::BindGroupEntry {
|
|
|
|
binding: 1,
|
|
|
|
resource: wgpu::BindingResource::TextureView(&texture_view),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
label: create_debug_label!("Bitmap {} bind group", handle.0).as_deref(),
|
|
|
|
});
|
|
|
|
|
2020-12-15 07:34:48 +00:00
|
|
|
self.bitmap_registry.insert(handle, bitmap);
|
2020-12-30 23:35:43 +00:00
|
|
|
self.textures.push(Texture {
|
|
|
|
width,
|
|
|
|
height,
|
2021-02-27 22:50:44 +00:00
|
|
|
texture,
|
2020-12-30 23:35:43 +00:00
|
|
|
bind_group,
|
|
|
|
});
|
2020-04-29 10:58:29 +00:00
|
|
|
|
2020-11-25 02:14:29 +00:00
|
|
|
BitmapInfo {
|
2020-04-29 10:58:29 +00:00
|
|
|
handle,
|
2020-12-15 07:34:48 +00:00
|
|
|
width: width as u16,
|
|
|
|
height: height as u16,
|
2020-11-25 02:14:29 +00:00
|
|
|
}
|
2020-04-29 10:58:29 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 13:27:10 +00:00
|
|
|
pub fn target(&self) -> &T {
|
|
|
|
&self.target
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn device(&self) -> &wgpu::Device {
|
2020-10-15 23:23:36 +00:00
|
|
|
&self.descriptors.device
|
2020-05-05 13:27:10 +00:00
|
|
|
}
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 13:27:10 +00:00
|
|
|
impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
2020-04-21 13:32:50 +00:00
|
|
|
fn set_viewport_dimensions(&mut self, width: u32, height: u32) {
|
2020-05-02 15:44:15 +00:00
|
|
|
// Avoid panics from creating 0-sized framebuffers.
|
|
|
|
let width = std::cmp::max(width, 1);
|
|
|
|
let height = std::cmp::max(height, 1);
|
|
|
|
|
2020-10-15 23:23:36 +00:00
|
|
|
self.target.resize(&self.descriptors.device, width, height);
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-05-01 02:56:07 +00:00
|
|
|
let label = create_debug_label!("Framebuffer texture");
|
2020-10-15 23:23:36 +00:00
|
|
|
let frame_buffer = self
|
|
|
|
.descriptors
|
|
|
|
.device
|
|
|
|
.create_texture(&wgpu::TextureDescriptor {
|
|
|
|
label: label.as_deref(),
|
|
|
|
size: wgpu::Extent3d {
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
depth: 1,
|
|
|
|
},
|
|
|
|
mip_level_count: 1,
|
|
|
|
sample_count: self.descriptors.msaa_sample_count,
|
|
|
|
dimension: wgpu::TextureDimension::D2,
|
|
|
|
format: self.target.format(),
|
2020-12-04 18:47:20 +00:00
|
|
|
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
|
2020-10-15 23:23:36 +00:00
|
|
|
});
|
2020-08-27 10:32:41 +00:00
|
|
|
self.frame_buffer_view = frame_buffer.create_view(&Default::default());
|
2020-05-01 02:56:07 +00:00
|
|
|
|
2020-04-27 20:42:59 +00:00
|
|
|
let label = create_debug_label!("Depth texture");
|
2020-10-15 23:23:36 +00:00
|
|
|
let depth_texture = self
|
|
|
|
.descriptors
|
|
|
|
.device
|
|
|
|
.create_texture(&wgpu::TextureDescriptor {
|
|
|
|
label: label.as_deref(),
|
|
|
|
size: wgpu::Extent3d {
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
depth: 1,
|
|
|
|
},
|
|
|
|
mip_level_count: 1,
|
|
|
|
sample_count: self.descriptors.msaa_sample_count,
|
|
|
|
dimension: wgpu::TextureDimension::D2,
|
|
|
|
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
2020-12-04 18:47:20 +00:00
|
|
|
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
|
2020-10-15 23:23:36 +00:00
|
|
|
});
|
2020-08-27 10:32:41 +00:00
|
|
|
self.depth_texture_view = depth_texture.create_view(&Default::default());
|
2020-10-16 15:46:40 +00:00
|
|
|
self.descriptors.globals.set_resolution(width, height);
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2020-12-30 23:35:43 +00:00
|
|
|
fn register_shape(
|
|
|
|
&mut self,
|
|
|
|
shape: DistilledShape,
|
|
|
|
library: Option<&MovieLibrary<'_>>,
|
|
|
|
) -> ShapeHandle {
|
2020-05-19 11:03:23 +00:00
|
|
|
let handle = ShapeHandle(self.meshes.len());
|
2020-12-30 23:35:43 +00:00
|
|
|
let mesh = self.register_shape_internal(shape, library);
|
2020-05-19 11:03:23 +00:00
|
|
|
self.meshes.push(mesh);
|
|
|
|
handle
|
|
|
|
}
|
|
|
|
|
2020-12-30 23:35:43 +00:00
|
|
|
fn replace_shape(
|
|
|
|
&mut self,
|
|
|
|
shape: DistilledShape,
|
|
|
|
library: Option<&MovieLibrary<'_>>,
|
|
|
|
handle: ShapeHandle,
|
|
|
|
) {
|
|
|
|
let mesh = self.register_shape_internal(shape, library);
|
2020-05-19 11:03:23 +00:00
|
|
|
self.meshes[handle.0] = mesh;
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn register_glyph_shape(&mut self, glyph: &Glyph) -> ShapeHandle {
|
2020-09-15 02:38:08 +00:00
|
|
|
let shape = ruffle_core::shape_utils::swf_glyph_to_shape(glyph);
|
2020-05-19 11:03:23 +00:00
|
|
|
let handle = ShapeHandle(self.meshes.len());
|
2020-12-30 23:35:43 +00:00
|
|
|
let mesh = self.register_shape_internal((&shape).into(), None);
|
2020-05-19 11:03:23 +00:00
|
|
|
self.meshes.push(mesh);
|
|
|
|
handle
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn register_bitmap_jpeg(
|
|
|
|
&mut self,
|
|
|
|
data: &[u8],
|
|
|
|
jpeg_tables: Option<&[u8]>,
|
2020-05-19 10:15:33 +00:00
|
|
|
) -> Result<BitmapInfo, Error> {
|
2020-04-21 13:32:50 +00:00
|
|
|
let data = ruffle_core::backend::render::glue_tables_to_jpeg(data, jpeg_tables);
|
2020-12-30 23:35:43 +00:00
|
|
|
self.register_bitmap_jpeg_2(&data[..])
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2020-12-30 23:35:43 +00:00
|
|
|
fn register_bitmap_jpeg_2(&mut self, data: &[u8]) -> Result<BitmapInfo, Error> {
|
2020-05-19 10:15:33 +00:00
|
|
|
let bitmap = ruffle_core::backend::render::decode_define_bits_jpeg(data, None)?;
|
2020-12-30 23:35:43 +00:00
|
|
|
Ok(self.register_bitmap(bitmap, "JPEG2"))
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn register_bitmap_jpeg_3(
|
|
|
|
&mut self,
|
|
|
|
jpeg_data: &[u8],
|
|
|
|
alpha_data: &[u8],
|
2020-05-19 10:15:33 +00:00
|
|
|
) -> Result<BitmapInfo, Error> {
|
2020-04-29 10:58:29 +00:00
|
|
|
let bitmap =
|
2020-05-19 10:15:33 +00:00
|
|
|
ruffle_core::backend::render::decode_define_bits_jpeg(jpeg_data, Some(alpha_data))?;
|
2020-12-30 23:35:43 +00:00
|
|
|
Ok(self.register_bitmap(bitmap, "JPEG3"))
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2020-05-19 10:15:33 +00:00
|
|
|
fn register_bitmap_png(&mut self, swf_tag: &DefineBitsLossless) -> Result<BitmapInfo, Error> {
|
|
|
|
let bitmap = ruffle_core::backend::render::decode_define_bits_lossless(swf_tag)?;
|
2020-12-30 23:35:43 +00:00
|
|
|
Ok(self.register_bitmap(bitmap, "PNG"))
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2020-05-01 12:13:48 +00:00
|
|
|
fn begin_frame(&mut self, clear: Color) {
|
2020-12-26 00:43:03 +00:00
|
|
|
self.mask_state = MaskState::NoMask;
|
|
|
|
self.num_masks = 0;
|
|
|
|
|
|
|
|
let frame_output = match self.target.get_next_texture() {
|
|
|
|
Ok(frame) => frame,
|
2020-08-27 10:32:41 +00:00
|
|
|
Err(e) => {
|
|
|
|
log::warn!("Couldn't begin new render frame: {}", e);
|
2020-12-28 23:17:37 +00:00
|
|
|
// Attemp to recreate the swap chain in this case.
|
|
|
|
self.target.resize(
|
|
|
|
&self.descriptors.device,
|
|
|
|
self.target.width(),
|
|
|
|
self.target.height(),
|
|
|
|
);
|
|
|
|
return;
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
};
|
2020-10-14 18:25:55 +00:00
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
let label = create_debug_label!("Draw encoder");
|
|
|
|
let draw_encoder =
|
|
|
|
self.descriptors
|
|
|
|
.device
|
|
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
|
|
label: label.as_deref(),
|
|
|
|
});
|
|
|
|
let mut frame_data = Box::new((draw_encoder, frame_output));
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
self.descriptors
|
|
|
|
.globals
|
|
|
|
.update_uniform(&self.descriptors.device, &mut frame_data.0);
|
|
|
|
|
|
|
|
let (color_attachment, resolve_target) = if self.descriptors.msaa_sample_count >= 2 {
|
|
|
|
(&self.frame_buffer_view, Some(frame_data.1.view()))
|
|
|
|
} else {
|
|
|
|
(frame_data.1.view(), None)
|
|
|
|
};
|
|
|
|
|
|
|
|
let render_pass = frame_data.0.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
|
|
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
|
|
|
attachment: color_attachment,
|
|
|
|
ops: wgpu::Operations {
|
|
|
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
|
|
|
r: f64::from(clear.r) / 255.0,
|
|
|
|
g: f64::from(clear.g) / 255.0,
|
|
|
|
b: f64::from(clear.b) / 255.0,
|
|
|
|
a: f64::from(clear.a) / 255.0,
|
2020-08-27 10:32:41 +00:00
|
|
|
}),
|
2020-12-26 00:43:03 +00:00
|
|
|
store: true,
|
|
|
|
},
|
|
|
|
resolve_target,
|
|
|
|
}],
|
|
|
|
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
|
|
|
|
attachment: &self.depth_texture_view,
|
|
|
|
depth_ops: Some(wgpu::Operations {
|
|
|
|
load: wgpu::LoadOp::Clear(0.0),
|
|
|
|
store: true,
|
2020-04-21 13:32:50 +00:00
|
|
|
}),
|
2020-12-26 00:43:03 +00:00
|
|
|
stencil_ops: Some(wgpu::Operations {
|
|
|
|
load: wgpu::LoadOp::Clear(0),
|
|
|
|
store: true,
|
|
|
|
}),
|
|
|
|
}),
|
2020-12-27 06:33:38 +00:00
|
|
|
label: None,
|
2020-12-26 00:43:03 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Since RenderPass holds a reference to the CommandEncoder, we cast the lifetime
|
|
|
|
// away to allow for the self-referencing struct. draw_encoder is boxed so its
|
|
|
|
// address should remain stable.
|
|
|
|
self.current_frame = Some(Frame {
|
|
|
|
render_pass: unsafe {
|
|
|
|
std::mem::transmute::<_, wgpu::RenderPass<'static>>(render_pass)
|
|
|
|
},
|
|
|
|
frame_data,
|
|
|
|
});
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2020-12-15 03:18:27 +00:00
|
|
|
fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform, smoothing: bool) {
|
2020-12-30 23:35:43 +00:00
|
|
|
if let Some(texture) = self.textures.get(bitmap.0) {
|
2020-12-26 00:43:03 +00:00
|
|
|
let frame = if let Some(frame) = &mut self.current_frame {
|
|
|
|
frame.get()
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
2020-04-29 10:06:02 +00:00
|
|
|
|
|
|
|
let transform = Transform {
|
|
|
|
matrix: transform.matrix
|
|
|
|
* Matrix {
|
|
|
|
a: texture.width as f32,
|
|
|
|
d: texture.height as f32,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
..*transform
|
|
|
|
};
|
|
|
|
|
|
|
|
let world_matrix = [
|
|
|
|
[transform.matrix.a, transform.matrix.b, 0.0, 0.0],
|
|
|
|
[transform.matrix.c, transform.matrix.d, 0.0, 0.0],
|
|
|
|
[0.0, 0.0, 1.0, 0.0],
|
|
|
|
[
|
|
|
|
transform.matrix.tx.to_pixels() as f32,
|
|
|
|
transform.matrix.ty.to_pixels() as f32,
|
|
|
|
0.0,
|
|
|
|
1.0,
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_pipeline(
|
2020-12-26 03:19:42 +00:00
|
|
|
self.descriptors
|
2020-10-15 23:23:36 +00:00
|
|
|
.pipelines
|
2020-10-17 19:05:16 +00:00
|
|
|
.bitmap_pipelines
|
2020-10-15 23:23:36 +00:00
|
|
|
.pipeline_for(self.mask_state),
|
|
|
|
);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_push_constants(
|
|
|
|
wgpu::ShaderStage::VERTEX,
|
|
|
|
0,
|
|
|
|
bytemuck::cast_slice(&[Transforms { world_matrix }]),
|
|
|
|
);
|
|
|
|
frame.render_pass.set_push_constants(
|
|
|
|
wgpu::ShaderStage::FRAGMENT,
|
|
|
|
std::mem::size_of::<Transforms>() as u32,
|
|
|
|
bytemuck::cast_slice(&[ColorAdjustments::from(transform.color_transform)]),
|
|
|
|
);
|
|
|
|
frame
|
|
|
|
.render_pass
|
|
|
|
.set_bind_group(0, self.descriptors.globals.bind_group(), &[]);
|
2020-12-26 03:19:42 +00:00
|
|
|
frame
|
|
|
|
.render_pass
|
|
|
|
.set_bind_group(1, &texture.bind_group, &[]);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_bind_group(
|
|
|
|
2,
|
2020-12-15 03:18:27 +00:00
|
|
|
self.descriptors
|
|
|
|
.bitmap_samplers
|
|
|
|
.get_bind_group(false, smoothing),
|
2020-10-15 23:23:36 +00:00
|
|
|
&[],
|
|
|
|
);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame
|
|
|
|
.render_pass
|
|
|
|
.set_vertex_buffer(0, self.quad_vbo.slice(..));
|
2020-12-27 06:33:38 +00:00
|
|
|
frame
|
|
|
|
.render_pass
|
2021-01-04 23:45:40 +00:00
|
|
|
.set_index_buffer(self.quad_ibo.slice(..), wgpu::IndexFormat::Uint32);
|
2020-04-29 10:06:02 +00:00
|
|
|
|
2020-10-14 18:25:55 +00:00
|
|
|
match self.mask_state {
|
|
|
|
MaskState::NoMask => (),
|
|
|
|
MaskState::DrawMaskStencil => {
|
|
|
|
debug_assert!(self.num_masks > 0);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_stencil_reference(self.num_masks - 1);
|
2020-10-14 18:25:55 +00:00
|
|
|
}
|
|
|
|
MaskState::DrawMaskedContent | MaskState::ClearMaskStencil => {
|
|
|
|
debug_assert!(self.num_masks > 0);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_stencil_reference(self.num_masks);
|
2020-10-14 18:25:55 +00:00
|
|
|
}
|
|
|
|
};
|
2020-04-29 10:06:02 +00:00
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.draw_indexed(0..6, 0, 0..1);
|
2020-04-29 10:06:02 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-21 13:32:50 +00:00
|
|
|
|
|
|
|
fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) {
|
2020-12-26 00:43:03 +00:00
|
|
|
let frame = if let Some(frame) = &mut self.current_frame {
|
|
|
|
frame.get()
|
2020-05-04 20:59:06 +00:00
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-04-30 14:33:41 +00:00
|
|
|
let mesh = &mut self.meshes[shape.0];
|
2020-04-21 13:32:50 +00:00
|
|
|
|
|
|
|
let world_matrix = [
|
|
|
|
[transform.matrix.a, transform.matrix.b, 0.0, 0.0],
|
|
|
|
[transform.matrix.c, transform.matrix.d, 0.0, 0.0],
|
|
|
|
[0.0, 0.0, 1.0, 0.0],
|
|
|
|
[
|
|
|
|
transform.matrix.tx.to_pixels() as f32,
|
|
|
|
transform.matrix.ty.to_pixels() as f32,
|
|
|
|
0.0,
|
|
|
|
1.0,
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
frame
|
|
|
|
.render_pass
|
|
|
|
.set_bind_group(0, self.descriptors.globals.bind_group(), &[]);
|
2020-10-17 21:39:24 +00:00
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
for draw in &mesh.draws {
|
|
|
|
match &draw.draw_type {
|
|
|
|
DrawType::Color => {
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_pipeline(
|
2020-10-15 23:23:36 +00:00
|
|
|
&self
|
|
|
|
.descriptors
|
|
|
|
.pipelines
|
2020-10-17 19:05:16 +00:00
|
|
|
.color_pipelines
|
2020-10-15 23:23:36 +00:00
|
|
|
.pipeline_for(self.mask_state),
|
|
|
|
);
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
2020-10-17 21:39:24 +00:00
|
|
|
DrawType::Gradient { bind_group, .. } => {
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_pipeline(
|
2020-10-15 23:23:36 +00:00
|
|
|
&self
|
|
|
|
.descriptors
|
|
|
|
.pipelines
|
2020-10-17 19:05:16 +00:00
|
|
|
.gradient_pipelines
|
2020-10-15 23:23:36 +00:00
|
|
|
.pipeline_for(self.mask_state),
|
|
|
|
);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_bind_group(1, bind_group, &[]);
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
2020-10-15 22:55:19 +00:00
|
|
|
DrawType::Bitmap {
|
|
|
|
is_repeating,
|
|
|
|
is_smoothed,
|
2020-10-17 21:39:24 +00:00
|
|
|
bind_group,
|
2020-10-15 22:55:19 +00:00
|
|
|
..
|
|
|
|
} => {
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_pipeline(
|
2020-10-15 23:23:36 +00:00
|
|
|
&self
|
|
|
|
.descriptors
|
|
|
|
.pipelines
|
2020-10-17 19:05:16 +00:00
|
|
|
.bitmap_pipelines
|
2020-10-15 23:23:36 +00:00
|
|
|
.pipeline_for(self.mask_state),
|
|
|
|
);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_bind_group(1, bind_group, &[]);
|
|
|
|
frame.render_pass.set_bind_group(
|
|
|
|
2,
|
2020-10-15 23:23:36 +00:00
|
|
|
self.descriptors
|
|
|
|
.bitmap_samplers
|
2020-10-15 22:55:19 +00:00
|
|
|
.get_bind_group(*is_repeating, *is_smoothed),
|
|
|
|
&[],
|
|
|
|
);
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_push_constants(
|
|
|
|
wgpu::ShaderStage::VERTEX,
|
|
|
|
0,
|
|
|
|
bytemuck::cast_slice(&[Transforms { world_matrix }]),
|
|
|
|
);
|
|
|
|
frame.render_pass.set_push_constants(
|
|
|
|
wgpu::ShaderStage::FRAGMENT,
|
|
|
|
std::mem::size_of::<Transforms>() as u32,
|
|
|
|
bytemuck::cast_slice(&[ColorAdjustments::from(transform.color_transform)]),
|
|
|
|
);
|
|
|
|
frame
|
|
|
|
.render_pass
|
|
|
|
.set_vertex_buffer(0, draw.vertex_buffer.slice(..));
|
|
|
|
frame
|
|
|
|
.render_pass
|
2021-01-04 23:45:40 +00:00
|
|
|
.set_index_buffer(draw.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-10-14 18:25:55 +00:00
|
|
|
match self.mask_state {
|
|
|
|
MaskState::NoMask => (),
|
|
|
|
MaskState::DrawMaskStencil => {
|
|
|
|
debug_assert!(self.num_masks > 0);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_stencil_reference(self.num_masks - 1);
|
2020-10-14 18:25:55 +00:00
|
|
|
}
|
|
|
|
MaskState::DrawMaskedContent | MaskState::ClearMaskStencil => {
|
|
|
|
debug_assert!(self.num_masks > 0);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_stencil_reference(self.num_masks);
|
2020-10-14 18:25:55 +00:00
|
|
|
}
|
|
|
|
};
|
2020-04-28 20:48:17 +00:00
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.draw_indexed(0..draw.index_count, 0, 0..1);
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-15 21:14:25 +00:00
|
|
|
fn draw_rect(&mut self, color: Color, matrix: &Matrix) {
|
2020-12-26 00:43:03 +00:00
|
|
|
let frame = if let Some(frame) = &mut self.current_frame {
|
|
|
|
frame.get()
|
2020-09-15 21:14:25 +00:00
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
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,
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
let 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,
|
|
|
|
];
|
|
|
|
|
|
|
|
let add_color = [0.0, 0.0, 0.0, 0.0];
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_pipeline(
|
|
|
|
&self
|
|
|
|
.descriptors
|
|
|
|
.pipelines
|
|
|
|
.color_pipelines
|
|
|
|
.pipeline_for(self.mask_state),
|
|
|
|
);
|
2020-09-15 21:14:25 +00:00
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_push_constants(
|
|
|
|
wgpu::ShaderStage::VERTEX,
|
|
|
|
0,
|
2020-10-16 15:46:40 +00:00
|
|
|
bytemuck::cast_slice(&[Transforms { world_matrix }]),
|
2020-09-15 21:14:25 +00:00
|
|
|
);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_push_constants(
|
|
|
|
wgpu::ShaderStage::FRAGMENT,
|
|
|
|
std::mem::size_of::<Transforms>() as u32,
|
2020-09-15 21:14:25 +00:00
|
|
|
bytemuck::cast_slice(&[ColorAdjustments {
|
|
|
|
mult_color,
|
|
|
|
add_color,
|
|
|
|
}]),
|
|
|
|
);
|
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
frame
|
|
|
|
.render_pass
|
|
|
|
.set_bind_group(0, self.descriptors.globals.bind_group(), &[]);
|
|
|
|
frame
|
|
|
|
.render_pass
|
|
|
|
.set_vertex_buffer(0, self.quad_vbo.slice(..));
|
2020-12-27 06:33:38 +00:00
|
|
|
frame
|
|
|
|
.render_pass
|
2021-01-04 23:45:40 +00:00
|
|
|
.set_index_buffer(self.quad_ibo.slice(..), wgpu::IndexFormat::Uint32);
|
2020-09-15 21:14:25 +00:00
|
|
|
|
2020-10-14 18:25:55 +00:00
|
|
|
match self.mask_state {
|
|
|
|
MaskState::NoMask => (),
|
|
|
|
MaskState::DrawMaskStencil => {
|
|
|
|
debug_assert!(self.num_masks > 0);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_stencil_reference(self.num_masks - 1);
|
2020-10-14 18:25:55 +00:00
|
|
|
}
|
|
|
|
MaskState::DrawMaskedContent | MaskState::ClearMaskStencil => {
|
|
|
|
debug_assert!(self.num_masks > 0);
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.set_stencil_reference(self.num_masks);
|
2020-10-14 18:25:55 +00:00
|
|
|
}
|
|
|
|
};
|
2020-09-15 21:14:25 +00:00
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
frame.render_pass.draw_indexed(0..6, 0, 0..1);
|
2020-09-15 21:14:25 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
fn end_frame(&mut self) {
|
2020-12-26 00:43:03 +00:00
|
|
|
if let Some(frame) = self.current_frame.take() {
|
|
|
|
// Finalize render pass.
|
|
|
|
drop(frame.render_pass);
|
2020-10-29 03:08:41 +00:00
|
|
|
|
2020-12-26 00:43:03 +00:00
|
|
|
let draw_encoder = frame.frame_data.0;
|
2020-10-15 23:23:36 +00:00
|
|
|
self.target.submit(
|
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.queue,
|
2020-12-26 00:43:03 +00:00
|
|
|
vec![draw_encoder.finish()],
|
2020-10-15 23:23:36 +00:00
|
|
|
);
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-28 20:48:17 +00:00
|
|
|
fn push_mask(&mut self) {
|
2021-01-14 21:59:23 +00:00
|
|
|
debug_assert!(
|
2020-10-14 18:25:55 +00:00
|
|
|
self.mask_state == MaskState::NoMask || self.mask_state == MaskState::DrawMaskedContent
|
|
|
|
);
|
2020-04-28 20:48:17 +00:00
|
|
|
self.num_masks += 1;
|
2020-10-14 18:25:55 +00:00
|
|
|
self.mask_state = MaskState::DrawMaskStencil;
|
2020-04-28 20:48:17 +00:00
|
|
|
}
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-04-28 20:48:17 +00:00
|
|
|
fn activate_mask(&mut self) {
|
2021-01-14 21:59:23 +00:00
|
|
|
debug_assert!(self.num_masks > 0 && self.mask_state == MaskState::DrawMaskStencil);
|
2020-10-14 18:25:55 +00:00
|
|
|
self.mask_state = MaskState::DrawMaskedContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn deactivate_mask(&mut self) {
|
2021-01-14 21:59:23 +00:00
|
|
|
debug_assert!(self.num_masks > 0 && self.mask_state == MaskState::DrawMaskedContent);
|
2020-10-14 18:25:55 +00:00
|
|
|
self.mask_state = MaskState::ClearMaskStencil;
|
2020-04-28 20:48:17 +00:00
|
|
|
}
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2020-04-28 20:48:17 +00:00
|
|
|
fn pop_mask(&mut self) {
|
2021-01-14 21:59:23 +00:00
|
|
|
debug_assert!(self.num_masks > 0 && self.mask_state == MaskState::ClearMaskStencil);
|
2020-10-14 18:25:55 +00:00
|
|
|
self.num_masks -= 1;
|
|
|
|
self.mask_state = if self.num_masks == 0 {
|
|
|
|
MaskState::NoMask
|
|
|
|
} else {
|
|
|
|
MaskState::DrawMaskedContent
|
|
|
|
};
|
2020-04-28 20:48:17 +00:00
|
|
|
}
|
2020-10-19 00:25:23 +00:00
|
|
|
|
2020-11-01 18:43:15 +00:00
|
|
|
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> Option<Bitmap> {
|
|
|
|
self.bitmap_registry.get(&bitmap).cloned()
|
|
|
|
}
|
|
|
|
|
2020-12-09 18:38:41 +00:00
|
|
|
fn register_bitmap_raw(
|
|
|
|
&mut self,
|
|
|
|
width: u32,
|
|
|
|
height: u32,
|
|
|
|
rgba: Vec<u8>,
|
|
|
|
) -> Result<BitmapHandle, Error> {
|
|
|
|
Ok(self
|
|
|
|
.register_bitmap(
|
|
|
|
Bitmap {
|
|
|
|
height,
|
|
|
|
width,
|
|
|
|
data: BitmapFormat::Rgba(rgba),
|
|
|
|
},
|
|
|
|
"RAW",
|
2020-12-09 19:01:25 +00:00
|
|
|
)
|
2020-12-09 18:38:41 +00:00
|
|
|
.handle)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_texture(
|
|
|
|
&mut self,
|
|
|
|
handle: BitmapHandle,
|
|
|
|
width: u32,
|
|
|
|
height: u32,
|
|
|
|
rgba: Vec<u8>,
|
|
|
|
) -> Result<BitmapHandle, Error> {
|
2020-12-30 23:35:43 +00:00
|
|
|
let texture = if let Some(texture) = self.textures.get(handle.0) {
|
2020-12-15 02:35:12 +00:00
|
|
|
&texture.texture
|
|
|
|
} else {
|
|
|
|
return Err("update_texture: Bitmap not registered".into());
|
2020-12-09 18:38:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let extent = wgpu::Extent3d {
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
depth: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
self.descriptors.queue.write_texture(
|
|
|
|
wgpu::TextureCopyView {
|
|
|
|
texture: &texture,
|
|
|
|
mip_level: 0,
|
|
|
|
origin: Default::default(),
|
|
|
|
},
|
|
|
|
&rgba,
|
|
|
|
wgpu::TextureDataLayout {
|
|
|
|
offset: 0,
|
|
|
|
bytes_per_row: 4 * extent.width,
|
|
|
|
rows_per_image: 0,
|
2020-11-01 21:07:27 +00:00
|
|
|
},
|
2020-12-09 18:38:41 +00:00
|
|
|
extent,
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(handle)
|
2020-10-19 00:25:23 +00:00
|
|
|
}
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
2020-04-29 10:06:02 +00:00
|
|
|
fn create_quad_buffers(device: &wgpu::Device) -> (wgpu::Buffer, wgpu::Buffer, wgpu::Buffer) {
|
|
|
|
let vertices = [
|
2021-02-12 13:03:17 +00:00
|
|
|
GpuVertex {
|
2020-04-29 10:06:02 +00:00
|
|
|
position: [0.0, 0.0],
|
|
|
|
color: [1.0, 1.0, 1.0, 1.0],
|
|
|
|
},
|
2021-02-12 13:03:17 +00:00
|
|
|
GpuVertex {
|
2020-04-29 10:06:02 +00:00
|
|
|
position: [1.0, 0.0],
|
|
|
|
color: [1.0, 1.0, 1.0, 1.0],
|
|
|
|
},
|
2021-02-12 13:03:17 +00:00
|
|
|
GpuVertex {
|
2020-04-29 10:06:02 +00:00
|
|
|
position: [1.0, 1.0],
|
|
|
|
color: [1.0, 1.0, 1.0, 1.0],
|
|
|
|
},
|
2021-02-12 13:03:17 +00:00
|
|
|
GpuVertex {
|
2020-04-29 10:06:02 +00:00
|
|
|
position: [0.0, 1.0],
|
|
|
|
color: [1.0, 1.0, 1.0, 1.0],
|
|
|
|
},
|
|
|
|
];
|
2021-01-04 23:45:40 +00:00
|
|
|
let indices: [u32; 6] = [0, 1, 2, 0, 2, 3];
|
2020-04-29 10:06:02 +00:00
|
|
|
|
|
|
|
let vbo = create_buffer_with_data(
|
|
|
|
device,
|
|
|
|
bytemuck::cast_slice(&vertices),
|
|
|
|
wgpu::BufferUsage::VERTEX,
|
|
|
|
create_debug_label!("Quad vbo"),
|
|
|
|
);
|
|
|
|
|
|
|
|
let ibo = create_buffer_with_data(
|
|
|
|
device,
|
|
|
|
bytemuck::cast_slice(&indices),
|
|
|
|
wgpu::BufferUsage::INDEX,
|
|
|
|
create_debug_label!("Quad ibo"),
|
|
|
|
);
|
|
|
|
|
|
|
|
let tex_transforms = create_buffer_with_data(
|
|
|
|
device,
|
|
|
|
bytemuck::cast_slice(&[TextureTransforms {
|
|
|
|
u_matrix: [
|
|
|
|
[1.0, 0.0, 0.0, 0.0],
|
|
|
|
[0.0, 1.0, 0.0, 0.0],
|
|
|
|
[0.0, 0.0, 1.0, 0.0],
|
|
|
|
[0.0, 0.0, 0.0, 1.0],
|
|
|
|
],
|
|
|
|
}]),
|
|
|
|
wgpu::BufferUsage::UNIFORM,
|
|
|
|
create_debug_label!("Quad tex transforms"),
|
|
|
|
);
|
|
|
|
|
|
|
|
(vbo, ibo, tex_transforms)
|
|
|
|
}
|
|
|
|
|
2020-05-20 22:02:42 +00:00
|
|
|
/// Converts a gradient to the uniforms used by the shader.
|
|
|
|
fn swf_gradient_to_uniforms(
|
|
|
|
gradient_type: i32,
|
|
|
|
gradient: &swf::Gradient,
|
|
|
|
focal_point: f32,
|
|
|
|
) -> GradientUniforms {
|
|
|
|
let mut colors: [[f32; 4]; 16] = Default::default();
|
|
|
|
let mut ratios: [f32; 16] = Default::default();
|
|
|
|
for (i, record) in gradient.records.iter().enumerate() {
|
|
|
|
if i >= 16 {
|
|
|
|
// TODO: we need to support these!
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
colors[i] = [
|
|
|
|
f32::from(record.color.r) / 255.0,
|
|
|
|
f32::from(record.color.g) / 255.0,
|
|
|
|
f32::from(record.color.b) / 255.0,
|
|
|
|
f32::from(record.color.a) / 255.0,
|
|
|
|
];
|
|
|
|
ratios[i] = f32::from(record.ratio) / 255.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert colors from sRGB to linear space if necessary.
|
2021-02-12 13:03:17 +00:00
|
|
|
if gradient.interpolation == GradientInterpolation::LinearRgb {
|
2020-05-20 22:02:42 +00:00
|
|
|
for color in &mut colors[0..gradient.records.len()] {
|
|
|
|
*color = srgb_to_linear(*color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GradientUniforms {
|
|
|
|
gradient_type,
|
|
|
|
ratios,
|
|
|
|
colors,
|
2021-02-12 13:03:17 +00:00
|
|
|
interpolation: (gradient.interpolation == GradientInterpolation::LinearRgb) as i32,
|
2020-05-20 22:02:42 +00:00
|
|
|
num_colors: gradient.records.len() as u32,
|
|
|
|
repeat_mode: gradient_spread_mode_index(gradient.spread),
|
|
|
|
focal_point,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct Texture {
|
|
|
|
width: u32,
|
|
|
|
height: u32,
|
|
|
|
texture: wgpu::Texture,
|
2020-12-26 03:19:42 +00:00
|
|
|
bind_group: wgpu::BindGroup,
|
2020-04-21 13:32:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct RuffleVertexCtor {
|
|
|
|
color: [f32; 4],
|
|
|
|
}
|
|
|
|
|
2021-02-12 13:03:17 +00:00
|
|
|
impl FillVertexConstructor<GpuVertex> for RuffleVertexCtor {
|
|
|
|
fn new_vertex(&mut self, vertex: FillVertex) -> GpuVertex {
|
|
|
|
GpuVertex {
|
2021-01-04 20:45:16 +00:00
|
|
|
position: [vertex.position().x, vertex.position().y],
|
2020-04-21 13:32:50 +00:00
|
|
|
color: self.color,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 13:03:17 +00:00
|
|
|
impl StrokeVertexConstructor<GpuVertex> for RuffleVertexCtor {
|
|
|
|
fn new_vertex(&mut self, vertex: StrokeVertex) -> GpuVertex {
|
|
|
|
GpuVertex {
|
2021-01-04 20:45:16 +00:00
|
|
|
position: [vertex.position().x, vertex.position().y],
|
2020-04-21 13:32:50 +00:00
|
|
|
color: self.color,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|