Skip to content

Commit

Permalink
wgpu: Detect if buffers are unusable on web and fail to create wgpu r…
Browse files Browse the repository at this point in the history
…enderer if so
  • Loading branch information
Dinnerbone committed Jan 12, 2023
1 parent 8db8e46 commit 2e72013
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 0 deletions.
18 changes: 18 additions & 0 deletions render/wgpu/shaders/buffer_bug_detection.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@group(0) @binding(0) var<uniform> color: vec4<f32>;

struct VertexOutput {
@location(0) color: vec4<f32>,
@builtin(position) position: vec4<f32>,
};

@vertex
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> VertexOutput {
let x = f32(i32(in_vertex_index / 2u) * 2 - 1);
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
return VertexOutput(color, vec4<f32>(x, y, 0.0, 1.0));
}

@fragment
fn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {
return vertex.color;
}
5 changes: 5 additions & 0 deletions render/wgpu/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::surface::Surface;
use crate::target::RenderTargetFrame;
use crate::target::TextureTarget;
use crate::uniform_buffer::BufferStorage;
use crate::utils::detect_buffer_bug;
use crate::{
as_texture, format_list, get_backend_names, ColorAdjustments, Descriptors, Error,
QueueSyncHandle, RenderTarget, SwapChainTarget, Texture, Transforms,
Expand Down Expand Up @@ -209,6 +210,10 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {

let (device, queue) = request_device(&adapter, trace_path).await?;

if cfg!(target_arch = "wasm") {
detect_buffer_bug(&device, &queue)?;
}

Ok(Descriptors::new(adapter, device, queue))
}

Expand Down
127 changes: 127 additions & 0 deletions render/wgpu/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::Error;
use ruffle_render::utils::unmultiply_alpha_rgba;
use std::borrow::Cow;
use std::mem::size_of;
use std::num::NonZeroU32;
use wgpu::include_wgsl;
use wgpu::util::DeviceExt;

macro_rules! create_debug_label {
Expand Down Expand Up @@ -155,3 +157,128 @@ pub fn buffer_to_image(
buffer.unmap();
image
}

#[allow(dead_code)]
// https://github.com/gfx-rs/wgpu/issues/3371
pub fn detect_buffer_bug(device: &wgpu::Device, queue: &wgpu::Queue) -> Result<(), Error> {
let expected_color: [f32; 4] = [1.0, 0.5, 0.25, 1.0];
let size = wgpu::Extent3d {
width: 32,
height: 32,
depth_or_array_layers: 1,
};
let format = wgpu::TextureFormat::Rgba8Unorm;
let buffer_dimensions = BufferDimensions::new(size.width as usize, size.height as usize);

let shader = device.create_shader_module(include_wgsl!("../shaders/buffer_bug_detection.wgsl"));
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(std::mem::size_of::<[f32; 4]>() as u64),
},
count: None,
}],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
multisample: Default::default(),
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(format.into())],
}),
multiview: None,
});
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
});
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents: bytemuck::cast_slice(&expected_color),
usage: wgpu::BufferUsages::UNIFORM,
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: uniform_buffer.as_entire_binding(),
}],
});
let result = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: (buffer_dimensions.padded_bytes_per_row.get() as u64
* buffer_dimensions.height as u64),
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&Default::default());
{
let view = texture.create_view(&Default::default());
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: true,
},
})],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&pipeline);
render_pass.set_bind_group(0, &bind_group, &[]);
render_pass.draw(0..4, 0..1);
}
encoder.copy_texture_to_buffer(
texture.as_image_copy(),
wgpu::ImageCopyBuffer {
buffer: &result,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(buffer_dimensions.padded_bytes_per_row),
rows_per_image: None,
},
},
size,
);
let index = queue.submit(Some(encoder.finish()));
let image = buffer_to_image(device, &result, &buffer_dimensions, Some(index), size, true);
let expected_rgba = image::Rgba(expected_color.map(|c| (c * 256.0) as u8));
if image.get_pixel(0, 0) != &expected_rgba {
tracing::error!(
"Buffer test failed, expected {expected_rgba:?} but found {:?}",
image.get_pixel(0, 0)
);
return Err("Buffer test failed".to_string().into());
}
Ok(())
}

0 comments on commit 2e72013

Please sign in to comment.