render: Add wgpu-rs based renderer, used by desktop

This commit is contained in:
Nathan Adams 2020-04-21 15:32:50 +02:00
parent 834756c167
commit f0445d94b8
22 changed files with 2418 additions and 1256 deletions

696
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ members = [
"scanner", "scanner",
"render/canvas", "render/canvas",
"render/glium", "render/wgpu",
"render/common_tess", "render/common_tess",
] ]

View File

@ -8,9 +8,7 @@ default-run = "ruffle_desktop"
[dependencies] [dependencies]
cpal = "0.11.0" cpal = "0.11.0"
ruffle_core = { path = "../core" } ruffle_core = { path = "../core" }
ruffle_render_glium = { path = "../render/glium" } ruffle_render_wgpu = { path = "../render/wgpu" }
glium = "0.27.0"
glutin = "0.24"
env_logger = "0.7.1" env_logger = "0.7.1"
generational-arena = "0.2.7" generational-arena = "0.2.7"
image = "0.23.4" image = "0.23.4"

View File

@ -3,11 +3,11 @@
use crate::custom_event::RuffleEvent; use crate::custom_event::RuffleEvent;
use crate::task::Task; use crate::task::Task;
use generational_arena::{Arena, Index}; use generational_arena::{Arena, Index};
use glutin::event_loop::EventLoopProxy;
use ruffle_core::backend::navigator::{Error, OwnedFuture}; use ruffle_core::backend::navigator::{Error, OwnedFuture};
use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex, Weak}; use std::sync::{Arc, Mutex, Weak};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use winit::event_loop::EventLoopProxy;
/// Exeuctor context passed to event sources. /// Exeuctor context passed to event sources.
/// ///

View File

@ -1,23 +1,24 @@
use glium::Display;
use ruffle_core::backend::input::{InputBackend, MouseCursor}; use ruffle_core::backend::input::{InputBackend, MouseCursor};
use ruffle_core::events::{KeyCode, PlayerEvent}; use ruffle_core::events::{KeyCode, PlayerEvent};
use std::collections::HashSet; use std::collections::HashSet;
use std::rc::Rc;
use winit::event::{ElementState, VirtualKeyCode, WindowEvent}; use winit::event::{ElementState, VirtualKeyCode, WindowEvent};
use winit::window::Window;
pub struct WinitInputBackend { pub struct WinitInputBackend {
keys_down: HashSet<VirtualKeyCode>, keys_down: HashSet<VirtualKeyCode>,
display: Display, window: Rc<Window>,
cursor_visible: bool, cursor_visible: bool,
last_key: KeyCode, last_key: KeyCode,
} }
impl WinitInputBackend { impl WinitInputBackend {
pub fn new(display: Display) -> Self { pub fn new(window: Rc<Window>) -> Self {
Self { Self {
keys_down: HashSet::new(), keys_down: HashSet::new(),
cursor_visible: true, cursor_visible: true,
last_key: KeyCode::Unknown, last_key: KeyCode::Unknown,
display, window,
} }
} }
@ -176,12 +177,12 @@ impl InputBackend for WinitInputBackend {
} }
fn hide_mouse(&mut self) { fn hide_mouse(&mut self) {
self.display.gl_window().window().set_cursor_visible(false); self.window.set_cursor_visible(false);
self.cursor_visible = false; self.cursor_visible = false;
} }
fn show_mouse(&mut self) { fn show_mouse(&mut self) {
self.display.gl_window().window().set_cursor_visible(true); self.window.set_cursor_visible(true);
self.cursor_visible = true; self.cursor_visible = true;
} }
@ -193,7 +194,7 @@ impl InputBackend for WinitInputBackend {
MouseCursor::IBeam => CursorIcon::Text, MouseCursor::IBeam => CursorIcon::Text,
MouseCursor::Grab => CursorIcon::Grab, MouseCursor::Grab => CursorIcon::Grab,
}; };
self.display.gl_window().window().set_cursor_icon(icon); self.window.set_cursor_icon(icon);
} }
} }

View File

@ -9,22 +9,21 @@ mod task;
use crate::custom_event::RuffleEvent; use crate::custom_event::RuffleEvent;
use crate::executor::GlutinAsyncExecutor; use crate::executor::GlutinAsyncExecutor;
use glutin::{
dpi::{LogicalSize, PhysicalPosition},
event::{ElementState, MouseButton, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
ContextBuilder,
};
use ruffle_core::{ use ruffle_core::{
backend::audio::{AudioBackend, NullAudioBackend}, backend::audio::{AudioBackend, NullAudioBackend},
Player, Player,
}; };
use ruffle_render_glium::GliumRenderBackend; use ruffle_render_wgpu::WGPURenderBackend;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Instant; use std::time::Instant;
use structopt::StructOpt; use structopt::StructOpt;
use std::rc::Rc;
use winit::dpi::{LogicalSize, PhysicalPosition};
use winit::event::{ElementState, MouseButton, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
#[structopt(name = "basic")] #[structopt(name = "basic")]
struct Opt { struct Opt {
@ -49,16 +48,15 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
let swf_data = std::fs::read(&input_path)?; let swf_data = std::fs::read(&input_path)?;
let event_loop: EventLoop<RuffleEvent> = EventLoop::with_user_event(); let event_loop: EventLoop<RuffleEvent> = EventLoop::with_user_event();
let window_builder = WindowBuilder::new().with_title(format!( let window = Rc::new(
WindowBuilder::new()
.with_title(format!(
"Ruffle - {}", "Ruffle - {}",
input_path.file_name().unwrap_or_default().to_string_lossy() input_path.file_name().unwrap_or_default().to_string_lossy()
)); ))
let windowed_context = ContextBuilder::new() .build(&event_loop)?,
.with_vsync(true) );
.with_multisampling(4)
.with_srgb(true)
.with_stencil_buffer(8)
.build_windowed(window_builder, &event_loop)?;
let audio: Box<dyn AudioBackend> = match audio::CpalAudioBackend::new() { let audio: Box<dyn AudioBackend> = match audio::CpalAudioBackend::new() {
Ok(audio) => Box::new(audio), Ok(audio) => Box::new(audio),
Err(e) => { Err(e) => {
@ -66,7 +64,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
Box::new(NullAudioBackend::new()) Box::new(NullAudioBackend::new())
} }
}; };
let renderer = Box::new(GliumRenderBackend::new(windowed_context)?); let renderer = Box::new(WGPURenderBackend::new(window.clone())?);
let (executor, chan) = GlutinAsyncExecutor::new(event_loop.create_proxy()); let (executor, chan) = GlutinAsyncExecutor::new(event_loop.create_proxy());
let navigator = Box::new(navigator::ExternalNavigatorBackend::with_base_path( let navigator = Box::new(navigator::ExternalNavigatorBackend::with_base_path(
input_path input_path
@ -75,8 +73,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
chan, chan,
event_loop.create_proxy(), event_loop.create_proxy(),
)); //TODO: actually implement this backend type )); //TODO: actually implement this backend type
let display = renderer.display().clone(); let input = Box::new(input::WinitInputBackend::new(window.clone()));
let input = Box::new(input::WinitInputBackend::new(display.clone()));
let player = Player::new(renderer, audio, navigator, input, swf_data)?; let player = Player::new(renderer, audio, navigator, input, swf_data)?;
let logical_size: LogicalSize<u32> = { let logical_size: LogicalSize<u32> = {
@ -85,13 +82,15 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
(player_lock.movie_width(), player_lock.movie_height()).into() (player_lock.movie_width(), player_lock.movie_height()).into()
}; };
let scale_factor = display.gl_window().window().scale_factor(); let scale_factor = window.scale_factor();
// Set initial size to movie dimensions. // Set initial size to movie dimensions.
display.gl_window().window().set_inner_size(logical_size); window.set_inner_size(logical_size);
display let size = logical_size.to_physical(scale_factor);
.gl_window() player
.resize(logical_size.to_physical(scale_factor)); .lock()
.unwrap()
.set_viewport_dimensions(size.width, size.height);
let mut mouse_pos = PhysicalPosition::new(0.0, 0.0); let mut mouse_pos = PhysicalPosition::new(0.0, 0.0);
let mut time = Instant::now(); let mut time = Instant::now();
@ -100,8 +99,8 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
event_loop.run(move |event, _window_target, control_flow| { event_loop.run(move |event, _window_target, control_flow| {
*control_flow = ControlFlow::Wait; *control_flow = ControlFlow::Wait;
match event { match event {
glutin::event::Event::LoopDestroyed => return, winit::event::Event::LoopDestroyed => return,
glutin::event::Event::WindowEvent { event, .. } => match event { winit::event::Event::WindowEvent { event, .. } => match event {
WindowEvent::Resized(size) => { WindowEvent::Resized(size) => {
let mut player_lock = player.lock().unwrap(); let mut player_lock = player.lock().unwrap();
player_lock.set_viewport_dimensions(size.width, size.height); player_lock.set_viewport_dimensions(size.width, size.height);
@ -155,7 +154,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
} }
_ => (), _ => (),
}, },
glutin::event::Event::UserEvent(RuffleEvent::TaskPoll) => executor winit::event::Event::UserEvent(RuffleEvent::TaskPoll) => executor
.lock() .lock()
.expect("active executor reference") .expect("active executor reference")
.poll_all(), .poll_all(),

View File

@ -1,7 +1,6 @@
//! Navigator backend for web //! Navigator backend for web
use crate::custom_event::RuffleEvent; use crate::custom_event::RuffleEvent;
use glutin::event_loop::EventLoopProxy;
use ruffle_core::backend::navigator::{ use ruffle_core::backend::navigator::{
Error, NavigationMethod, NavigatorBackend, OwnedFuture, RequestOptions, Error, NavigationMethod, NavigatorBackend, OwnedFuture, RequestOptions,
}; };
@ -11,6 +10,7 @@ use std::path::{Path, PathBuf};
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use url::Url; use url::Url;
use winit::event_loop::EventLoopProxy;
/// Implementation of `NavigatorBackend` for non-web environments that can call /// Implementation of `NavigatorBackend` for non-web environments that can call
/// out to a web browser. /// out to a web browser.

View File

@ -1,14 +0,0 @@
[package]
name = "ruffle_render_glium"
version = "0.1.0"
authors = ["Mike Welsh <mwelsh@gmail.com>"]
edition = "2018"
[dependencies]
glium = "0.27.0"
glutin = "0.24"
image = "0.23.4"
jpeg-decoder = "0.1.18"
log = "0.4"
ruffle_core = { path = "../../core" }
ruffle_render_common_tess = { path = "../common_tess" }

File diff suppressed because it is too large Load Diff

17
render/wgpu/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "ruffle_render_wgpu"
version = "0.1.0"
authors = ["Nathan Adams <dinnerbone@dinnerbone.com>"]
edition = "2018"
[dependencies]
wgpu = "0.5"
wgpu-native = "0.5"
image = "0.23.4"
jpeg-decoder = "0.1.18"
log = "0.4"
lyon = "0.15.6"
ruffle_core = { path = "../../core" }
futures = "0.3.4"
bytemuck = "1.2.0"
winit = "0.22"

6
render/wgpu/build_shaders.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
glslangValidator -V ./shaders/color.frag -o ./shaders/color.frag.spv
glslangValidator -V ./shaders/color.vert -o ./shaders/color.vert.spv
glslangValidator -V ./shaders/bitmap.frag -o ./shaders/bitmap.frag.spv
glslangValidator -V ./shaders/gradient.frag -o ./shaders/gradient.frag.spv
glslangValidator -V ./shaders/texture.vert -o ./shaders/texture.vert.spv

View File

@ -0,0 +1,26 @@
#version 450
layout(set = 0, binding = 2) uniform Colors {
vec4 mult_color;
vec4 add_color;
};
layout(set = 0, binding = 3) uniform texture2D t_color;
layout(set = 0, binding = 4) uniform sampler s_color;
layout(location=0) in vec2 frag_uv;
layout(location=0) out vec4 out_color;
void main() {
vec4 color = texture(sampler2D(t_color, s_color), frag_uv);
// Unmultiply alpha before apply color transform.
if( color.a > 0 ) {
color.rgb /= color.a;
color = mult_color * color + add_color;
color.rgb *= color.a;
}
out_color = color;
}

Binary file not shown.

View File

@ -0,0 +1,9 @@
#version 450
layout(location=0) in vec4 frag_color;
layout(location=0) out vec4 out_color;
void main() {
out_color = frag_color;
}

Binary file not shown.

View File

@ -0,0 +1,22 @@
#version 450
layout(set = 0, binding = 0) uniform Transforms {
mat4 view_matrix;
mat4 world_matrix;
};
layout(set = 0, binding = 1) uniform Colors {
vec4 mult_color;
vec4 add_color;
};
layout(location = 0) in vec2 position;
layout(location = 1) in vec4 color;
layout(location = 0) out vec4 frag_color;
void main() {
frag_color = color * mult_color + add_color;
gl_Position = view_matrix * world_matrix * vec4(position, 0.0, 1.0);
gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0;
}

Binary file not shown.

View File

@ -0,0 +1,75 @@
#version 450
layout(set = 0, binding = 2) uniform Colors {
vec4 mult_color;
vec4 add_color;
};
layout(set = 0, binding = 3) uniform Gradient {
int u_gradient_type;
uint u_num_colors;
int u_repeat_mode;
float u_focal_point;
vec4 u_ratios[16];
vec4 u_colors[16];
};
layout(location=0) in vec2 frag_uv;
layout(location=0) out vec4 out_color;
void main() {
vec4 color;
int last = int(int(u_num_colors) - 1);
float t;
if( u_gradient_type == 0 )
{
t = frag_uv.x;
}
else if( u_gradient_type == 1 )
{
t = length(frag_uv * 2.0 - 1.0);
}
else if( u_gradient_type == 2 )
{
vec2 uv = frag_uv * 2.0 - 1.0;
vec2 d = vec2(u_focal_point, 0.0) - uv;
float l = length(d);
d /= l;
t = l / (sqrt(1.0 - u_focal_point*u_focal_point*d.y*d.y) + u_focal_point*d.x);
}
if( u_repeat_mode == 0 )
{
// Clamp
t = clamp(t, 0.0, 1.0);
}
else if( u_repeat_mode == 1 )
{
// Repeat
t = fract(t);
}
else
{
// Mirror
if( t < 0.0 )
{
t = -t;
}
if( (int(t)&1) == 0 ) {
t = fract(t);
} else {
t = 1.0 - fract(t);
}
}
int i = 0;
int j = 1;
t = clamp(t, u_ratios[0].x, u_ratios[last].x);
while( t > u_ratios[j].x )
{
i = j;
j++;
}
float a = (t - u_ratios[i].x) / (u_ratios[j].x - u_ratios[i].x);
color = mix(u_colors[i], u_colors[j], a);
out_color = mult_color * color + add_color;
}

Binary file not shown.

View File

@ -0,0 +1,21 @@
#version 450
layout(set = 0, binding = 0) uniform Transforms {
mat4 view_matrix;
mat4 world_matrix;
};
layout(set = 0, binding = 1) uniform Texture {
mat4 u_matrix;
};
layout(location = 0) in vec2 position;
layout(location = 1) in vec4 color;
layout(location = 0) out vec2 frag_uv;
void main() {
frag_uv = vec2(mat3(u_matrix) * vec3(position, 1.0));
gl_Position = view_matrix * world_matrix * vec4(position, 0.0, 1.0);
gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0;
}

Binary file not shown.

1692
render/wgpu/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff