2023-06-15 22:50:24 +00:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::num::NonZeroU64;
|
|
|
|
use std::{borrow::Cow, cell::Cell, sync::Arc};
|
|
|
|
|
|
|
|
use indexmap::IndexMap;
|
|
|
|
use ruffle_render::error::Error as BitmapError;
|
|
|
|
use ruffle_render::pixel_bender::{
|
2023-07-01 17:50:43 +00:00
|
|
|
ImageInputTexture, PixelBenderShaderHandle, PixelBenderShaderImpl, PixelBenderType,
|
|
|
|
OUT_COORD_NAME,
|
2023-06-15 22:50:24 +00:00
|
|
|
};
|
|
|
|
use ruffle_render::{
|
2023-07-01 17:50:43 +00:00
|
|
|
bitmap::BitmapHandle,
|
2023-06-15 22:50:24 +00:00
|
|
|
pixel_bender::{PixelBenderParam, PixelBenderShader, PixelBenderShaderArgument},
|
|
|
|
};
|
|
|
|
use wgpu::util::StagingBelt;
|
|
|
|
use wgpu::{
|
|
|
|
BindGroupEntry, BindingResource, BlendComponent, BufferDescriptor, BufferUsages,
|
2023-07-01 17:50:43 +00:00
|
|
|
ColorTargetState, ColorWrites, CommandEncoder, FrontFace, ImageCopyTexture,
|
|
|
|
RenderPipelineDescriptor, SamplerBindingType, ShaderModuleDescriptor, TextureDescriptor,
|
|
|
|
TextureFormat, TextureView, VertexState,
|
2023-06-15 22:50:24 +00:00
|
|
|
};
|
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
use crate::filters::{FilterSource, VERTEX_BUFFERS_DESCRIPTION_FILTERS};
|
|
|
|
use crate::raw_texture_as_texture;
|
2023-06-15 22:50:24 +00:00
|
|
|
use crate::{
|
2023-07-01 17:50:43 +00:00
|
|
|
as_texture, backend::WgpuRenderBackend, descriptors::Descriptors, target::RenderTarget, Texture,
|
2023-06-15 22:50:24 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PixelBenderWgpuShader {
|
|
|
|
bind_group_layout: wgpu::BindGroupLayout,
|
|
|
|
pipeline: wgpu::RenderPipeline,
|
|
|
|
shader: PixelBenderShader,
|
|
|
|
float_parameters_buffer: wgpu::Buffer,
|
|
|
|
float_parameters_buffer_size: u64,
|
|
|
|
int_parameters_buffer: wgpu::Buffer,
|
|
|
|
int_parameters_buffer_size: u64,
|
2023-07-01 17:50:43 +00:00
|
|
|
zeroed_out_of_range_mode: wgpu::Buffer,
|
2023-06-15 22:50:24 +00:00
|
|
|
staging_belt: RefCell<StagingBelt>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PixelBenderShaderImpl for PixelBenderWgpuShader {
|
|
|
|
fn parsed_shader(&self) -> &PixelBenderShader {
|
|
|
|
&self.shader
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn as_cache_holder(handle: &PixelBenderShaderHandle) -> &PixelBenderWgpuShader {
|
|
|
|
<dyn PixelBenderShaderImpl>::downcast_ref(&*handle.0).unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PixelBenderWgpuShader {
|
|
|
|
pub fn new(descriptors: &Descriptors, shader: PixelBenderShader) -> PixelBenderWgpuShader {
|
|
|
|
let mut layout_entries = vec![
|
|
|
|
// One sampler per filter/wrapping combination - see BitmapFilters
|
|
|
|
// An AGAL shader can use any of these samplers, so
|
|
|
|
// we need to bind them all.
|
|
|
|
wgpu::BindGroupLayoutEntry {
|
|
|
|
binding: naga_pixelbender::SAMPLER_CLAMP_NEAREST,
|
|
|
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
|
|
ty: wgpu::BindingType::Sampler(SamplerBindingType::Filtering),
|
|
|
|
count: None,
|
|
|
|
},
|
|
|
|
wgpu::BindGroupLayoutEntry {
|
|
|
|
binding: naga_pixelbender::SAMPLER_CLAMP_LINEAR,
|
|
|
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
|
|
ty: wgpu::BindingType::Sampler(SamplerBindingType::Filtering),
|
|
|
|
count: None,
|
|
|
|
},
|
|
|
|
wgpu::BindGroupLayoutEntry {
|
|
|
|
binding: naga_pixelbender::SAMPLER_CLAMP_BILINEAR,
|
|
|
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
|
|
ty: wgpu::BindingType::Sampler(SamplerBindingType::Filtering),
|
|
|
|
count: None,
|
|
|
|
},
|
|
|
|
wgpu::BindGroupLayoutEntry {
|
|
|
|
binding: naga_pixelbender::SHADER_FLOAT_PARAMETERS_INDEX,
|
|
|
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
|
|
ty: wgpu::BindingType::Buffer {
|
|
|
|
ty: wgpu::BufferBindingType::Uniform,
|
|
|
|
has_dynamic_offset: false,
|
|
|
|
min_binding_size: None,
|
|
|
|
},
|
|
|
|
count: None,
|
|
|
|
},
|
|
|
|
wgpu::BindGroupLayoutEntry {
|
|
|
|
binding: naga_pixelbender::SHADER_INT_PARAMETERS_INDEX,
|
|
|
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
|
|
ty: wgpu::BindingType::Buffer {
|
|
|
|
ty: wgpu::BufferBindingType::Uniform,
|
|
|
|
has_dynamic_offset: false,
|
|
|
|
min_binding_size: None,
|
|
|
|
},
|
|
|
|
count: None,
|
|
|
|
},
|
2023-07-01 17:50:43 +00:00
|
|
|
wgpu::BindGroupLayoutEntry {
|
|
|
|
binding: naga_pixelbender::ZEROED_OUT_OF_RANGE_MODE_INDEX,
|
|
|
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
|
|
ty: wgpu::BindingType::Buffer {
|
|
|
|
ty: wgpu::BufferBindingType::Uniform,
|
|
|
|
has_dynamic_offset: false,
|
|
|
|
min_binding_size: None,
|
|
|
|
},
|
|
|
|
count: None,
|
|
|
|
},
|
2023-06-15 22:50:24 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
for param in &shader.params {
|
|
|
|
if let PixelBenderParam::Texture { index, .. } = param {
|
|
|
|
let binding = naga_pixelbender::TEXTURE_START_BIND_INDEX + *index as u32;
|
|
|
|
layout_entries.push(wgpu::BindGroupLayoutEntry {
|
|
|
|
binding,
|
|
|
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
|
|
ty: wgpu::BindingType::Texture {
|
|
|
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
|
|
view_dimension: wgpu::TextureViewDimension::D2,
|
|
|
|
multisampled: false,
|
|
|
|
},
|
|
|
|
count: None,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let globals_layout_label =
|
|
|
|
create_debug_label!("PixelBender bind group layout for {:?}", shader.name);
|
|
|
|
let bind_group_layout =
|
|
|
|
descriptors
|
|
|
|
.device
|
|
|
|
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
|
|
label: globals_layout_label.as_deref(),
|
|
|
|
entries: &layout_entries,
|
|
|
|
});
|
|
|
|
|
|
|
|
let pipeline_layout_label =
|
|
|
|
create_debug_label!("PixelBender pipeline layout for {:?}", shader.name);
|
|
|
|
let pipeline_layout =
|
|
|
|
descriptors
|
|
|
|
.device
|
|
|
|
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
|
|
label: pipeline_layout_label.as_deref(),
|
|
|
|
bind_group_layouts: &[&bind_group_layout],
|
|
|
|
push_constant_ranges: &[],
|
|
|
|
});
|
|
|
|
|
|
|
|
let shaders =
|
|
|
|
naga_pixelbender::ShaderBuilder::build(&shader).expect("Failed to compile shader");
|
|
|
|
|
|
|
|
let float_label =
|
|
|
|
create_debug_label!("PixelBender float parameters buffer for {:?}", shader.name);
|
|
|
|
|
|
|
|
let float_parameters_buffer = descriptors.device.create_buffer(&BufferDescriptor {
|
|
|
|
label: float_label.as_deref(),
|
|
|
|
size: shaders.float_parameters_buffer_size,
|
|
|
|
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
|
|
|
mapped_at_creation: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
let int_label =
|
|
|
|
create_debug_label!("PixelBender int parameters buffer for {:?}", shader.name);
|
|
|
|
|
|
|
|
let int_parameters_buffer = descriptors.device.create_buffer(&BufferDescriptor {
|
|
|
|
label: int_label.as_deref(),
|
|
|
|
size: shaders.int_parameters_buffer_size,
|
|
|
|
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
|
|
|
mapped_at_creation: false,
|
|
|
|
});
|
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
let zeroed_out_of_range_mode = descriptors.device.create_buffer(&BufferDescriptor {
|
|
|
|
label: create_debug_label!("PixelBender zeroed_out_of_range_mode parameter buffer")
|
|
|
|
.as_deref(),
|
|
|
|
size: shaders.int_parameters_buffer_size,
|
|
|
|
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
|
|
|
mapped_at_creation: false,
|
|
|
|
});
|
|
|
|
|
2023-06-15 22:50:24 +00:00
|
|
|
let vertex_shader = descriptors
|
|
|
|
.device
|
|
|
|
.create_shader_module(ShaderModuleDescriptor {
|
|
|
|
label: None,
|
|
|
|
source: wgpu::ShaderSource::Naga(Cow::Owned(shaders.vertex)),
|
|
|
|
});
|
|
|
|
|
|
|
|
let fragment_shader = descriptors
|
|
|
|
.device
|
|
|
|
.create_shader_module(ShaderModuleDescriptor {
|
|
|
|
label: None,
|
|
|
|
source: wgpu::ShaderSource::Naga(Cow::Owned(shaders.fragment)),
|
|
|
|
});
|
|
|
|
|
|
|
|
let pipeline = descriptors
|
|
|
|
.device
|
|
|
|
.create_render_pipeline(&RenderPipelineDescriptor {
|
|
|
|
label: create_debug_label!("RenderPipeline").as_deref(),
|
|
|
|
layout: Some(&pipeline_layout),
|
|
|
|
vertex: VertexState {
|
|
|
|
module: &vertex_shader,
|
2023-07-01 17:50:43 +00:00
|
|
|
entry_point: naga_pixelbender::VERTEX_SHADER_ENTRYPOINT,
|
|
|
|
buffers: &VERTEX_BUFFERS_DESCRIPTION_FILTERS,
|
2023-06-15 22:50:24 +00:00
|
|
|
},
|
|
|
|
fragment: Some(wgpu::FragmentState {
|
|
|
|
module: &fragment_shader,
|
2023-07-01 17:50:43 +00:00
|
|
|
entry_point: naga_pixelbender::FRAGMENT_SHADER_ENTRYPOINT,
|
2023-06-15 22:50:24 +00:00
|
|
|
targets: &[Some(ColorTargetState {
|
|
|
|
format: TextureFormat::Rgba8Unorm,
|
|
|
|
// FIXME - what should this be?
|
|
|
|
blend: Some(wgpu::BlendState {
|
|
|
|
color: BlendComponent::OVER,
|
|
|
|
alpha: BlendComponent::OVER,
|
|
|
|
}),
|
|
|
|
write_mask: ColorWrites::all(),
|
|
|
|
})],
|
|
|
|
}),
|
|
|
|
primitive: wgpu::PrimitiveState {
|
|
|
|
front_face: FrontFace::Ccw,
|
|
|
|
cull_mode: None,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
depth_stencil: None,
|
|
|
|
multisample: wgpu::MultisampleState {
|
|
|
|
count: 1,
|
|
|
|
mask: !0,
|
|
|
|
alpha_to_coverage_enabled: false,
|
|
|
|
},
|
|
|
|
multiview: Default::default(),
|
|
|
|
});
|
|
|
|
|
|
|
|
PixelBenderWgpuShader {
|
|
|
|
bind_group_layout,
|
|
|
|
pipeline,
|
|
|
|
shader,
|
|
|
|
float_parameters_buffer,
|
|
|
|
float_parameters_buffer_size: shaders.float_parameters_buffer_size,
|
|
|
|
int_parameters_buffer,
|
|
|
|
int_parameters_buffer_size: shaders.int_parameters_buffer_size,
|
2023-07-01 17:50:43 +00:00
|
|
|
zeroed_out_of_range_mode,
|
2023-06-15 22:50:24 +00:00
|
|
|
// FIXME - come up with a good chunk size
|
|
|
|
staging_belt: RefCell::new(StagingBelt::new(8)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
fn image_input_as_texture<'a>(input: &'a ImageInputTexture<'a>) -> &wgpu::Texture {
|
|
|
|
match input {
|
|
|
|
ImageInputTexture::Bitmap(handle) => &as_texture(handle).texture,
|
|
|
|
ImageInputTexture::TextureRef(raw_texture) => raw_texture_as_texture(*raw_texture),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-15 22:50:24 +00:00
|
|
|
impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|
|
|
pub(super) fn compile_pixelbender_shader_impl(
|
|
|
|
&mut self,
|
|
|
|
shader: PixelBenderShader,
|
|
|
|
) -> Result<PixelBenderShaderHandle, BitmapError> {
|
|
|
|
let handle = PixelBenderWgpuShader::new(&self.descriptors, shader);
|
|
|
|
Ok(PixelBenderShaderHandle(Arc::new(handle)))
|
|
|
|
}
|
2023-07-01 17:50:43 +00:00
|
|
|
}
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
pub enum ShaderMode {
|
|
|
|
ShaderJob,
|
|
|
|
Filter,
|
|
|
|
}
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
pub(super) fn run_pixelbender_shader_impl(
|
|
|
|
descriptors: &Descriptors,
|
|
|
|
shader: PixelBenderShaderHandle,
|
|
|
|
mode: ShaderMode,
|
|
|
|
arguments: &[PixelBenderShaderArgument],
|
|
|
|
target: &wgpu::Texture,
|
|
|
|
render_command_encoder: &mut CommandEncoder,
|
|
|
|
color_attachment: Option<wgpu::RenderPassColorAttachment>,
|
|
|
|
// FIXME - do we cover the whole source or the whole dest?
|
|
|
|
source: &FilterSource,
|
|
|
|
) -> Result<(), BitmapError> {
|
|
|
|
let compiled_shader = &as_cache_holder(&shader);
|
|
|
|
let mut staging_belt = compiled_shader.staging_belt.borrow_mut();
|
|
|
|
|
|
|
|
let mut arguments = arguments.to_vec();
|
|
|
|
|
|
|
|
let mut bind_group_entries = vec![
|
|
|
|
BindGroupEntry {
|
|
|
|
binding: naga_pixelbender::SAMPLER_CLAMP_NEAREST,
|
|
|
|
resource: BindingResource::Sampler(&descriptors.bitmap_samplers.clamp_nearest),
|
|
|
|
},
|
|
|
|
BindGroupEntry {
|
|
|
|
binding: naga_pixelbender::SAMPLER_CLAMP_LINEAR,
|
|
|
|
resource: BindingResource::Sampler(&descriptors.bitmap_samplers.clamp_linear),
|
|
|
|
},
|
|
|
|
BindGroupEntry {
|
|
|
|
binding: naga_pixelbender::SAMPLER_CLAMP_BILINEAR,
|
|
|
|
// FIXME - create bilinear sampler
|
|
|
|
resource: BindingResource::Sampler(&descriptors.bitmap_samplers.clamp_linear),
|
|
|
|
},
|
|
|
|
BindGroupEntry {
|
|
|
|
binding: naga_pixelbender::SHADER_FLOAT_PARAMETERS_INDEX,
|
|
|
|
resource: BindingResource::Buffer(wgpu::BufferBinding {
|
|
|
|
buffer: &compiled_shader.float_parameters_buffer,
|
|
|
|
offset: 0,
|
|
|
|
size: Some(NonZeroU64::new(compiled_shader.float_parameters_buffer_size).unwrap()),
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
BindGroupEntry {
|
|
|
|
binding: naga_pixelbender::SHADER_INT_PARAMETERS_INDEX,
|
|
|
|
resource: BindingResource::Buffer(wgpu::BufferBinding {
|
|
|
|
buffer: &compiled_shader.int_parameters_buffer,
|
|
|
|
offset: 0,
|
|
|
|
size: Some(NonZeroU64::new(compiled_shader.int_parameters_buffer_size).unwrap()),
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
BindGroupEntry {
|
|
|
|
binding: naga_pixelbender::ZEROED_OUT_OF_RANGE_MODE_INDEX,
|
|
|
|
resource: BindingResource::Buffer(wgpu::BufferBinding {
|
|
|
|
buffer: &compiled_shader.zeroed_out_of_range_mode,
|
|
|
|
offset: 0,
|
|
|
|
size: Some(NonZeroU64::new(std::mem::size_of::<f32>() as u64).unwrap()),
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
let mut zeroed_out_of_range_mode_slice = staging_belt.write_buffer(
|
|
|
|
render_command_encoder,
|
|
|
|
&compiled_shader.zeroed_out_of_range_mode,
|
|
|
|
0,
|
|
|
|
NonZeroU64::new(std::mem::size_of::<f32>() as u64).unwrap(),
|
|
|
|
&descriptors.device,
|
|
|
|
);
|
|
|
|
|
|
|
|
zeroed_out_of_range_mode_slice.copy_from_slice(bytemuck::cast_slice(&[match mode {
|
|
|
|
// When a Shader is run via a ShaderJob, out-of-range texture sample coordinates
|
|
|
|
// seem to be clamped to the edge of the texture (despite what the docs describe)
|
|
|
|
ShaderMode::ShaderJob => 0.0f32,
|
|
|
|
// When a Shader is run through a ShaderFilter, out-of-range texture sample coordinates
|
|
|
|
// return transparent black (0.0, 0.0, 0.0, 0.0). This is easiest to observe with
|
|
|
|
// BitmapData.applyFilter when the BitampData destination is larger than the source.
|
|
|
|
ShaderMode::Filter => 1.0f32,
|
|
|
|
}]));
|
|
|
|
drop(zeroed_out_of_range_mode_slice);
|
|
|
|
|
|
|
|
let mut texture_views: IndexMap<u8, TextureView> = Default::default();
|
|
|
|
|
|
|
|
let mut target_clone = None;
|
|
|
|
|
|
|
|
let mut float_offset = 0;
|
|
|
|
let mut int_offset = 0;
|
|
|
|
|
|
|
|
let mut first_image = None;
|
|
|
|
|
|
|
|
for input in &mut arguments {
|
|
|
|
match input {
|
|
|
|
PixelBenderShaderArgument::ImageInput { index, texture, .. } => {
|
|
|
|
let input_texture = &image_input_as_texture(texture.as_ref().unwrap());
|
|
|
|
if std::ptr::eq(*input_texture, target) {
|
2023-06-15 22:50:24 +00:00
|
|
|
// The input is the same as the output - we need to clone the input.
|
|
|
|
// We will write to the original output, and use a clone of the input as a texture input binding
|
2023-07-01 17:50:43 +00:00
|
|
|
let cached_fresh_handle = target_clone.get_or_insert_with(|| {
|
|
|
|
let extent = wgpu::Extent3d {
|
|
|
|
width: target.width(),
|
|
|
|
height: target.height(),
|
|
|
|
depth_or_array_layers: 1,
|
|
|
|
};
|
|
|
|
let fresh_texture = descriptors.device.create_texture(&TextureDescriptor {
|
|
|
|
label: Some("PixelBenderShader target clone"),
|
|
|
|
size: extent,
|
|
|
|
mip_level_count: 1,
|
|
|
|
sample_count: 1,
|
|
|
|
dimension: wgpu::TextureDimension::D2,
|
|
|
|
format: wgpu::TextureFormat::Rgba8Unorm,
|
|
|
|
usage: wgpu::TextureUsages::COPY_DST
|
|
|
|
| wgpu::TextureUsages::TEXTURE_BINDING,
|
|
|
|
view_formats: &[wgpu::TextureFormat::Rgba8Unorm],
|
2023-06-15 22:50:24 +00:00
|
|
|
});
|
2023-07-01 17:50:43 +00:00
|
|
|
render_command_encoder.copy_texture_to_texture(
|
|
|
|
ImageCopyTexture {
|
|
|
|
texture: target,
|
|
|
|
mip_level: 0,
|
|
|
|
origin: Default::default(),
|
|
|
|
aspect: Default::default(),
|
|
|
|
},
|
|
|
|
ImageCopyTexture {
|
|
|
|
texture: &fresh_texture,
|
|
|
|
mip_level: 0,
|
|
|
|
origin: Default::default(),
|
|
|
|
aspect: Default::default(),
|
|
|
|
},
|
|
|
|
extent,
|
|
|
|
);
|
|
|
|
|
|
|
|
BitmapHandle(Arc::new(Texture {
|
|
|
|
texture: Arc::new(fresh_texture),
|
|
|
|
bind_linear: Default::default(),
|
|
|
|
bind_nearest: Default::default(),
|
|
|
|
copy_count: Cell::new(0),
|
|
|
|
}))
|
|
|
|
});
|
|
|
|
*texture = Some(cached_fresh_handle.clone().into());
|
2023-06-15 22:50:24 +00:00
|
|
|
}
|
2023-07-01 17:50:43 +00:00
|
|
|
let wgpu_texture = image_input_as_texture(texture.as_ref().unwrap());
|
|
|
|
texture_views.insert(
|
|
|
|
*index,
|
|
|
|
wgpu_texture.create_view(&wgpu::TextureViewDescriptor::default()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
PixelBenderShaderArgument::ValueInput { index, value } => {
|
|
|
|
let param = &compiled_shader.shader.params[*index as usize];
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
let name = match param {
|
|
|
|
PixelBenderParam::Normal { name, .. } => name,
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
if name == OUT_COORD_NAME {
|
|
|
|
continue;
|
|
|
|
}
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
let (value_vec, is_float): (Vec<f32>, bool) = match value {
|
|
|
|
PixelBenderType::TFloat(f1) => (vec![*f1, 0.0, 0.0, 0.0], true),
|
|
|
|
PixelBenderType::TFloat2(f1, f2) => (vec![*f1, *f2, 0.0, 0.0], true),
|
|
|
|
PixelBenderType::TFloat3(f1, f2, f3) => (vec![*f1, *f2, *f3, 0.0], true),
|
|
|
|
PixelBenderType::TFloat4(f1, f2, f3, f4) => (vec![*f1, *f2, *f3, *f4], true),
|
|
|
|
PixelBenderType::TInt(i1) => (vec![*i1 as f32, 0.0, 0.0, 0.0], false),
|
|
|
|
PixelBenderType::TInt2(i1, i2) => {
|
|
|
|
(vec![*i1 as f32, *i2 as f32, 0.0, 0.0], false)
|
|
|
|
}
|
|
|
|
PixelBenderType::TInt3(i1, i2, i3) => {
|
|
|
|
(vec![*i1 as f32, *i2 as f32, *i3 as f32, 0.0], false)
|
|
|
|
}
|
|
|
|
PixelBenderType::TInt4(i1, i2, i3, i4) => {
|
|
|
|
(vec![*i1 as f32, *i2 as f32, *i3 as f32, *i4 as f32], false)
|
|
|
|
}
|
|
|
|
PixelBenderType::TFloat2x2(arr) => (arr.to_vec(), true),
|
|
|
|
PixelBenderType::TFloat3x3(arr) => {
|
|
|
|
// Add a zero after every 3 values to created zero-padded vec4s
|
|
|
|
let mut vec4_arr = Vec::with_capacity(16);
|
|
|
|
for (i, val) in arr.iter().enumerate() {
|
|
|
|
vec4_arr.push(*val);
|
|
|
|
if i % 3 == 2 {
|
|
|
|
vec4_arr.push(0.0);
|
|
|
|
}
|
2023-06-15 22:50:24 +00:00
|
|
|
}
|
2023-07-01 17:50:43 +00:00
|
|
|
(vec4_arr, true)
|
|
|
|
}
|
|
|
|
PixelBenderType::TFloat4x4(arr) => (arr.to_vec(), true),
|
|
|
|
_ => unreachable!("Unimplemented value {value:?}"),
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
value_vec.len() % 4,
|
|
|
|
0,
|
|
|
|
"value_vec should represent concatenated vec4fs"
|
|
|
|
);
|
|
|
|
let num_vec4s = value_vec.len() / 4;
|
|
|
|
// Both float32 and int are 4 bytes
|
|
|
|
let component_size_bytes = 4;
|
|
|
|
|
|
|
|
let (buffer, vec4_count) = if is_float {
|
|
|
|
let res = (&compiled_shader.float_parameters_buffer, float_offset);
|
|
|
|
float_offset += num_vec4s;
|
|
|
|
res
|
|
|
|
} else {
|
|
|
|
let res = (&compiled_shader.int_parameters_buffer, int_offset);
|
|
|
|
int_offset += num_vec4s;
|
|
|
|
res
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut buffer_slice = staging_belt.write_buffer(
|
|
|
|
render_command_encoder,
|
|
|
|
buffer,
|
|
|
|
vec4_count as u64 * 4 * component_size_bytes,
|
|
|
|
NonZeroU64::new(value_vec.len() as u64 * component_size_bytes).unwrap(),
|
|
|
|
&descriptors.device,
|
|
|
|
);
|
|
|
|
buffer_slice.copy_from_slice(bytemuck::cast_slice(&value_vec));
|
2023-06-15 22:50:24 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-01 17:50:43 +00:00
|
|
|
}
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
// This needs to be a separate loop, so that we can get references into `texture_views`
|
|
|
|
for input in &arguments {
|
|
|
|
match input {
|
|
|
|
PixelBenderShaderArgument::ImageInput { index, texture, .. } => {
|
|
|
|
let wgpu_texture = image_input_as_texture(texture.as_ref().unwrap());
|
|
|
|
|
|
|
|
if first_image.is_none() {
|
|
|
|
first_image = Some(wgpu_texture);
|
2023-06-15 22:50:24 +00:00
|
|
|
}
|
2023-07-01 17:50:43 +00:00
|
|
|
|
|
|
|
let binding = naga_pixelbender::TEXTURE_START_BIND_INDEX + *index as u32;
|
|
|
|
bind_group_entries.push(BindGroupEntry {
|
|
|
|
binding,
|
|
|
|
resource: BindingResource::TextureView(&texture_views[index]),
|
|
|
|
});
|
2023-06-15 22:50:24 +00:00
|
|
|
}
|
2023-07-01 17:50:43 +00:00
|
|
|
PixelBenderShaderArgument::ValueInput { .. } => {}
|
2023-06-15 22:50:24 +00:00
|
|
|
}
|
2023-07-01 17:50:43 +00:00
|
|
|
}
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
let bind_group = descriptors
|
|
|
|
.device
|
|
|
|
.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
|
|
label: None,
|
|
|
|
layout: &compiled_shader.bind_group_layout,
|
|
|
|
entries: &bind_group_entries,
|
|
|
|
});
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
staging_belt.finish();
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
let vertices = source.vertices(&descriptors.device);
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
let mut render_pass = render_command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
|
|
label: Some("PixelBender render pass"),
|
|
|
|
color_attachments: &[color_attachment],
|
|
|
|
depth_stencil_attachment: None,
|
|
|
|
});
|
|
|
|
render_pass.set_bind_group(0, &bind_group, &[]);
|
|
|
|
render_pass.set_pipeline(&compiled_shader.pipeline);
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
render_pass.set_vertex_buffer(0, vertices.slice(..));
|
|
|
|
render_pass.set_index_buffer(
|
|
|
|
descriptors.quad.indices.slice(..),
|
|
|
|
wgpu::IndexFormat::Uint32,
|
|
|
|
);
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
render_pass.draw_indexed(0..6, 0, 0..1);
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
// Note - we just drop the staging belt, instead of recalling it,
|
|
|
|
// since we're not going to use it again.
|
2023-06-15 22:50:24 +00:00
|
|
|
|
2023-07-01 17:50:43 +00:00
|
|
|
Ok(())
|
2023-06-15 22:50:24 +00:00
|
|
|
}
|