desktop: Render game separately to UI, blit both onto surface
This commit is contained in:
parent
5bf43f9025
commit
b9322ba93a
|
@ -3726,6 +3726,7 @@ dependencies = [
|
||||||
"egui-wgpu",
|
"egui-wgpu",
|
||||||
"egui-winit",
|
"egui-winit",
|
||||||
"embed-resource",
|
"embed-resource",
|
||||||
|
"futures",
|
||||||
"generational-arena",
|
"generational-arena",
|
||||||
"isahc",
|
"isahc",
|
||||||
"os_info",
|
"os_info",
|
||||||
|
|
|
@ -33,6 +33,7 @@ os_info = { version = "3", default-features = false }
|
||||||
unic-langid = "0.9.1"
|
unic-langid = "0.9.1"
|
||||||
sys-locale = "0.3.0"
|
sys-locale = "0.3.0"
|
||||||
wgpu = { version = "0.16.0" }
|
wgpu = { version = "0.16.0" }
|
||||||
|
futures = "0.3.28"
|
||||||
|
|
||||||
# Deliberately held back to match tracy client used by profiling crate
|
# Deliberately held back to match tracy client used by profiling crate
|
||||||
tracing-tracy = { version = "=0.10.0", optional = true }
|
tracing-tracy = { version = "=0.10.0", optional = true }
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Vertex shader bindings
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@location(0) tex_coord: vec2<f32>,
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Converts a color from sRGB to linear color space.
|
||||||
|
fn srgb_to_linear(srgb: vec4<f32>) -> vec4<f32> {
|
||||||
|
var rgb: vec3<f32> = srgb.rgb;
|
||||||
|
if( srgb.a > 0.0 ) {
|
||||||
|
rgb = rgb / srgb.a;
|
||||||
|
}
|
||||||
|
let a = rgb / 12.92;
|
||||||
|
let b = pow((rgb + vec3<f32>(0.055)) / 1.055, vec3<f32>(2.4));
|
||||||
|
let c = step(vec3<f32>(0.04045), rgb);
|
||||||
|
return vec4<f32>(mix(a, b, c) * srgb.a, srgb.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
@location(0) a_pos: vec2<f32>,
|
||||||
|
@location(1) a_tex_coord: vec2<f32>,
|
||||||
|
) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
out.tex_coord = a_tex_coord;
|
||||||
|
out.position = vec4<f32>(a_pos, 0.0, 1.0);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fragment shader bindings
|
||||||
|
|
||||||
|
@group(0) @binding(0) var r_tex_color: texture_2d<f32>;
|
||||||
|
@group(0) @binding(1) var r_tex_sampler: sampler;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main_linear_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
// We always have a linear texture at the moment.
|
||||||
|
return textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main_srgb_framebuffer(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
// We always have a linear texture at the moment.
|
||||||
|
let tex = textureSample(r_tex_color, r_tex_sampler, in.tex_coord);
|
||||||
|
return srgb_to_linear(tex);
|
||||||
|
}
|
|
@ -1,12 +1,21 @@
|
||||||
use crate::custom_event::RuffleEvent;
|
use crate::custom_event::RuffleEvent;
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
use egui::*;
|
use egui::*;
|
||||||
|
use ruffle_render_wgpu::backend::request_adapter_and_device;
|
||||||
|
use ruffle_render_wgpu::descriptors::Descriptors;
|
||||||
|
use ruffle_render_wgpu::utils::{format_list, get_backend_names};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
use winit::event_loop::{EventLoop, EventLoopProxy};
|
use winit::event_loop::{EventLoop, EventLoopProxy};
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
/// Integration layer conneting wgpu+winit to egui.
|
/// Integration layer conneting wgpu+winit to egui.
|
||||||
pub struct GuiController {
|
pub struct GuiController {
|
||||||
|
descriptors: Arc<Descriptors>,
|
||||||
egui_ctx: egui::Context,
|
egui_ctx: egui::Context,
|
||||||
egui_winit: egui_winit::State,
|
egui_winit: egui_winit::State,
|
||||||
egui_renderer: egui_wgpu::renderer::Renderer,
|
egui_renderer: egui_wgpu::renderer::Renderer,
|
||||||
|
@ -14,25 +23,166 @@ pub struct GuiController {
|
||||||
window: Rc<Window>,
|
window: Rc<Window>,
|
||||||
last_update: Instant,
|
last_update: Instant,
|
||||||
repaint_after: Duration,
|
repaint_after: Duration,
|
||||||
|
surface: wgpu::Surface,
|
||||||
|
surface_format: wgpu::TextureFormat,
|
||||||
|
blit_bind_group_layout: wgpu::BindGroupLayout,
|
||||||
|
blit_pipeline: wgpu::RenderPipeline,
|
||||||
|
blit_sampler: wgpu::Sampler,
|
||||||
|
blit_vertices: wgpu::Buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// x y u v
|
||||||
|
const BLIT_VERTICES: [[f32; 4]; 6] = [
|
||||||
|
[-1.0, 1.0, 0.0, 0.0], // tl
|
||||||
|
[1.0, 1.0, 1.0, 0.0], // tr
|
||||||
|
[1.0, -1.0, 1.0, 1.0], // br
|
||||||
|
[1.0, -1.0, 1.0, 1.0], // br
|
||||||
|
[-1.0, -1.0, 0.0, 1.0], // bl
|
||||||
|
[-1.0, 1.0, 0.0, 0.0], // tl
|
||||||
|
];
|
||||||
|
|
||||||
impl GuiController {
|
impl GuiController {
|
||||||
pub fn new<T: ruffle_render_wgpu::target::RenderTarget>(
|
pub fn new(
|
||||||
renderer: &ruffle_render_wgpu::backend::WgpuRenderBackend<T>,
|
|
||||||
window: Rc<Window>,
|
window: Rc<Window>,
|
||||||
event_loop: &EventLoop<RuffleEvent>,
|
event_loop: &EventLoop<RuffleEvent>,
|
||||||
) -> Self {
|
trace_path: Option<&Path>,
|
||||||
|
backend: wgpu::Backends,
|
||||||
|
power_preference: wgpu::PowerPreference,
|
||||||
|
) -> Result<Self> {
|
||||||
|
if wgpu::Backends::SECONDARY.contains(backend) {
|
||||||
|
tracing::warn!(
|
||||||
|
"{} graphics backend support may not be fully supported.",
|
||||||
|
format_list(&get_backend_names(backend), "and")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||||
|
backends: backend,
|
||||||
|
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
||||||
|
});
|
||||||
|
let surface = unsafe { instance.create_surface(window.as_ref()) }?;
|
||||||
|
let (adapter, device, queue) = futures::executor::block_on(request_adapter_and_device(
|
||||||
|
backend,
|
||||||
|
instance,
|
||||||
|
Some(&surface),
|
||||||
|
power_preference,
|
||||||
|
trace_path,
|
||||||
|
))
|
||||||
|
.map_err(|e| anyhow!(e.to_string()))?;
|
||||||
|
let descriptors = Descriptors::new(adapter, device, queue);
|
||||||
let egui_ctx = Context::default();
|
let egui_ctx = Context::default();
|
||||||
let mut egui_winit = egui_winit::State::new(event_loop);
|
let mut egui_winit = egui_winit::State::new(event_loop);
|
||||||
egui_winit.set_pixels_per_point(window.scale_factor() as f32);
|
egui_winit.set_pixels_per_point(window.scale_factor() as f32);
|
||||||
egui_winit
|
egui_winit.set_max_texture_side(descriptors.limits.max_texture_dimension_2d as usize);
|
||||||
.set_max_texture_side(renderer.descriptors().limits.max_texture_dimension_2d as usize);
|
let surface_format = surface
|
||||||
|
.get_capabilities(&descriptors.adapter)
|
||||||
|
.formats
|
||||||
|
.first()
|
||||||
|
.cloned()
|
||||||
|
.expect("At least one format should be supported");
|
||||||
|
|
||||||
let target_format = renderer.target().format();
|
let module = descriptors
|
||||||
let egui_renderer = egui_wgpu::Renderer::new(renderer.device(), target_format, None, 1);
|
.device
|
||||||
|
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("blit.wgsl"))),
|
||||||
|
});
|
||||||
|
let bind_group_layout =
|
||||||
|
descriptors
|
||||||
|
.device
|
||||||
|
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
let sampler = descriptors.device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
mag_filter: wgpu::FilterMode::Linear,
|
||||||
|
min_filter: wgpu::FilterMode::Linear,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let pipeline_layout =
|
||||||
|
descriptors
|
||||||
|
.device
|
||||||
|
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
let pipeline = descriptors
|
||||||
|
.device
|
||||||
|
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: Some(&pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
entry_point: "vs_main",
|
||||||
|
module: &module,
|
||||||
|
buffers: &[wgpu::VertexBufferLayout {
|
||||||
|
array_stride: 4 * 4,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
// 0: vec2 position
|
||||||
|
// 1: vec2 texture coordinates
|
||||||
|
attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
unclipped_depth: false,
|
||||||
|
conservative: false,
|
||||||
|
cull_mode: None,
|
||||||
|
front_face: wgpu::FrontFace::default(),
|
||||||
|
polygon_mode: wgpu::PolygonMode::default(),
|
||||||
|
strip_index_format: None,
|
||||||
|
},
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: wgpu::MultisampleState {
|
||||||
|
alpha_to_coverage_enabled: false,
|
||||||
|
count: 1,
|
||||||
|
mask: !0,
|
||||||
|
},
|
||||||
|
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &module,
|
||||||
|
entry_point: if surface_format.is_srgb() {
|
||||||
|
"fs_main_srgb_framebuffer"
|
||||||
|
} else {
|
||||||
|
"fs_main_linear_framebuffer"
|
||||||
|
},
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: surface_format,
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
let vertices = descriptors
|
||||||
|
.device
|
||||||
|
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: None,
|
||||||
|
contents: bytemuck::cast_slice(&BLIT_VERTICES),
|
||||||
|
usage: wgpu::BufferUsages::VERTEX,
|
||||||
|
});
|
||||||
|
|
||||||
|
let egui_renderer = egui_wgpu::Renderer::new(&descriptors.device, surface_format, None, 1);
|
||||||
let event_loop = event_loop.create_proxy();
|
let event_loop = event_loop.create_proxy();
|
||||||
let gui = RuffleGui::new(event_loop);
|
let gui = RuffleGui::new(event_loop);
|
||||||
Self {
|
Ok(Self {
|
||||||
|
descriptors: Arc::new(descriptors),
|
||||||
egui_ctx,
|
egui_ctx,
|
||||||
egui_winit,
|
egui_winit,
|
||||||
egui_renderer,
|
egui_renderer,
|
||||||
|
@ -40,11 +190,35 @@ impl GuiController {
|
||||||
window,
|
window,
|
||||||
last_update: Instant::now(),
|
last_update: Instant::now(),
|
||||||
repaint_after: Duration::ZERO,
|
repaint_after: Duration::ZERO,
|
||||||
}
|
surface,
|
||||||
|
surface_format,
|
||||||
|
blit_bind_group_layout: bind_group_layout,
|
||||||
|
blit_pipeline: pipeline,
|
||||||
|
blit_sampler: sampler,
|
||||||
|
blit_vertices: vertices,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn descriptors(&self) -> &Arc<Descriptors> {
|
||||||
|
&self.descriptors
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn handle_event(&mut self, event: &winit::event::WindowEvent) -> bool {
|
pub fn handle_event(&mut self, event: &winit::event::WindowEvent) -> bool {
|
||||||
|
if let winit::event::WindowEvent::Resized(size) = &event {
|
||||||
|
self.surface.configure(
|
||||||
|
&self.descriptors.device,
|
||||||
|
&wgpu::SurfaceConfiguration {
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
format: self.surface_format,
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
present_mode: Default::default(),
|
||||||
|
alpha_mode: Default::default(),
|
||||||
|
view_formats: Default::default(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
let response = self.egui_winit.on_event(&self.egui_ctx, event);
|
let response = self.egui_winit.on_event(&self.egui_ctx, event);
|
||||||
if response.repaint {
|
if response.repaint {
|
||||||
self.window.request_redraw();
|
self.window.request_redraw();
|
||||||
|
@ -52,10 +226,13 @@ impl GuiController {
|
||||||
response.consumed
|
response.consumed
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(
|
pub fn render(&mut self, movie: &wgpu::Texture) {
|
||||||
&mut self,
|
let surface_texture = self
|
||||||
render_ctx: ruffle_render_wgpu::backend::RenderCallbackParams,
|
.surface
|
||||||
) -> Vec<wgpu::CommandBuffer> {
|
.get_current_texture()
|
||||||
|
.expect("Surface became unavailable");
|
||||||
|
let movie_view = &movie.create_view(&Default::default());
|
||||||
|
|
||||||
let raw_input = self.egui_winit.take_egui_input(&self.window);
|
let raw_input = self.egui_winit.take_egui_input(&self.window);
|
||||||
let full_output = self.egui_ctx.run(raw_input, |context| {
|
let full_output = self.egui_ctx.run(raw_input, |context| {
|
||||||
self.gui.update(context);
|
self.gui.update(context);
|
||||||
|
@ -77,7 +254,7 @@ impl GuiController {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut encoder =
|
let mut encoder =
|
||||||
render_ctx
|
self.descriptors
|
||||||
.device
|
.device
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
label: Some("egui encoder"),
|
label: Some("egui encoder"),
|
||||||
|
@ -85,28 +262,46 @@ impl GuiController {
|
||||||
|
|
||||||
for (id, image_delta) in &full_output.textures_delta.set {
|
for (id, image_delta) in &full_output.textures_delta.set {
|
||||||
self.egui_renderer.update_texture(
|
self.egui_renderer.update_texture(
|
||||||
render_ctx.device,
|
&self.descriptors.device,
|
||||||
render_ctx.queue,
|
&self.descriptors.queue,
|
||||||
*id,
|
*id,
|
||||||
image_delta,
|
image_delta,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut command_buffers = self.egui_renderer.update_buffers(
|
let mut command_buffers = self.egui_renderer.update_buffers(
|
||||||
render_ctx.device,
|
&self.descriptors.device,
|
||||||
render_ctx.queue,
|
&self.descriptors.queue,
|
||||||
&mut encoder,
|
&mut encoder,
|
||||||
&clipped_primitives,
|
&clipped_primitives,
|
||||||
&screen_descriptor,
|
&screen_descriptor,
|
||||||
);
|
);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
let surface_view = surface_texture.texture.create_view(&Default::default());
|
||||||
|
let blit_bind_group =
|
||||||
|
self.descriptors
|
||||||
|
.device
|
||||||
|
.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: &self.blit_bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(movie_view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&self.blit_sampler),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
view: render_ctx.texture_view,
|
view: &surface_view,
|
||||||
resolve_target: None,
|
resolve_target: None,
|
||||||
ops: wgpu::Operations {
|
ops: wgpu::Operations {
|
||||||
load: wgpu::LoadOp::Load,
|
load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
|
||||||
store: true,
|
store: true,
|
||||||
},
|
},
|
||||||
})],
|
})],
|
||||||
|
@ -114,6 +309,11 @@ impl GuiController {
|
||||||
label: Some("egui_render"),
|
label: Some("egui_render"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
render_pass.set_pipeline(&self.blit_pipeline);
|
||||||
|
render_pass.set_bind_group(0, &blit_bind_group, &[]);
|
||||||
|
render_pass.set_vertex_buffer(0, self.blit_vertices.slice(..));
|
||||||
|
render_pass.draw(0..6, 0..1);
|
||||||
|
|
||||||
self.egui_renderer
|
self.egui_renderer
|
||||||
.render(&mut render_pass, &clipped_primitives, &screen_descriptor);
|
.render(&mut render_pass, &clipped_primitives, &screen_descriptor);
|
||||||
}
|
}
|
||||||
|
@ -123,7 +323,8 @@ impl GuiController {
|
||||||
}
|
}
|
||||||
|
|
||||||
command_buffers.push(encoder.finish());
|
command_buffers.push(encoder.finish());
|
||||||
command_buffers
|
self.descriptors.queue.submit(command_buffers);
|
||||||
|
surface_texture.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ui_visible(&mut self, value: bool) {
|
pub fn set_ui_visible(&mut self, value: bool) {
|
||||||
|
|
|
@ -33,6 +33,7 @@ use ruffle_render::backend::RenderBackend;
|
||||||
use ruffle_render::quality::StageQuality;
|
use ruffle_render::quality::StageQuality;
|
||||||
use ruffle_render_wgpu::backend::WgpuRenderBackend;
|
use ruffle_render_wgpu::backend::WgpuRenderBackend;
|
||||||
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
|
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
|
||||||
|
use ruffle_render_wgpu::target::TextureTarget;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::panic::PanicInfo;
|
use std::panic::PanicInfo;
|
||||||
|
@ -338,18 +339,6 @@ impl App {
|
||||||
opt.open_url_mode,
|
opt.open_url_mode,
|
||||||
);
|
);
|
||||||
|
|
||||||
let viewport_size = window.inner_size();
|
|
||||||
let mut renderer = WgpuRenderBackend::for_window(
|
|
||||||
&window,
|
|
||||||
(viewport_size.width, viewport_size.height),
|
|
||||||
opt.graphics.into(),
|
|
||||||
opt.power.into(),
|
|
||||||
trace_path(&opt),
|
|
||||||
)
|
|
||||||
.map_err(|e| anyhow!(e.to_string()))
|
|
||||||
.context("Couldn't create wgpu rendering backend")?;
|
|
||||||
RENDER_INFO.with(|i| *i.borrow_mut() = Some(renderer.debug_info().to_string()));
|
|
||||||
|
|
||||||
let window = Rc::new(window);
|
let window = Rc::new(window);
|
||||||
|
|
||||||
if cfg!(feature = "software_video") {
|
if cfg!(feature = "software_video") {
|
||||||
|
@ -357,18 +346,26 @@ impl App {
|
||||||
builder.with_video(ruffle_video_software::backend::SoftwareVideoBackend::new());
|
builder.with_video(ruffle_video_software::backend::SoftwareVideoBackend::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
let gui = Arc::new(Mutex::new(GuiController::new(
|
let gui = GuiController::new(
|
||||||
&renderer,
|
|
||||||
window.clone(),
|
window.clone(),
|
||||||
&event_loop,
|
&event_loop,
|
||||||
)));
|
trace_path(&opt),
|
||||||
{
|
opt.graphics.into(),
|
||||||
let gui = gui.clone();
|
opt.power.into(),
|
||||||
renderer.set_render_callback(Some(Box::new(move |render_ctx| {
|
)?;
|
||||||
let mut gui = gui.lock().expect("Gui lock");
|
|
||||||
gui.render(render_ctx)
|
let viewport_size = window.inner_size();
|
||||||
})));
|
let renderer = WgpuRenderBackend::new(
|
||||||
}
|
gui.descriptors().clone(),
|
||||||
|
TextureTarget::new(
|
||||||
|
&gui.descriptors().device,
|
||||||
|
(viewport_size.width, viewport_size.height),
|
||||||
|
)
|
||||||
|
.map_err(|e| anyhow!(e.to_string()))?,
|
||||||
|
)
|
||||||
|
.map_err(|e| anyhow!(e.to_string()))
|
||||||
|
.context("Couldn't create wgpu rendering backend")?;
|
||||||
|
RENDER_INFO.with(|i| *i.borrow_mut() = Some(renderer.debug_info().to_string()));
|
||||||
|
|
||||||
builder = builder
|
builder = builder
|
||||||
.with_navigator(navigator)
|
.with_navigator(navigator)
|
||||||
|
@ -399,7 +396,7 @@ impl App {
|
||||||
event_loop_proxy: event_loop.create_proxy(),
|
event_loop_proxy: event_loop.create_proxy(),
|
||||||
event_loop: Some(event_loop),
|
event_loop: Some(event_loop),
|
||||||
executor,
|
executor,
|
||||||
gui,
|
gui: Arc::new(Mutex::new(gui)),
|
||||||
player,
|
player,
|
||||||
min_window_size,
|
min_window_size,
|
||||||
max_window_size,
|
max_window_size,
|
||||||
|
@ -491,7 +488,16 @@ impl App {
|
||||||
winit::event::Event::RedrawRequested(_) => {
|
winit::event::Event::RedrawRequested(_) => {
|
||||||
// Don't render when minimized to avoid potential swap chain errors in `wgpu`.
|
// Don't render when minimized to avoid potential swap chain errors in `wgpu`.
|
||||||
if !minimized {
|
if !minimized {
|
||||||
self.player.lock().expect("Cannot reenter").render();
|
let mut player = self.player.lock().expect("Cannot reenter");
|
||||||
|
player.render();
|
||||||
|
let renderer = player
|
||||||
|
.renderer_mut()
|
||||||
|
.downcast_mut::<WgpuRenderBackend<TextureTarget>>()
|
||||||
|
.expect("Renderer must be correct type");
|
||||||
|
self.gui
|
||||||
|
.lock()
|
||||||
|
.expect("Gui lock")
|
||||||
|
.render(&renderer.target().texture);
|
||||||
#[cfg(feature = "tracy")]
|
#[cfg(feature = "tracy")]
|
||||||
tracing_tracy::client::Client::running()
|
tracing_tracy::client::Client::running()
|
||||||
.expect("tracy client must be running")
|
.expect("tracy client must be running")
|
||||||
|
|
|
@ -6,7 +6,7 @@ use rayon::prelude::*;
|
||||||
use ruffle_core::limits::ExecutionLimit;
|
use ruffle_core::limits::ExecutionLimit;
|
||||||
use ruffle_core::tag_utils::SwfMovie;
|
use ruffle_core::tag_utils::SwfMovie;
|
||||||
use ruffle_core::PlayerBuilder;
|
use ruffle_core::PlayerBuilder;
|
||||||
use ruffle_render_wgpu::backend::WgpuRenderBackend;
|
use ruffle_render_wgpu::backend::{request_adapter_and_device, WgpuRenderBackend};
|
||||||
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
|
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
|
||||||
use ruffle_render_wgpu::descriptors::Descriptors;
|
use ruffle_render_wgpu::descriptors::Descriptors;
|
||||||
use ruffle_render_wgpu::target::TextureTarget;
|
use ruffle_render_wgpu::target::TextureTarget;
|
||||||
|
@ -405,15 +405,14 @@ fn main() -> Result<()> {
|
||||||
backends: opt.graphics.into(),
|
backends: opt.graphics.into(),
|
||||||
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
||||||
});
|
});
|
||||||
let (adapter, device, queue) =
|
let (adapter, device, queue) = futures::executor::block_on(request_adapter_and_device(
|
||||||
futures::executor::block_on(WgpuRenderBackend::<TextureTarget>::request_device(
|
opt.graphics.into(),
|
||||||
opt.graphics.into(),
|
instance,
|
||||||
instance,
|
None,
|
||||||
None,
|
opt.power.into(),
|
||||||
opt.power.into(),
|
trace_path(&opt),
|
||||||
trace_path(&opt),
|
))
|
||||||
))
|
.map_err(|e| anyhow!(e.to_string()))?;
|
||||||
.map_err(|e| anyhow!(e.to_string()))?;
|
|
||||||
|
|
||||||
let descriptors = Arc::new(Descriptors::new(adapter, device, queue));
|
let descriptors = Arc::new(Descriptors::new(adapter, device, queue));
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ impl WgpuRenderBackend<SwapChainTarget> {
|
||||||
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
||||||
});
|
});
|
||||||
let surface = instance.create_surface_from_canvas(canvas)?;
|
let surface = instance.create_surface_from_canvas(canvas)?;
|
||||||
let (adapter, device, queue) = Self::request_device(
|
let (adapter, device, queue) = request_adapter_and_device(
|
||||||
wgpu::Backends::BROWSER_WEBGPU | wgpu::Backends::GL,
|
wgpu::Backends::BROWSER_WEBGPU | wgpu::Backends::GL,
|
||||||
instance,
|
instance,
|
||||||
Some(&surface),
|
Some(&surface),
|
||||||
|
@ -95,7 +95,7 @@ impl WgpuRenderBackend<SwapChainTarget> {
|
||||||
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
||||||
});
|
});
|
||||||
let surface = unsafe { instance.create_surface(window) }?;
|
let surface = unsafe { instance.create_surface(window) }?;
|
||||||
let (adapter, device, queue) = futures::executor::block_on(Self::request_device(
|
let (adapter, device, queue) = futures::executor::block_on(request_adapter_and_device(
|
||||||
backend,
|
backend,
|
||||||
instance,
|
instance,
|
||||||
Some(&surface),
|
Some(&surface),
|
||||||
|
@ -126,7 +126,7 @@ impl WgpuRenderBackend<crate::target::TextureTarget> {
|
||||||
backends: backend,
|
backends: backend,
|
||||||
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
dx12_shader_compiler: wgpu::Dx12Compiler::default(),
|
||||||
});
|
});
|
||||||
let (adapter, device, queue) = futures::executor::block_on(Self::request_device(
|
let (adapter, device, queue) = futures::executor::block_on(request_adapter_and_device(
|
||||||
backend,
|
backend,
|
||||||
instance,
|
instance,
|
||||||
None,
|
None,
|
||||||
|
@ -210,33 +210,6 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn request_device(
|
|
||||||
backend: wgpu::Backends,
|
|
||||||
instance: wgpu::Instance,
|
|
||||||
surface: Option<&wgpu::Surface>,
|
|
||||||
power_preference: wgpu::PowerPreference,
|
|
||||||
trace_path: Option<&Path>,
|
|
||||||
) -> Result<(wgpu::Adapter, wgpu::Device, wgpu::Queue), Error> {
|
|
||||||
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
|
|
||||||
power_preference,
|
|
||||||
compatible_surface: surface,
|
|
||||||
force_fallback_adapter: false,
|
|
||||||
}).await
|
|
||||||
.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 if cfg!(any(windows, target_os = "macos")) {
|
|
||||||
format!("Ruffle does not support OpenGL on {}.", if cfg!(windows) { "Windows" } else { "macOS" })
|
|
||||||
} else {
|
|
||||||
format!("Ruffle requires hardware acceleration, but no compatible graphics device was found supporting {}", format_list(&names, "or"))
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let (device, queue) = request_device(&adapter, trace_path).await?;
|
|
||||||
Ok((adapter, device, queue))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_shape_internal(
|
fn register_shape_internal(
|
||||||
&mut self,
|
&mut self,
|
||||||
shape: DistilledShape,
|
shape: DistilledShape,
|
||||||
|
@ -800,6 +773,33 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn request_adapter_and_device(
|
||||||
|
backend: wgpu::Backends,
|
||||||
|
instance: wgpu::Instance,
|
||||||
|
surface: Option<&wgpu::Surface>,
|
||||||
|
power_preference: wgpu::PowerPreference,
|
||||||
|
trace_path: Option<&Path>,
|
||||||
|
) -> Result<(wgpu::Adapter, wgpu::Device, wgpu::Queue), Error> {
|
||||||
|
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
|
power_preference,
|
||||||
|
compatible_surface: surface,
|
||||||
|
force_fallback_adapter: false,
|
||||||
|
}).await
|
||||||
|
.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 if cfg!(any(windows, target_os = "macos")) {
|
||||||
|
format!("Ruffle does not support OpenGL on {}.", if cfg!(windows) { "Windows" } else { "macOS" })
|
||||||
|
} else {
|
||||||
|
format!("Ruffle requires hardware acceleration, but no compatible graphics device was found supporting {}", format_list(&names, "or"))
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let (device, queue) = request_device(&adapter, trace_path).await?;
|
||||||
|
Ok((adapter, device, queue))
|
||||||
|
}
|
||||||
|
|
||||||
// We try to request the highest limits we can get away with
|
// We try to request the highest limits we can get away with
|
||||||
async fn request_device(
|
async fn request_device(
|
||||||
adapter: &wgpu::Adapter,
|
adapter: &wgpu::Adapter,
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub use wgpu;
|
||||||
type Error = Box<dyn std::error::Error>;
|
type Error = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
mod bitmaps;
|
mod bitmaps;
|
||||||
mod context3d;
|
mod context3d;
|
||||||
|
|
|
@ -214,7 +214,9 @@ impl TextureTarget {
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
format,
|
format,
|
||||||
view_formats: &[format],
|
view_formats: &[format],
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||||
|
| wgpu::TextureUsages::COPY_SRC
|
||||||
|
| wgpu::TextureUsages::TEXTURE_BINDING,
|
||||||
});
|
});
|
||||||
let buffer_label = create_debug_label!("Render target buffer");
|
let buffer_label = create_debug_label!("Render target buffer");
|
||||||
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use ruffle_render_wgpu::backend::WgpuRenderBackend;
|
use ruffle_render_wgpu::backend::request_adapter_and_device;
|
||||||
use ruffle_render_wgpu::descriptors::Descriptors;
|
use ruffle_render_wgpu::descriptors::Descriptors;
|
||||||
use ruffle_render_wgpu::target::TextureTarget;
|
|
||||||
use ruffle_render_wgpu::wgpu;
|
use ruffle_render_wgpu::wgpu;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@ use std::sync::Arc;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn create_wgpu_device() -> Option<(wgpu::Adapter, wgpu::Device, wgpu::Queue)> {
|
fn create_wgpu_device() -> Option<(wgpu::Adapter, wgpu::Device, wgpu::Queue)> {
|
||||||
futures::executor::block_on(WgpuRenderBackend::<TextureTarget>::request_device(
|
futures::executor::block_on(request_adapter_and_device(
|
||||||
wgpu::Backends::all(),
|
wgpu::Backends::all(),
|
||||||
wgpu::Instance::new(Default::default()),
|
wgpu::Instance::new(Default::default()),
|
||||||
None,
|
None,
|
||||||
|
|
Loading…
Reference in New Issue