2022-09-07 20:27:57 +00:00
|
|
|
use crate::mesh::{Draw, Mesh};
|
2022-09-07 14:54:05 +00:00
|
|
|
use crate::surface::Surface;
|
2022-09-03 00:18:57 +00:00
|
|
|
use crate::target::RenderTargetFrame;
|
|
|
|
use crate::target::TextureTarget;
|
2022-09-04 19:19:16 +00:00
|
|
|
use crate::uniform_buffer::BufferStorage;
|
2022-09-03 00:18:57 +00:00
|
|
|
use crate::{
|
2022-09-07 20:27:57 +00:00
|
|
|
format_list, get_backend_names, BufferDimensions, Descriptors, Error, Globals, RegistryData,
|
2022-09-08 19:43:11 +00:00
|
|
|
RenderTarget, SwapChainTarget, Texture, TextureOffscreen, Transforms,
|
2022-09-03 00:18:57 +00:00
|
|
|
};
|
|
|
|
use fnv::FnvHashMap;
|
2022-10-07 10:17:41 +00:00
|
|
|
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
2022-09-03 00:18:57 +00:00
|
|
|
use ruffle_render::backend::{RenderBackend, ShapeHandle, ViewportDimensions};
|
|
|
|
use ruffle_render::bitmap::{Bitmap, BitmapHandle, BitmapSource};
|
2022-09-04 19:19:16 +00:00
|
|
|
use ruffle_render::commands::CommandList;
|
2022-09-03 00:18:57 +00:00
|
|
|
use ruffle_render::error::Error as BitmapError;
|
|
|
|
use ruffle_render::shape_utils::DistilledShape;
|
2022-09-07 20:27:57 +00:00
|
|
|
use ruffle_render::tessellator::ShapeTessellator;
|
2022-09-03 00:18:57 +00:00
|
|
|
use std::num::NonZeroU32;
|
|
|
|
use std::path::Path;
|
|
|
|
use std::sync::Arc;
|
2022-09-04 19:19:16 +00:00
|
|
|
use swf::Color;
|
2022-09-03 00:18:57 +00:00
|
|
|
|
|
|
|
const DEFAULT_SAMPLE_COUNT: u32 = 4;
|
|
|
|
|
|
|
|
pub struct WgpuRenderBackend<T: RenderTarget> {
|
|
|
|
descriptors: Arc<Descriptors>,
|
|
|
|
globals: Globals,
|
2022-09-04 19:19:16 +00:00
|
|
|
uniform_buffers_storage: BufferStorage<Transforms>,
|
2022-09-03 00:18:57 +00:00
|
|
|
target: T,
|
2022-09-07 14:54:05 +00:00
|
|
|
surface: Surface,
|
2022-09-03 00:18:57 +00:00
|
|
|
meshes: Vec<Mesh>,
|
|
|
|
shape_tessellator: ShapeTessellator,
|
|
|
|
bitmap_registry: FnvHashMap<BitmapHandle, RegistryData>,
|
|
|
|
next_bitmap_handle: BitmapHandle,
|
|
|
|
// This is currently unused - we just store it to report in
|
|
|
|
// `get_viewport_dimensions`
|
|
|
|
viewport_scale_factor: f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WgpuRenderBackend<SwapChainTarget> {
|
|
|
|
#[cfg(target_family = "wasm")]
|
|
|
|
pub async fn for_canvas(canvas: &web_sys::HtmlCanvasElement) -> Result<Self, Error> {
|
|
|
|
let instance = wgpu::Instance::new(wgpu::Backends::BROWSER_WEBGPU | wgpu::Backends::GL);
|
|
|
|
let surface = instance.create_surface_from_canvas(canvas);
|
|
|
|
let descriptors = Self::build_descriptors(
|
|
|
|
wgpu::Backends::BROWSER_WEBGPU | wgpu::Backends::GL,
|
|
|
|
instance,
|
|
|
|
Some(&surface),
|
|
|
|
wgpu::PowerPreference::HighPerformance,
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
.await?;
|
2022-10-07 10:17:41 +00:00
|
|
|
let target =
|
|
|
|
SwapChainTarget::new(surface, &descriptors.adapter, (1, 1), &descriptors.device);
|
2022-09-03 00:18:57 +00:00
|
|
|
Self::new(Arc::new(descriptors), target)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_family = "wasm"))]
|
2022-10-07 10:17:41 +00:00
|
|
|
pub fn for_window<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
2022-09-03 00:18:57 +00:00
|
|
|
window: &W,
|
|
|
|
size: (u32, u32),
|
|
|
|
backend: wgpu::Backends,
|
|
|
|
power_preference: wgpu::PowerPreference,
|
|
|
|
trace_path: Option<&Path>,
|
|
|
|
) -> Result<Self, Error> {
|
|
|
|
if wgpu::Backends::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 surface = unsafe { instance.create_surface(window) };
|
|
|
|
let descriptors = futures::executor::block_on(Self::build_descriptors(
|
|
|
|
backend,
|
|
|
|
instance,
|
|
|
|
Some(&surface),
|
|
|
|
power_preference,
|
|
|
|
trace_path,
|
|
|
|
))?;
|
2022-10-07 10:17:41 +00:00
|
|
|
let target = SwapChainTarget::new(surface, &descriptors.adapter, size, &descriptors.device);
|
2022-09-03 00:18:57 +00:00
|
|
|
Self::new(Arc::new(descriptors), target)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_family = "wasm"))]
|
2022-09-07 01:30:52 +00:00
|
|
|
impl WgpuRenderBackend<crate::target::TextureTarget> {
|
2022-09-03 00:18:57 +00:00
|
|
|
pub fn for_offscreen(
|
|
|
|
size: (u32, u32),
|
|
|
|
backend: wgpu::Backends,
|
|
|
|
power_preference: wgpu::PowerPreference,
|
|
|
|
trace_path: Option<&Path>,
|
|
|
|
) -> Result<Self, Error> {
|
|
|
|
if wgpu::Backends::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 = futures::executor::block_on(Self::build_descriptors(
|
|
|
|
backend,
|
|
|
|
instance,
|
|
|
|
None,
|
|
|
|
power_preference,
|
|
|
|
trace_path,
|
|
|
|
))?;
|
2022-09-07 01:30:52 +00:00
|
|
|
let target = crate::target::TextureTarget::new(&descriptors.device, size)?;
|
2022-09-03 00:18:57 +00:00
|
|
|
Self::new(Arc::new(descriptors), target)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn capture_frame(&self, premultiplied_alpha: bool) -> Option<image::RgbaImage> {
|
|
|
|
self.target
|
|
|
|
.capture(&self.descriptors.device, premultiplied_alpha)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|
|
|
pub fn new(descriptors: Arc<Descriptors>, target: T) -> Result<Self, Error> {
|
|
|
|
if target.width() > descriptors.limits.max_texture_dimension_2d
|
|
|
|
|| target.height() > descriptors.limits.max_texture_dimension_2d
|
|
|
|
{
|
|
|
|
return Err(format!(
|
|
|
|
"Render target texture cannot be larger than {}px on either dimension (requested {} x {})",
|
|
|
|
descriptors.limits.max_texture_dimension_2d,
|
|
|
|
target.width(),
|
|
|
|
target.height()
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
2022-10-07 10:17:41 +00:00
|
|
|
// TODO: Allow the sample count to be set from command line/settings file.
|
2022-09-07 14:54:05 +00:00
|
|
|
let surface = Surface::new(
|
|
|
|
&descriptors,
|
2022-09-08 08:05:34 +00:00
|
|
|
DEFAULT_SAMPLE_COUNT,
|
2022-09-07 14:54:05 +00:00
|
|
|
target.width(),
|
|
|
|
target.height(),
|
2022-09-08 08:05:34 +00:00
|
|
|
target.format(),
|
2022-09-07 14:54:05 +00:00
|
|
|
);
|
2022-09-03 00:18:57 +00:00
|
|
|
|
2022-09-07 02:45:33 +00:00
|
|
|
let mut globals = Globals::new(&descriptors.device, &descriptors.bind_layouts.globals);
|
2022-09-03 00:18:57 +00:00
|
|
|
globals.set_resolution(target.width(), target.height());
|
|
|
|
|
2022-09-04 19:19:16 +00:00
|
|
|
let uniform_buffers_storage =
|
2022-09-07 04:32:15 +00:00
|
|
|
BufferStorage::from_alignment(descriptors.limits.min_uniform_buffer_offset_alignment);
|
2022-09-03 00:18:57 +00:00
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
descriptors,
|
|
|
|
globals,
|
2022-09-04 19:19:16 +00:00
|
|
|
uniform_buffers_storage,
|
2022-09-03 00:18:57 +00:00
|
|
|
target,
|
2022-09-07 14:54:05 +00:00
|
|
|
surface,
|
2022-09-03 00:18:57 +00:00
|
|
|
meshes: Vec::new(),
|
|
|
|
shape_tessellator: ShapeTessellator::new(),
|
|
|
|
|
|
|
|
bitmap_registry: Default::default(),
|
|
|
|
next_bitmap_handle: BitmapHandle(0),
|
|
|
|
viewport_scale_factor: 1.0,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn build_descriptors(
|
|
|
|
backend: wgpu::Backends,
|
|
|
|
instance: wgpu::Instance,
|
|
|
|
surface: Option<&wgpu::Surface>,
|
|
|
|
power_preference: wgpu::PowerPreference,
|
|
|
|
trace_path: Option<&Path>,
|
|
|
|
) -> Result<Descriptors, 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?;
|
2022-10-07 10:17:41 +00:00
|
|
|
|
|
|
|
Ok(Descriptors::new(adapter, device, queue))
|
2022-09-03 00:18:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn register_shape_internal(
|
|
|
|
&mut self,
|
|
|
|
shape: DistilledShape,
|
|
|
|
bitmap_source: &dyn BitmapSource,
|
|
|
|
) -> Mesh {
|
2022-09-07 20:27:57 +00:00
|
|
|
let shape_id = shape.id;
|
2022-09-03 00:18:57 +00:00
|
|
|
let lyon_mesh = self
|
|
|
|
.shape_tessellator
|
|
|
|
.tessellate_shape(shape, bitmap_source);
|
|
|
|
|
|
|
|
let mut draws = Vec::with_capacity(lyon_mesh.len());
|
|
|
|
for draw in lyon_mesh {
|
|
|
|
let draw_id = draws.len();
|
2022-09-07 20:27:57 +00:00
|
|
|
draws.push(Draw::new(
|
|
|
|
&self.descriptors,
|
|
|
|
draw,
|
|
|
|
shape_id,
|
|
|
|
draw_id,
|
|
|
|
&self.bitmap_registry,
|
|
|
|
));
|
2022-09-03 00:18:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Mesh { draws }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn descriptors(&self) -> &Arc<Descriptors> {
|
|
|
|
&self.descriptors
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn target(&self) -> &T {
|
|
|
|
&self.target
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn device(&self) -> &wgpu::Device {
|
|
|
|
&self.descriptors.device
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|
|
|
fn set_viewport_dimensions(&mut self, dimensions: ViewportDimensions) {
|
|
|
|
// Avoid panics from creating 0-sized framebuffers.
|
|
|
|
// TODO: find a way to bubble an error when the size is too large
|
|
|
|
let width = std::cmp::max(
|
|
|
|
std::cmp::min(
|
|
|
|
dimensions.width,
|
|
|
|
self.descriptors.limits.max_texture_dimension_2d,
|
|
|
|
),
|
|
|
|
1,
|
|
|
|
);
|
|
|
|
let height = std::cmp::max(
|
|
|
|
std::cmp::min(
|
|
|
|
dimensions.height,
|
|
|
|
self.descriptors.limits.max_texture_dimension_2d,
|
|
|
|
),
|
|
|
|
1,
|
|
|
|
);
|
|
|
|
self.target.resize(&self.descriptors.device, width, height);
|
2022-09-08 08:05:34 +00:00
|
|
|
self.surface = Surface::new(&self.descriptors, 4, width, height, self.target.format());
|
2022-09-03 00:18:57 +00:00
|
|
|
|
|
|
|
self.globals.set_resolution(width, height);
|
|
|
|
self.viewport_scale_factor = dimensions.scale_factor;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn viewport_dimensions(&self) -> ViewportDimensions {
|
|
|
|
ViewportDimensions {
|
|
|
|
width: self.target.width(),
|
|
|
|
height: self.target.height(),
|
|
|
|
scale_factor: self.viewport_scale_factor,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn register_shape(
|
|
|
|
&mut self,
|
|
|
|
shape: DistilledShape,
|
|
|
|
bitmap_source: &dyn BitmapSource,
|
|
|
|
) -> ShapeHandle {
|
|
|
|
let handle = ShapeHandle(self.meshes.len());
|
|
|
|
let mesh = self.register_shape_internal(shape, bitmap_source);
|
|
|
|
self.meshes.push(mesh);
|
|
|
|
handle
|
|
|
|
}
|
|
|
|
|
|
|
|
fn replace_shape(
|
|
|
|
&mut self,
|
|
|
|
shape: DistilledShape,
|
|
|
|
bitmap_source: &dyn BitmapSource,
|
|
|
|
handle: ShapeHandle,
|
|
|
|
) {
|
|
|
|
let mesh = self.register_shape_internal(shape, bitmap_source);
|
|
|
|
self.meshes[handle.0] = mesh;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn register_glyph_shape(&mut self, glyph: &swf::Glyph) -> ShapeHandle {
|
|
|
|
let shape = ruffle_render::shape_utils::swf_glyph_to_shape(glyph);
|
|
|
|
let handle = ShapeHandle(self.meshes.len());
|
|
|
|
let mesh = self.register_shape_internal(
|
|
|
|
(&shape).into(),
|
|
|
|
&ruffle_render::backend::null::NullBitmapSource,
|
|
|
|
);
|
|
|
|
self.meshes.push(mesh);
|
|
|
|
handle
|
|
|
|
}
|
|
|
|
|
2022-09-03 17:22:57 +00:00
|
|
|
fn submit_frame(&mut self, clear: Color, commands: CommandList) {
|
2022-09-04 19:19:16 +00:00
|
|
|
let frame_output = match self.target.get_next_texture() {
|
|
|
|
Ok(frame) => frame,
|
|
|
|
Err(e) => {
|
|
|
|
log::warn!("Couldn't begin new render frame: {}", e);
|
|
|
|
// Attempt to recreate the swap chain in this case.
|
|
|
|
self.target.resize(
|
|
|
|
&self.descriptors.device,
|
|
|
|
self.target.width(),
|
|
|
|
self.target.height(),
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-09-07 19:53:21 +00:00
|
|
|
let command_buffers = self.surface.draw_commands(
|
|
|
|
frame_output.view(),
|
2022-09-28 15:47:35 +00:00
|
|
|
Some(wgpu::Color {
|
2022-09-07 19:53:21 +00:00
|
|
|
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,
|
2022-09-28 15:47:35 +00:00
|
|
|
}),
|
2022-09-04 19:19:16 +00:00
|
|
|
&self.descriptors,
|
2022-09-07 19:53:21 +00:00
|
|
|
&mut self.globals,
|
|
|
|
&mut self.uniform_buffers_storage,
|
2022-09-07 09:33:12 +00:00
|
|
|
&self.meshes,
|
|
|
|
&self.bitmap_registry,
|
2022-09-07 19:53:21 +00:00
|
|
|
commands,
|
2022-09-07 14:54:05 +00:00
|
|
|
);
|
2022-09-04 19:19:16 +00:00
|
|
|
|
|
|
|
self.target.submit(
|
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.queue,
|
|
|
|
command_buffers,
|
|
|
|
frame_output,
|
|
|
|
);
|
2022-09-03 17:22:57 +00:00
|
|
|
}
|
2022-09-03 00:18:57 +00:00
|
|
|
|
2022-09-03 17:22:57 +00:00
|
|
|
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> Option<Bitmap> {
|
|
|
|
self.bitmap_registry.get(&bitmap).map(|e| e.bitmap.clone())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn register_bitmap(&mut self, bitmap: Bitmap) -> Result<BitmapHandle, BitmapError> {
|
|
|
|
if bitmap.width() > self.descriptors.limits.max_texture_dimension_2d
|
|
|
|
|| bitmap.height() > self.descriptors.limits.max_texture_dimension_2d
|
|
|
|
{
|
|
|
|
return Err(BitmapError::TooLarge);
|
|
|
|
}
|
|
|
|
|
|
|
|
let bitmap = bitmap.to_rgba();
|
|
|
|
let extent = wgpu::Extent3d {
|
|
|
|
width: bitmap.width(),
|
|
|
|
height: bitmap.height(),
|
|
|
|
depth_or_array_layers: 1,
|
2022-09-03 00:18:57 +00:00
|
|
|
};
|
|
|
|
|
2022-09-03 17:22:57 +00:00
|
|
|
let texture_label = create_debug_label!("Bitmap");
|
|
|
|
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::TextureUsages::TEXTURE_BINDING
|
|
|
|
| wgpu::TextureUsages::COPY_DST
|
|
|
|
| wgpu::TextureUsages::RENDER_ATTACHMENT
|
|
|
|
| wgpu::TextureUsages::COPY_SRC,
|
|
|
|
});
|
2022-09-03 00:18:57 +00:00
|
|
|
|
2022-09-03 17:22:57 +00:00
|
|
|
self.descriptors.queue.write_texture(
|
|
|
|
wgpu::ImageCopyTexture {
|
|
|
|
texture: &texture,
|
|
|
|
mip_level: 0,
|
|
|
|
origin: Default::default(),
|
|
|
|
aspect: wgpu::TextureAspect::All,
|
|
|
|
},
|
|
|
|
bitmap.data(),
|
|
|
|
wgpu::ImageDataLayout {
|
|
|
|
offset: 0,
|
|
|
|
bytes_per_row: NonZeroU32::new(4 * extent.width),
|
|
|
|
rows_per_image: None,
|
|
|
|
},
|
|
|
|
extent,
|
|
|
|
);
|
2022-09-03 00:18:57 +00:00
|
|
|
|
2022-09-03 17:22:57 +00:00
|
|
|
let handle = self.next_bitmap_handle;
|
|
|
|
self.next_bitmap_handle = BitmapHandle(self.next_bitmap_handle.0 + 1);
|
2022-09-03 00:18:57 +00:00
|
|
|
|
2022-09-03 17:22:57 +00:00
|
|
|
if self
|
|
|
|
.bitmap_registry
|
|
|
|
.insert(
|
|
|
|
handle,
|
|
|
|
RegistryData {
|
|
|
|
bitmap,
|
|
|
|
texture_wrapper: Texture {
|
|
|
|
texture,
|
2022-09-08 19:43:11 +00:00
|
|
|
bind_linear: Default::default(),
|
|
|
|
bind_nearest: Default::default(),
|
2022-09-03 17:22:57 +00:00
|
|
|
texture_offscreen: None,
|
|
|
|
},
|
2022-09-03 00:18:57 +00:00
|
|
|
},
|
2022-09-03 17:22:57 +00:00
|
|
|
)
|
|
|
|
.is_some()
|
|
|
|
{
|
2022-10-26 23:46:09 +00:00
|
|
|
panic!("Overwrote existing bitmap {handle:?}");
|
2022-09-03 17:22:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(handle)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unregister_bitmap(&mut self, handle: BitmapHandle) {
|
|
|
|
self.bitmap_registry.remove(&handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_texture(
|
|
|
|
&mut self,
|
|
|
|
handle: BitmapHandle,
|
|
|
|
width: u32,
|
|
|
|
height: u32,
|
|
|
|
rgba: Vec<u8>,
|
2022-09-11 07:57:17 +00:00
|
|
|
) -> Result<(), BitmapError> {
|
2022-09-03 17:22:57 +00:00
|
|
|
let texture = if let Some(entry) = self.bitmap_registry.get(&handle) {
|
|
|
|
&entry.texture_wrapper.texture
|
|
|
|
} else {
|
2022-09-11 07:57:17 +00:00
|
|
|
return Err(BitmapError::UnknownHandle(handle));
|
2022-09-03 17:22:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let extent = wgpu::Extent3d {
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
depth_or_array_layers: 1,
|
|
|
|
};
|
2022-09-03 00:18:57 +00:00
|
|
|
|
2022-09-03 17:22:57 +00:00
|
|
|
self.descriptors.queue.write_texture(
|
|
|
|
wgpu::ImageCopyTexture {
|
|
|
|
texture,
|
|
|
|
mip_level: 0,
|
|
|
|
origin: Default::default(),
|
|
|
|
aspect: wgpu::TextureAspect::All,
|
2022-09-03 00:18:57 +00:00
|
|
|
},
|
2022-09-03 17:22:57 +00:00
|
|
|
&rgba,
|
|
|
|
wgpu::ImageDataLayout {
|
|
|
|
offset: 0,
|
|
|
|
bytes_per_row: NonZeroU32::new(4 * extent.width),
|
|
|
|
rows_per_image: None,
|
|
|
|
},
|
|
|
|
extent,
|
|
|
|
);
|
|
|
|
|
2022-09-11 07:57:17 +00:00
|
|
|
Ok(())
|
2022-09-03 00:18:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn render_offscreen(
|
|
|
|
&mut self,
|
|
|
|
handle: BitmapHandle,
|
|
|
|
width: u32,
|
|
|
|
height: u32,
|
2022-09-03 17:22:57 +00:00
|
|
|
commands: CommandList,
|
2022-09-03 00:18:57 +00:00
|
|
|
) -> Result<Bitmap, ruffle_render::error::Error> {
|
2022-09-07 01:18:31 +00:00
|
|
|
// We need ownership of `Texture` to access the non-`Clone`
|
|
|
|
// `wgpu` fields. At the end of this method, we re-insert
|
|
|
|
// `texture` into the map.
|
2022-09-03 00:18:57 +00:00
|
|
|
//
|
2022-09-07 01:18:31 +00:00
|
|
|
// This means that the target texture will be inaccessible
|
|
|
|
// while the callback `f` is a problem. This would only be
|
|
|
|
// an issue if a caller tried to render the target texture
|
|
|
|
// to itself, which probably isn't supported by Flash. If it
|
|
|
|
// is, then we could change `TextureTarget` to use an `Rc<wgpu::Texture>`
|
|
|
|
let mut texture = self.bitmap_registry.remove(&handle).unwrap();
|
|
|
|
|
|
|
|
let extent = wgpu::Extent3d {
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
depth_or_array_layers: 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
// We will (presumably) never render to the majority of textures, so
|
|
|
|
// we lazily create the buffer and depth texture.
|
|
|
|
// Once created, we never destroy this data, under the assumption
|
|
|
|
// that the SWF will try to render to this more than once.
|
2022-09-03 00:18:57 +00:00
|
|
|
//
|
2022-09-07 01:18:31 +00:00
|
|
|
// If we end up hitting wgpu device limits due to having too
|
|
|
|
// many buffers / depth textures rendered at once, we could
|
|
|
|
// try storing this data in an LRU cache, evicting entries
|
|
|
|
// as needed.
|
|
|
|
let mut texture_offscreen =
|
|
|
|
texture
|
|
|
|
.texture_wrapper
|
|
|
|
.texture_offscreen
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
let buffer_dimensions = BufferDimensions::new(width as usize, height as usize);
|
|
|
|
let buffer_label = create_debug_label!("Render target buffer");
|
|
|
|
let buffer = self
|
|
|
|
.descriptors
|
|
|
|
.device
|
|
|
|
.create_buffer(&wgpu::BufferDescriptor {
|
|
|
|
label: buffer_label.as_deref(),
|
|
|
|
size: (buffer_dimensions.padded_bytes_per_row.get() as u64
|
|
|
|
* buffer_dimensions.height as u64),
|
|
|
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
|
|
|
mapped_at_creation: false,
|
|
|
|
});
|
|
|
|
TextureOffscreen {
|
|
|
|
buffer,
|
|
|
|
buffer_dimensions,
|
2022-09-07 19:26:46 +00:00
|
|
|
surface: Surface::new(
|
|
|
|
&self.descriptors,
|
2022-09-08 08:05:34 +00:00
|
|
|
DEFAULT_SAMPLE_COUNT,
|
2022-09-07 19:26:46 +00:00
|
|
|
width,
|
|
|
|
height,
|
|
|
|
wgpu::TextureFormat::Rgba8Unorm,
|
|
|
|
),
|
2022-09-07 01:18:31 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let mut target = TextureTarget {
|
|
|
|
size: extent,
|
|
|
|
texture: texture.texture_wrapper.texture,
|
|
|
|
format: wgpu::TextureFormat::Rgba8Unorm,
|
|
|
|
buffer: texture_offscreen.buffer,
|
|
|
|
buffer_dimensions: texture_offscreen.buffer_dimensions,
|
|
|
|
};
|
|
|
|
|
|
|
|
let (old_width, old_height) = self.globals.resolution();
|
2022-09-07 19:53:21 +00:00
|
|
|
self.globals.set_resolution(width, height);
|
2022-09-07 01:18:31 +00:00
|
|
|
|
|
|
|
let frame_output = target
|
|
|
|
.get_next_texture()
|
|
|
|
.expect("TextureTargetFrame.get_next_texture is infallible");
|
|
|
|
|
2022-09-07 19:53:21 +00:00
|
|
|
let command_buffers = texture_offscreen.surface.draw_commands(
|
|
|
|
frame_output.view(),
|
2022-09-28 15:47:35 +00:00
|
|
|
None,
|
2022-09-07 01:18:31 +00:00
|
|
|
&self.descriptors,
|
2022-09-07 19:53:21 +00:00
|
|
|
&mut self.globals,
|
|
|
|
&mut self.uniform_buffers_storage,
|
2022-09-07 09:33:12 +00:00
|
|
|
&self.meshes,
|
|
|
|
&self.bitmap_registry,
|
2022-09-07 19:53:21 +00:00
|
|
|
commands,
|
|
|
|
);
|
2022-09-07 01:18:31 +00:00
|
|
|
target.submit(
|
|
|
|
&self.descriptors.device,
|
|
|
|
&self.descriptors.queue,
|
2022-09-07 19:53:21 +00:00
|
|
|
command_buffers,
|
2022-09-07 01:18:31 +00:00
|
|
|
frame_output,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Capture with premultiplied alpha, which is what we use for all textures
|
|
|
|
let image = target.capture(&self.descriptors.device, true);
|
|
|
|
|
|
|
|
let image = image.map(|image| {
|
|
|
|
Bitmap::new(
|
|
|
|
image.dimensions().0,
|
|
|
|
image.dimensions().1,
|
|
|
|
ruffle_render::bitmap::BitmapFormat::Rgba,
|
|
|
|
image.into_raw(),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
self.globals.set_resolution(old_width, old_height);
|
|
|
|
texture_offscreen.buffer = target.buffer;
|
|
|
|
texture_offscreen.buffer_dimensions = target.buffer_dimensions;
|
|
|
|
texture.texture_wrapper.texture_offscreen = Some(texture_offscreen);
|
|
|
|
texture.texture_wrapper.texture = target.texture;
|
2022-09-28 15:47:35 +00:00
|
|
|
texture.bitmap = image.clone().unwrap();
|
2022-09-07 01:18:31 +00:00
|
|
|
self.bitmap_registry.insert(handle, texture);
|
|
|
|
|
|
|
|
Ok(image.unwrap())
|
2022-09-03 00:18:57 +00:00
|
|
|
}
|
2022-09-03 17:22:57 +00:00
|
|
|
}
|
2022-09-03 00:18:57 +00:00
|
|
|
|
|
|
|
// We try to request the highest limits we can get away with
|
|
|
|
async fn request_device(
|
|
|
|
adapter: &wgpu::Adapter,
|
|
|
|
trace_path: Option<&Path>,
|
|
|
|
) -> Result<(wgpu::Device, wgpu::Queue), wgpu::RequestDeviceError> {
|
|
|
|
// We start off with the lowest limits we actually need - basically GL-ES 3.0
|
|
|
|
let mut limits = wgpu::Limits::downlevel_webgl2_defaults();
|
|
|
|
// Then we increase parts of it to the maximum supported by the adapter, to take advantage of
|
|
|
|
// more powerful hardware or capabilities
|
|
|
|
limits = limits.using_resolution(adapter.limits());
|
|
|
|
limits = limits.using_alignment(adapter.limits());
|
|
|
|
|
|
|
|
limits.max_storage_buffers_per_shader_stage =
|
|
|
|
adapter.limits().max_storage_buffers_per_shader_stage;
|
|
|
|
limits.max_storage_buffer_binding_size = adapter.limits().max_storage_buffer_binding_size;
|
|
|
|
|
|
|
|
adapter
|
|
|
|
.request_device(
|
|
|
|
&wgpu::DeviceDescriptor {
|
|
|
|
label: None,
|
2022-10-15 12:56:16 +00:00
|
|
|
features: wgpu::Features::DEPTH32FLOAT_STENCIL8,
|
2022-09-03 00:18:57 +00:00
|
|
|
limits,
|
|
|
|
},
|
|
|
|
trace_path,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|