2023-02-03 15:44:21 +00:00
|
|
|
use ruffle_render::quality::StageQuality;
|
2023-01-07 16:08:23 +00:00
|
|
|
use ruffle_render::utils::unmultiply_alpha_rgba;
|
2020-08-29 12:55:21 +00:00
|
|
|
use std::borrow::Cow;
|
2020-08-27 10:32:41 +00:00
|
|
|
use std::mem::size_of;
|
2021-04-16 20:55:31 +00:00
|
|
|
use std::num::NonZeroU32;
|
2020-08-27 10:32:41 +00:00
|
|
|
use wgpu::util::DeviceExt;
|
2021-03-20 14:12:39 +00:00
|
|
|
|
2020-04-29 10:25:37 +00:00
|
|
|
macro_rules! create_debug_label {
|
|
|
|
($($arg:tt)*) => (
|
|
|
|
if cfg!(feature = "render_debug_labels") {
|
|
|
|
Some(format!($($arg)*))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-09-08 08:05:34 +00:00
|
|
|
pub fn remove_srgb(format: wgpu::TextureFormat) -> wgpu::TextureFormat {
|
|
|
|
match format {
|
|
|
|
wgpu::TextureFormat::Rgba8UnormSrgb => wgpu::TextureFormat::Rgba8Unorm,
|
|
|
|
wgpu::TextureFormat::Bgra8UnormSrgb => wgpu::TextureFormat::Bgra8Unorm,
|
|
|
|
wgpu::TextureFormat::Bc1RgbaUnormSrgb => wgpu::TextureFormat::Bc1RgbaUnorm,
|
|
|
|
wgpu::TextureFormat::Bc2RgbaUnormSrgb => wgpu::TextureFormat::Bc2RgbaUnorm,
|
|
|
|
wgpu::TextureFormat::Bc3RgbaUnormSrgb => wgpu::TextureFormat::Bc3RgbaUnorm,
|
|
|
|
wgpu::TextureFormat::Bc7RgbaUnormSrgb => wgpu::TextureFormat::Bc7RgbaUnorm,
|
|
|
|
wgpu::TextureFormat::Etc2Rgb8UnormSrgb => wgpu::TextureFormat::Etc2Rgb8Unorm,
|
|
|
|
wgpu::TextureFormat::Etc2Rgb8A1UnormSrgb => wgpu::TextureFormat::Etc2Rgb8A1Unorm,
|
|
|
|
wgpu::TextureFormat::Etc2Rgba8UnormSrgb => wgpu::TextureFormat::Etc2Rgba8Unorm,
|
|
|
|
wgpu::TextureFormat::Astc {
|
|
|
|
block,
|
|
|
|
channel: wgpu::AstcChannel::UnormSrgb,
|
|
|
|
} => wgpu::TextureFormat::Astc {
|
|
|
|
block,
|
|
|
|
channel: wgpu::AstcChannel::Unorm,
|
|
|
|
},
|
|
|
|
_ => format,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-29 12:55:21 +00:00
|
|
|
pub fn format_list<'a>(values: &[&'a str], connector: &'a str) -> Cow<'a, str> {
|
|
|
|
match values.len() {
|
|
|
|
0 => Cow::Borrowed(""),
|
|
|
|
1 => Cow::Borrowed(values[0]),
|
|
|
|
_ => Cow::Owned(
|
|
|
|
values[0..values.len() - 1].join(", ")
|
|
|
|
+ " "
|
|
|
|
+ connector
|
|
|
|
+ " "
|
|
|
|
+ values[values.len() - 1],
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-08 07:20:11 +00:00
|
|
|
pub fn get_backend_names(backends: wgpu::Backends) -> Vec<&'static str> {
|
2020-08-29 12:55:21 +00:00
|
|
|
let mut names = Vec::new();
|
|
|
|
|
2021-09-08 07:20:11 +00:00
|
|
|
if backends.contains(wgpu::Backends::VULKAN) {
|
2020-08-29 12:55:21 +00:00
|
|
|
names.push("Vulkan");
|
|
|
|
}
|
2021-09-08 07:20:11 +00:00
|
|
|
if backends.contains(wgpu::Backends::DX12) {
|
2020-08-29 12:55:21 +00:00
|
|
|
names.push("DirectX 12");
|
|
|
|
}
|
2021-09-08 07:20:11 +00:00
|
|
|
if backends.contains(wgpu::Backends::DX11) {
|
2020-08-29 12:55:21 +00:00
|
|
|
names.push("DirectX 11");
|
|
|
|
}
|
2021-09-08 07:20:11 +00:00
|
|
|
if backends.contains(wgpu::Backends::METAL) {
|
2020-08-29 12:55:21 +00:00
|
|
|
names.push("Metal");
|
|
|
|
}
|
2021-09-08 07:20:11 +00:00
|
|
|
if backends.contains(wgpu::Backends::GL) {
|
2020-08-29 12:55:21 +00:00
|
|
|
names.push("Open GL");
|
|
|
|
}
|
2021-09-08 07:20:11 +00:00
|
|
|
if backends.contains(wgpu::Backends::BROWSER_WEBGPU) {
|
2020-08-29 12:55:21 +00:00
|
|
|
names.push("Web GPU");
|
|
|
|
}
|
|
|
|
|
|
|
|
names
|
|
|
|
}
|
|
|
|
|
2020-04-29 10:25:37 +00:00
|
|
|
pub fn create_buffer_with_data(
|
|
|
|
device: &wgpu::Device,
|
|
|
|
data: &[u8],
|
2021-09-08 07:20:11 +00:00
|
|
|
usage: wgpu::BufferUsages,
|
2020-04-29 10:25:37 +00:00
|
|
|
label: Option<String>,
|
|
|
|
) -> wgpu::Buffer {
|
2020-08-27 10:32:41 +00:00
|
|
|
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
2020-04-29 10:25:37 +00:00
|
|
|
usage,
|
|
|
|
label: label.as_deref(),
|
2020-08-27 10:32:41 +00:00
|
|
|
contents: data,
|
|
|
|
})
|
2020-04-29 10:25:37 +00:00
|
|
|
}
|
2020-04-29 10:59:55 +00:00
|
|
|
|
2020-08-27 10:32:41 +00:00
|
|
|
// Based off wgpu example 'capture'
|
2022-09-30 21:25:54 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2020-08-27 10:32:41 +00:00
|
|
|
pub struct BufferDimensions {
|
|
|
|
pub width: usize,
|
|
|
|
pub height: usize,
|
|
|
|
pub unpadded_bytes_per_row: usize,
|
2021-04-16 20:55:31 +00:00
|
|
|
pub padded_bytes_per_row: NonZeroU32,
|
2020-08-27 10:32:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BufferDimensions {
|
2021-09-09 00:54:13 +00:00
|
|
|
#[allow(dead_code)]
|
2020-08-27 10:32:41 +00:00
|
|
|
pub fn new(width: usize, height: usize) -> Self {
|
|
|
|
let bytes_per_pixel = size_of::<u32>();
|
|
|
|
let unpadded_bytes_per_row = width * bytes_per_pixel;
|
|
|
|
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as usize;
|
|
|
|
let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;
|
2021-04-16 20:55:31 +00:00
|
|
|
let padded_bytes_per_row =
|
|
|
|
NonZeroU32::new((unpadded_bytes_per_row + padded_bytes_per_row_padding) as u32)
|
|
|
|
.unwrap();
|
|
|
|
|
2020-08-27 10:32:41 +00:00
|
|
|
Self {
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
unpadded_bytes_per_row,
|
|
|
|
padded_bytes_per_row,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-07 16:08:23 +00:00
|
|
|
|
|
|
|
pub fn buffer_to_image(
|
|
|
|
device: &wgpu::Device,
|
|
|
|
buffer: &wgpu::Buffer,
|
|
|
|
dimensions: &BufferDimensions,
|
|
|
|
index: Option<wgpu::SubmissionIndex>,
|
|
|
|
size: wgpu::Extent3d,
|
|
|
|
premultiplied_alpha: bool,
|
|
|
|
) -> image::RgbaImage {
|
|
|
|
let (sender, receiver) = std::sync::mpsc::channel();
|
|
|
|
let buffer_slice = buffer.slice(..);
|
|
|
|
buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
|
|
|
|
sender.send(result).unwrap();
|
|
|
|
});
|
|
|
|
device.poll(
|
|
|
|
index
|
|
|
|
.map(wgpu::Maintain::WaitForSubmissionIndex)
|
|
|
|
.unwrap_or(wgpu::Maintain::Wait),
|
|
|
|
);
|
|
|
|
let _ = receiver.recv().expect("MPSC channel must not fail");
|
|
|
|
let map = buffer_slice.get_mapped_range();
|
|
|
|
let mut bytes = Vec::with_capacity(dimensions.height * dimensions.unpadded_bytes_per_row);
|
|
|
|
|
|
|
|
for chunk in map.chunks(dimensions.padded_bytes_per_row.get() as usize) {
|
|
|
|
bytes.extend_from_slice(&chunk[..dimensions.unpadded_bytes_per_row]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The image copied from the GPU uses premultiplied alpha, so
|
|
|
|
// convert to straight alpha if requested by the user.
|
|
|
|
if !premultiplied_alpha {
|
|
|
|
unmultiply_alpha_rgba(&mut bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
let image = image::RgbaImage::from_raw(size.width, size.height, bytes)
|
|
|
|
.expect("Retrieved texture buffer must be a valid RgbaImage");
|
|
|
|
drop(map);
|
|
|
|
buffer.unmap();
|
|
|
|
image
|
|
|
|
}
|
2023-02-03 15:44:21 +00:00
|
|
|
|
2023-02-03 18:04:42 +00:00
|
|
|
pub fn supported_sample_count(
|
|
|
|
adapter: &wgpu::Adapter,
|
|
|
|
quality: StageQuality,
|
|
|
|
format: wgpu::TextureFormat,
|
|
|
|
) -> u32 {
|
2023-02-03 15:44:21 +00:00
|
|
|
let mut sample_count = quality.sample_count();
|
2023-02-03 18:04:42 +00:00
|
|
|
let features = adapter.get_texture_format_features(format).flags;
|
2023-02-03 15:44:21 +00:00
|
|
|
|
|
|
|
// Keep halving the sample count until we get one that's supported - or 1 (no multisampling)
|
|
|
|
// It's not guaranteed that supporting 4x means supporting 2x, so there's no "max" option
|
|
|
|
// And it's probably safer to round down than up, given it's a performance setting.
|
|
|
|
while sample_count > 1 && !features.sample_count_supported(sample_count) {
|
|
|
|
sample_count /= 2;
|
|
|
|
}
|
|
|
|
sample_count
|
|
|
|
}
|