desktop: Turn MovieView into a custom render target

This commit is contained in:
Nathan Adams 2023-05-21 02:34:09 +02:00
parent 67fed1159c
commit a3d3230baf
4 changed files with 127 additions and 56 deletions

View File

@ -7,6 +7,7 @@ use std::time::{Duration, Instant};
use winit::event_loop::EventLoopProxy;
pub use controller::GuiController;
pub use movie::MovieView;
/// The main controller for the Ruffle GUI.
pub struct RuffleGui {

View File

@ -1,5 +1,5 @@
use crate::custom_event::RuffleEvent;
use crate::gui::movie::MovieView;
use crate::gui::movie::{MovieView, MovieViewRenderer};
use crate::gui::RuffleGui;
use anyhow::anyhow;
use egui::Context;
@ -25,7 +25,7 @@ pub struct GuiController {
repaint_after: Duration,
surface: wgpu::Surface,
surface_format: wgpu::TextureFormat,
movie_view: MovieView,
movie_view_renderer: Arc<MovieViewRenderer>,
}
impl GuiController {
@ -67,7 +67,8 @@ impl GuiController {
.cloned()
.expect("At least one format should be supported");
let game_view = MovieView::new(&descriptors.device, surface_format);
let movie_view_renderer =
Arc::new(MovieViewRenderer::new(&descriptors.device, surface_format));
let egui_renderer = egui_wgpu::Renderer::new(&descriptors.device, surface_format, None, 1);
let event_loop = event_loop.create_proxy();
let gui = RuffleGui::new(event_loop);
@ -82,7 +83,7 @@ impl GuiController {
repaint_after: Duration::ZERO,
surface,
surface_format,
movie_view: game_view,
movie_view_renderer,
})
}
@ -113,7 +114,17 @@ impl GuiController {
response.consumed
}
pub fn render(&mut self, movie: &wgpu::Texture) {
pub fn create_movie_view(&self) -> MovieView {
let size = self.window.inner_size();
MovieView::new(
self.movie_view_renderer.clone(),
&self.descriptors.device,
size.width,
size.height,
)
}
pub fn render(&mut self, movie: &MovieView) {
let surface_texture = self
.surface
.get_current_texture()
@ -166,17 +177,12 @@ impl GuiController {
{
let surface_view = surface_texture.texture.create_view(&Default::default());
// First draw the movie - this also clears the surface
self.movie_view
.render(&self.descriptors.device, &mut encoder, movie, &surface_view);
// Then any UI
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &surface_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: true,
},
})],
@ -184,6 +190,8 @@ impl GuiController {
label: Some("egui_render"),
});
movie.render(&self.movie_view_renderer, &mut render_pass);
self.egui_renderer
.render(&mut render_pass, &clipped_primitives, &screen_descriptor);
}

View File

@ -1,7 +1,10 @@
use ruffle_render_wgpu::target::{RenderTarget, RenderTargetFrame};
use std::borrow::Cow;
use std::sync::Arc;
use wgpu::util::DeviceExt;
pub struct MovieView {
#[derive(Debug)]
pub struct MovieViewRenderer {
bind_group_layout: wgpu::BindGroupLayout,
pipeline: wgpu::RenderPipeline,
sampler: wgpu::Sampler,
@ -18,7 +21,7 @@ const BLIT_VERTICES: [[f32; 4]; 6] = [
[-1.0, 1.0, 0.0, 0.0], // tl
];
impl MovieView {
impl MovieViewRenderer {
pub fn new(device: &wgpu::Device, surface_format: wgpu::TextureFormat) -> Self {
let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
@ -113,45 +116,115 @@ impl MovieView {
vertices,
}
}
}
pub fn render(
&self,
#[derive(Debug)]
pub struct MovieView {
renderer: Arc<MovieViewRenderer>,
texture: wgpu::Texture,
bind_group: wgpu::BindGroup,
}
impl MovieView {
pub fn new(
renderer: Arc<MovieViewRenderer>,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
movie: &wgpu::Texture,
surface_view: &wgpu::TextureView,
) {
let movie_view = movie.create_view(&Default::default());
let blit_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
width: u32,
height: u32,
) -> Self {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
layout: &self.bind_group_layout,
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let view = texture.create_view(&Default::default());
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &renderer.bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&movie_view),
resource: wgpu::BindingResource::TextureView(&view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&self.sampler),
resource: wgpu::BindingResource::Sampler(&renderer.sampler),
},
],
});
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: surface_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: true,
},
})],
depth_stencil_attachment: None,
label: Some("egui_render"),
});
Self {
renderer,
texture,
bind_group,
}
}
render_pass.set_pipeline(&self.pipeline);
render_pass.set_bind_group(0, &blit_bind_group, &[]);
render_pass.set_vertex_buffer(0, self.vertices.slice(..));
pub fn render<'pass, 'global: 'pass>(
&'pass self,
renderer: &'global MovieViewRenderer,
render_pass: &mut wgpu::RenderPass<'pass>,
) {
render_pass.set_pipeline(&renderer.pipeline);
render_pass.set_bind_group(0, &self.bind_group, &[]);
render_pass.set_vertex_buffer(0, renderer.vertices.slice(..));
render_pass.draw(0..6, 0..1);
}
}
impl RenderTarget for MovieView {
type Frame = MovieViewFrame;
fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
*self = MovieView::new(self.renderer.clone(), device, width, height);
}
fn format(&self) -> wgpu::TextureFormat {
self.texture.format()
}
fn width(&self) -> u32 {
self.texture.width()
}
fn height(&self) -> u32 {
self.texture.height()
}
fn get_next_texture(&mut self) -> Result<Self::Frame, wgpu::SurfaceError> {
Ok(MovieViewFrame(
self.texture.create_view(&Default::default()),
))
}
fn submit<I: IntoIterator<Item = wgpu::CommandBuffer>>(
&self,
_device: &wgpu::Device,
queue: &wgpu::Queue,
command_buffers: I,
_frame: Self::Frame,
) -> wgpu::SubmissionIndex {
queue.submit(command_buffers)
}
}
#[derive(Debug)]
pub struct MovieViewFrame(wgpu::TextureView);
impl RenderTargetFrame for MovieViewFrame {
fn into_view(self) -> wgpu::TextureView {
self.0
}
fn view(&self) -> &wgpu::TextureView {
&self.0
}
}

View File

@ -17,6 +17,7 @@ mod ui;
use crate::custom_event::RuffleEvent;
use crate::executor::GlutinAsyncExecutor;
use crate::gui::MovieView;
use anyhow::{anyhow, Context, Error};
use clap::Parser;
use gui::GuiController;
@ -33,7 +34,6 @@ use ruffle_render::backend::RenderBackend;
use ruffle_render::quality::StageQuality;
use ruffle_render_wgpu::backend::WgpuRenderBackend;
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
use ruffle_render_wgpu::target::TextureTarget;
use std::cell::RefCell;
use std::io::Read;
use std::panic::PanicInfo;
@ -354,17 +354,9 @@ impl App {
opt.power.into(),
)?;
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")?;
let renderer = WgpuRenderBackend::new(gui.descriptors().clone(), gui.create_movie_view())
.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
@ -492,12 +484,9 @@ impl App {
player.render();
let renderer = player
.renderer_mut()
.downcast_mut::<WgpuRenderBackend<TextureTarget>>()
.downcast_mut::<WgpuRenderBackend<MovieView>>()
.expect("Renderer must be correct type");
self.gui
.lock()
.expect("Gui lock")
.render(&renderer.target().texture);
self.gui.lock().expect("Gui lock").render(renderer.target());
#[cfg(feature = "tracy")]
tracing_tracy::client::Client::running()
.expect("tracy client must be running")