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",
"render/canvas",
"render/glium",
"render/wgpu",
"render/common_tess",
]

View File

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

View File

@ -3,11 +3,11 @@
use crate::custom_event::RuffleEvent;
use crate::task::Task;
use generational_arena::{Arena, Index};
use glutin::event_loop::EventLoopProxy;
use ruffle_core::backend::navigator::{Error, OwnedFuture};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex, Weak};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use winit::event_loop::EventLoopProxy;
/// 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::events::{KeyCode, PlayerEvent};
use std::collections::HashSet;
use std::rc::Rc;
use winit::event::{ElementState, VirtualKeyCode, WindowEvent};
use winit::window::Window;
pub struct WinitInputBackend {
keys_down: HashSet<VirtualKeyCode>,
display: Display,
window: Rc<Window>,
cursor_visible: bool,
last_key: KeyCode,
}
impl WinitInputBackend {
pub fn new(display: Display) -> Self {
pub fn new(window: Rc<Window>) -> Self {
Self {
keys_down: HashSet::new(),
cursor_visible: true,
last_key: KeyCode::Unknown,
display,
window,
}
}
@ -176,12 +177,12 @@ impl InputBackend for WinitInputBackend {
}
fn hide_mouse(&mut self) {
self.display.gl_window().window().set_cursor_visible(false);
self.window.set_cursor_visible(false);
self.cursor_visible = false;
}
fn show_mouse(&mut self) {
self.display.gl_window().window().set_cursor_visible(true);
self.window.set_cursor_visible(true);
self.cursor_visible = true;
}
@ -193,7 +194,7 @@ impl InputBackend for WinitInputBackend {
MouseCursor::IBeam => CursorIcon::Text,
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::executor::GlutinAsyncExecutor;
use glutin::{
dpi::{LogicalSize, PhysicalPosition},
event::{ElementState, MouseButton, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
ContextBuilder,
};
use ruffle_core::{
backend::audio::{AudioBackend, NullAudioBackend},
Player,
};
use ruffle_render_glium::GliumRenderBackend;
use ruffle_render_wgpu::WGPURenderBackend;
use std::path::PathBuf;
use std::time::Instant;
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)]
#[structopt(name = "basic")]
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 event_loop: EventLoop<RuffleEvent> = EventLoop::with_user_event();
let window_builder = WindowBuilder::new().with_title(format!(
"Ruffle - {}",
input_path.file_name().unwrap_or_default().to_string_lossy()
));
let windowed_context = ContextBuilder::new()
.with_vsync(true)
.with_multisampling(4)
.with_srgb(true)
.with_stencil_buffer(8)
.build_windowed(window_builder, &event_loop)?;
let window = Rc::new(
WindowBuilder::new()
.with_title(format!(
"Ruffle - {}",
input_path.file_name().unwrap_or_default().to_string_lossy()
))
.build(&event_loop)?,
);
let audio: Box<dyn AudioBackend> = match audio::CpalAudioBackend::new() {
Ok(audio) => Box::new(audio),
Err(e) => {
@ -66,7 +64,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
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 navigator = Box::new(navigator::ExternalNavigatorBackend::with_base_path(
input_path
@ -75,8 +73,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
chan,
event_loop.create_proxy(),
)); //TODO: actually implement this backend type
let display = renderer.display().clone();
let input = Box::new(input::WinitInputBackend::new(display.clone()));
let input = Box::new(input::WinitInputBackend::new(window.clone()));
let player = Player::new(renderer, audio, navigator, input, swf_data)?;
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()
};
let scale_factor = display.gl_window().window().scale_factor();
let scale_factor = window.scale_factor();
// Set initial size to movie dimensions.
display.gl_window().window().set_inner_size(logical_size);
display
.gl_window()
.resize(logical_size.to_physical(scale_factor));
window.set_inner_size(logical_size);
let size = logical_size.to_physical(scale_factor);
player
.lock()
.unwrap()
.set_viewport_dimensions(size.width, size.height);
let mut mouse_pos = PhysicalPosition::new(0.0, 0.0);
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| {
*control_flow = ControlFlow::Wait;
match event {
glutin::event::Event::LoopDestroyed => return,
glutin::event::Event::WindowEvent { event, .. } => match event {
winit::event::Event::LoopDestroyed => return,
winit::event::Event::WindowEvent { event, .. } => match event {
WindowEvent::Resized(size) => {
let mut player_lock = player.lock().unwrap();
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()
.expect("active executor reference")
.poll_all(),

View File

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