mirror of
https://github.com/AxioDL/metaforce.git
synced 2025-12-16 04:57:01 +00:00
Working movie player
This commit is contained in:
@@ -9,28 +9,34 @@ use aabb::queue_aabb;
|
||||
use bytemuck::Pod;
|
||||
use bytemuck_derive::{Pod, Zeroable};
|
||||
use cxx::{type_id, ExternType};
|
||||
use cxx::private::hash;
|
||||
use fog_volume_filter::queue_fog_volume_filter;
|
||||
use fog_volume_plane::queue_fog_volume_plane;
|
||||
use model::{add_material_set, add_model};
|
||||
use texture::{create_render_texture, create_static_texture_2d, drop_texture};
|
||||
use textured_quad::{queue_textured_quad_verts, queue_textured_quad};
|
||||
use movie_player::queue_movie_player;
|
||||
use texture::{
|
||||
create_dynamic_texture_2d, create_render_texture, create_static_texture_2d, drop_texture,
|
||||
write_texture,
|
||||
};
|
||||
use textured_quad::{queue_textured_quad, queue_textured_quad_verts};
|
||||
use twox_hash::Xxh3Hash64;
|
||||
use wgpu::RenderPipeline;
|
||||
|
||||
use crate::{
|
||||
gpu::GraphicsConfig,
|
||||
shaders::{
|
||||
ffi::{TextureFormat, TextureRef},
|
||||
texture::{RenderTexture, TextureWithView},
|
||||
},
|
||||
zeus::{CColor, CMatrix4f, CRectangle, CVector2f, CVector3f, IDENTITY_MATRIX4F},
|
||||
};
|
||||
use crate::shaders::ffi::{TextureFormat, TextureRef};
|
||||
use crate::shaders::texture::{RenderTexture, TextureWithView};
|
||||
|
||||
mod aabb;
|
||||
mod fog_volume_filter;
|
||||
mod fog_volume_plane;
|
||||
mod model;
|
||||
mod textured_quad;
|
||||
mod movie_player;
|
||||
mod texture;
|
||||
mod textured_quad;
|
||||
|
||||
#[cxx::bridge]
|
||||
mod ffi {
|
||||
@@ -203,6 +209,14 @@ mod ffi {
|
||||
rect: CRectangle,
|
||||
z: f32,
|
||||
);
|
||||
fn queue_movie_player(
|
||||
tex_y: TextureRef,
|
||||
tex_u: TextureRef,
|
||||
tex_v: TextureRef,
|
||||
color: CColor,
|
||||
h_pad: f32,
|
||||
v_pad: f32,
|
||||
);
|
||||
|
||||
fn create_static_texture_2d(
|
||||
width: u32,
|
||||
@@ -212,6 +226,13 @@ mod ffi {
|
||||
data: &[u8],
|
||||
label: &str,
|
||||
) -> TextureRef;
|
||||
fn create_dynamic_texture_2d(
|
||||
width: u32,
|
||||
height: u32,
|
||||
mips: u32,
|
||||
format: TextureFormat,
|
||||
label: &str,
|
||||
) -> TextureRef;
|
||||
fn create_render_texture(
|
||||
width: u32,
|
||||
height: u32,
|
||||
@@ -220,6 +241,7 @@ mod ffi {
|
||||
depth_bind_count: u32,
|
||||
label: &str,
|
||||
) -> TextureRef;
|
||||
fn write_texture(handle: TextureRef, data: &[u8]);
|
||||
fn drop_texture(handle: TextureRef);
|
||||
}
|
||||
}
|
||||
@@ -310,7 +332,7 @@ enum ShaderDrawCommand {
|
||||
game_blend_mode: u32,
|
||||
model_flags: u32,
|
||||
},
|
||||
MoviePlayer {/* TODO */},
|
||||
MoviePlayer(movie_player::DrawData),
|
||||
NES {/* TODO */},
|
||||
ParticleSwoosh {/* TODO */},
|
||||
PhazonSuitFilter {/* TODO */},
|
||||
@@ -347,6 +369,7 @@ struct RenderState {
|
||||
// Shader states
|
||||
aabb: aabb::State,
|
||||
textured_quad: textured_quad::State,
|
||||
movie_player: movie_player::State,
|
||||
}
|
||||
pub(crate) fn construct_state(
|
||||
device: Arc<wgpu::Device>,
|
||||
@@ -370,6 +393,7 @@ pub(crate) fn construct_state(
|
||||
};
|
||||
let aabb = aabb::construct_state(&device, &queue, &buffers);
|
||||
let textured_quad = textured_quad::construct_state(&device, &queue, &buffers, graphics_config);
|
||||
let movie_player = movie_player::construct_state(&device, &queue, &buffers, graphics_config);
|
||||
let mut state = RenderState {
|
||||
device: device.clone(),
|
||||
queue: queue.clone(),
|
||||
@@ -384,6 +408,7 @@ pub(crate) fn construct_state(
|
||||
render_textures: Default::default(),
|
||||
aabb,
|
||||
textured_quad,
|
||||
movie_player,
|
||||
};
|
||||
for config in aabb::INITIAL_PIPELINES {
|
||||
construct_pipeline(&mut state, config);
|
||||
@@ -486,6 +511,7 @@ struct PipelineRef {
|
||||
pub(crate) enum PipelineCreateCommand {
|
||||
Aabb(aabb::PipelineConfig),
|
||||
TexturedQuad(textured_quad::PipelineConfig),
|
||||
MoviePlayer(movie_player::PipelineConfig),
|
||||
}
|
||||
#[inline(always)]
|
||||
fn hash_with_seed<T: Hash>(value: &T, seed: u64) -> u64 {
|
||||
@@ -497,6 +523,7 @@ fn construct_pipeline(state: &mut RenderState, cmd: &PipelineCreateCommand) -> u
|
||||
let id = match cmd {
|
||||
PipelineCreateCommand::Aabb(ref config) => hash_with_seed(config, 0xAABB),
|
||||
PipelineCreateCommand::TexturedQuad(ref config) => hash_with_seed(config, 0xEEAD),
|
||||
PipelineCreateCommand::MoviePlayer(ref config) => hash_with_seed(config, 0xFAAE),
|
||||
};
|
||||
if !state.pipelines.contains_key(&id) {
|
||||
let pipeline = match cmd {
|
||||
@@ -512,6 +539,12 @@ fn construct_pipeline(state: &mut RenderState, cmd: &PipelineCreateCommand) -> u
|
||||
&state.textured_quad,
|
||||
config,
|
||||
),
|
||||
PipelineCreateCommand::MoviePlayer(ref config) => movie_player::construct_pipeline(
|
||||
state.device.as_ref(),
|
||||
&state.graphics_config,
|
||||
&state.movie_player,
|
||||
config,
|
||||
),
|
||||
};
|
||||
state.pipelines.insert(id, pipeline);
|
||||
}
|
||||
@@ -566,7 +599,20 @@ pub(crate) fn render_into_pass(pass: &mut wgpu::RenderPass) {
|
||||
aabb::draw_aabb(data, &state.aabb, pass, &state.buffers);
|
||||
}
|
||||
ShaderDrawCommand::TexturedQuad(data) => {
|
||||
textured_quad::draw_textured_quad(data, &state.textured_quad, pass, &state.buffers);
|
||||
textured_quad::draw_textured_quad(
|
||||
data,
|
||||
&state.textured_quad,
|
||||
pass,
|
||||
&state.buffers,
|
||||
);
|
||||
}
|
||||
ShaderDrawCommand::MoviePlayer(data) => {
|
||||
movie_player::draw_movie_player(
|
||||
data,
|
||||
&state.movie_player,
|
||||
pass,
|
||||
&state.buffers,
|
||||
);
|
||||
}
|
||||
_ => todo!(),
|
||||
},
|
||||
|
||||
301
Graphics/src/shaders/movie_player/mod.rs
Normal file
301
Graphics/src/shaders/movie_player/mod.rs
Normal file
@@ -0,0 +1,301 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
hash::{Hash, Hasher},
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
use bytemuck_derive::{Pod, Zeroable};
|
||||
use wgpu::{include_wgsl, vertex_attr_array};
|
||||
|
||||
use crate::{
|
||||
get_app,
|
||||
gpu::GraphicsConfig,
|
||||
shaders::{
|
||||
bind_pipeline,
|
||||
ffi::{CameraFilterType, TextureRef, ZTest},
|
||||
pipeline_ref, push_draw_command, push_uniform, push_verts,
|
||||
texture::create_sampler,
|
||||
BuiltBuffers, PipelineCreateCommand, PipelineHolder, PipelineRef, ShaderDrawCommand, STATE,
|
||||
},
|
||||
util::{align, Vec2, Vec3},
|
||||
zeus::{CColor, CMatrix4f, CRectangle, CVector4f},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct DrawData {
|
||||
pipeline: PipelineRef,
|
||||
vert_range: Range<u64>,
|
||||
uniform_range: Range<u64>,
|
||||
bind_group_id: u32,
|
||||
}
|
||||
|
||||
#[derive(Hash)]
|
||||
pub(crate) struct PipelineConfig {
|
||||
// nothing
|
||||
}
|
||||
pub(crate) const INITIAL_PIPELINES: &[PipelineCreateCommand] =
|
||||
&[PipelineCreateCommand::MoviePlayer(PipelineConfig {})];
|
||||
|
||||
pub(crate) struct State {
|
||||
shader: wgpu::ShaderModule,
|
||||
uniform_layout: wgpu::BindGroupLayout,
|
||||
uniform_bind_group: wgpu::BindGroup,
|
||||
texture_layout: wgpu::BindGroupLayout,
|
||||
sampler: wgpu::Sampler,
|
||||
pipeline_layout: wgpu::PipelineLayout,
|
||||
// Transient state
|
||||
texture_bind_groups: HashMap<u32, wgpu::BindGroup>,
|
||||
frame_used_textures: Vec<u32>, // TODO use to clear bind groups
|
||||
}
|
||||
|
||||
pub(crate) fn construct_state(
|
||||
device: &wgpu::Device,
|
||||
_queue: &wgpu::Queue,
|
||||
buffers: &BuiltBuffers,
|
||||
graphics_config: &GraphicsConfig,
|
||||
) -> State {
|
||||
let shader = device.create_shader_module(&include_wgsl!("shader.wgsl"));
|
||||
let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment;
|
||||
let uniform_size = wgpu::BufferSize::new(align(
|
||||
std::mem::size_of::<Uniform>() as u64,
|
||||
uniform_alignment as u64,
|
||||
));
|
||||
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("Textured Quad Uniform Bind Group Layout"),
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: true,
|
||||
min_binding_size: uniform_size,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
});
|
||||
let sampler = create_sampler(device, wgpu::AddressMode::Repeat, None);
|
||||
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("Textured Quad Uniform Bind Group"),
|
||||
layout: &uniform_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||
buffer: &buffers.uniform_buffer,
|
||||
offset: 0, // dynamic
|
||||
size: uniform_size,
|
||||
}),
|
||||
},
|
||||
wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler) },
|
||||
],
|
||||
});
|
||||
let texture_binding = wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
};
|
||||
let texture_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: Some("Textured Quad Texture Bind Group Layout"),
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: texture_binding,
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: texture_binding,
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: texture_binding,
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
});
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Textured Quad Pipeline Layout"),
|
||||
bind_group_layouts: &[&uniform_layout, &texture_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
State {
|
||||
shader,
|
||||
uniform_layout,
|
||||
uniform_bind_group,
|
||||
texture_layout,
|
||||
sampler,
|
||||
pipeline_layout,
|
||||
texture_bind_groups: Default::default(),
|
||||
frame_used_textures: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn construct_pipeline(
|
||||
device: &wgpu::Device,
|
||||
graphics: &GraphicsConfig,
|
||||
state: &State,
|
||||
config: &PipelineConfig,
|
||||
) -> PipelineHolder {
|
||||
PipelineHolder {
|
||||
pipeline: device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Textured Quad Pipeline"),
|
||||
layout: Some(&state.pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &state.shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Vert>() as u64,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &vertex_attr_array![0 => Float32x3, 1 => Float32x2],
|
||||
}],
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: graphics.depth_format,
|
||||
depth_write_enabled: false,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: Default::default(),
|
||||
bias: Default::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: graphics.msaa_samples,
|
||||
..Default::default()
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &state.shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[wgpu::ColorTargetState {
|
||||
format: graphics.color_format,
|
||||
blend: Some(wgpu::BlendState {
|
||||
color: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
alpha: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
}),
|
||||
write_mask: wgpu::ColorWrites::COLOR,
|
||||
}],
|
||||
}),
|
||||
multiview: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
||||
#[repr(C)]
|
||||
struct Uniform {
|
||||
xf: CMatrix4f,
|
||||
color: CColor,
|
||||
}
|
||||
|
||||
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
||||
#[repr(C)]
|
||||
struct Vert {
|
||||
pos: Vec3<f32>,
|
||||
uv: Vec2<f32>,
|
||||
}
|
||||
|
||||
pub(crate) fn queue_movie_player(
|
||||
tex_y: TextureRef,
|
||||
tex_u: TextureRef,
|
||||
tex_v: TextureRef,
|
||||
color: CColor,
|
||||
h_pad: f32,
|
||||
v_pad: f32,
|
||||
) {
|
||||
let pipeline = pipeline_ref(&PipelineCreateCommand::MoviePlayer(PipelineConfig {}));
|
||||
let vert_range = push_verts(&[
|
||||
Vert { pos: Vec3::new(-h_pad, v_pad, 0.0), uv: Vec2::new(0.0, 0.0) },
|
||||
Vert { pos: Vec3::new(-h_pad, -v_pad, 0.0), uv: Vec2::new(0.0, 1.0) },
|
||||
Vert { pos: Vec3::new(h_pad, v_pad, 0.0), uv: Vec2::new(1.0, 0.0) },
|
||||
Vert { pos: Vec3::new(h_pad, -v_pad, 0.0), uv: Vec2::new(1.0, 1.0) },
|
||||
]);
|
||||
let uniform_range = push_uniform(&Uniform { xf: CMatrix4f::default(), color });
|
||||
|
||||
// TODO defer bind group creation to draw time or another thread?
|
||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
||||
let groups = &mut state.movie_player.texture_bind_groups;
|
||||
let mut hasher = twox_hash::XxHash32::default();
|
||||
tex_y.id.hash(&mut hasher);
|
||||
tex_u.id.hash(&mut hasher);
|
||||
tex_v.id.hash(&mut hasher);
|
||||
let bind_group_id = hasher.finish() as u32;
|
||||
if !groups.contains_key(&bind_group_id) {
|
||||
let tex_y = state.textures.get(&tex_y.id).unwrap();
|
||||
let tex_u = state.textures.get(&tex_u.id).unwrap();
|
||||
let tex_v = state.textures.get(&tex_v.id).unwrap();
|
||||
let bind_group = get_app().gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &state.movie_player.texture_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&tex_y.view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&tex_u.view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::TextureView(&tex_v.view),
|
||||
},
|
||||
],
|
||||
});
|
||||
groups.insert(bind_group_id, bind_group);
|
||||
}
|
||||
|
||||
push_draw_command(ShaderDrawCommand::MoviePlayer(DrawData {
|
||||
pipeline,
|
||||
vert_range,
|
||||
uniform_range,
|
||||
bind_group_id,
|
||||
}));
|
||||
}
|
||||
|
||||
pub(crate) fn draw_movie_player<'a>(
|
||||
data: &DrawData,
|
||||
state: &'a State,
|
||||
pass: &mut wgpu::RenderPass<'a>,
|
||||
buffers: &'a BuiltBuffers,
|
||||
) {
|
||||
if !bind_pipeline(data.pipeline, pass) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Uniform bind group
|
||||
pass.set_bind_group(0, &state.uniform_bind_group, &[
|
||||
data.uniform_range.start as wgpu::DynamicOffset
|
||||
]);
|
||||
|
||||
// Texture bind group
|
||||
let texture_bind_group = state
|
||||
.texture_bind_groups
|
||||
.get(&data.bind_group_id)
|
||||
.expect("Failed to find texture bind group");
|
||||
pass.set_bind_group(1, texture_bind_group, &[]);
|
||||
|
||||
// Vertex buffer
|
||||
pass.set_vertex_buffer(0, buffers.vertex_buffer.slice(data.vert_range.clone()));
|
||||
pass.draw(0..4, 0..1);
|
||||
}
|
||||
42
Graphics/src/shaders/movie_player/shader.wgsl
Normal file
42
Graphics/src/shaders/movie_player/shader.wgsl
Normal file
@@ -0,0 +1,42 @@
|
||||
struct Uniform {
|
||||
xf: mat4x4<f32>;
|
||||
color: vec4<f32>;
|
||||
};
|
||||
@group(0) @binding(0)
|
||||
var<uniform> ubuf: Uniform;
|
||||
@group(0) @binding(1)
|
||||
var tex_sampler: sampler;
|
||||
@group(1) @binding(0)
|
||||
var tex_y: texture_2d<f32>;
|
||||
@group(1) @binding(1)
|
||||
var tex_u: texture_2d<f32>;
|
||||
@group(1) @binding(2)
|
||||
var tex_v: texture_2d<f32>;
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) pos: vec4<f32>;
|
||||
@location(0) uv: vec2<f32>;
|
||||
};
|
||||
|
||||
@stage(vertex)
|
||||
fn vs_main(@location(0) in_pos: vec3<f32>, @location(1) in_uv: vec2<f32>) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.pos = ubuf.xf * vec4<f32>(in_pos, 1.0);
|
||||
out.uv = in_uv;
|
||||
return out;
|
||||
}
|
||||
|
||||
@stage(fragment)
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
var yuv = vec3<f32>(
|
||||
1.1643 * (textureSample(tex_y, tex_sampler, in.uv).x - 0.0625),
|
||||
textureSample(tex_u, tex_sampler, in.uv).x - 0.5,
|
||||
textureSample(tex_v, tex_sampler, in.uv).x - 0.5
|
||||
);
|
||||
return ubuf.color * vec4<f32>(
|
||||
yuv.x + 1.5958 * yuv.z,
|
||||
yuv.x - 0.39173 * yuv.y - 0.8129 * yuv.z,
|
||||
yuv.x + 2.017 * yuv.y,
|
||||
1.0
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::num::NonZeroU8;
|
||||
use std::{
|
||||
collections::hash_map::Entry::{Occupied, Vacant},
|
||||
hash::{Hash, Hasher},
|
||||
num::{NonZeroU32, NonZeroU8},
|
||||
};
|
||||
|
||||
use twox_hash::XxHash32;
|
||||
use wgpu::util::DeviceExt;
|
||||
use wgpu::{util::DeviceExt, ImageDataLayout};
|
||||
|
||||
use crate::{
|
||||
get_app,
|
||||
@@ -15,11 +18,13 @@ use crate::{
|
||||
pub(crate) struct TextureWithView {
|
||||
pub(crate) texture: wgpu::Texture,
|
||||
pub(crate) view: wgpu::TextureView,
|
||||
pub(crate) format: wgpu::TextureFormat,
|
||||
pub(crate) extent: wgpu::Extent3d,
|
||||
}
|
||||
impl TextureWithView {
|
||||
fn new(texture: wgpu::Texture) -> Self {
|
||||
fn new(texture: wgpu::Texture, format: wgpu::TextureFormat, extent: wgpu::Extent3d) -> Self {
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
Self { texture, view }
|
||||
Self { texture, view, format, extent }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,24 +42,26 @@ pub(crate) fn create_static_texture_2d(
|
||||
label: &str,
|
||||
) -> TextureRef {
|
||||
let gpu = &get_app().gpu;
|
||||
let extent = wgpu::Extent3d { width, height, depth_or_array_layers: 1 };
|
||||
let wgpu_format = match format {
|
||||
TextureFormat::RGBA8 => wgpu::TextureFormat::Rgba8Unorm,
|
||||
TextureFormat::R8 => wgpu::TextureFormat::R8Unorm,
|
||||
TextureFormat::R32Float => wgpu::TextureFormat::R32Float,
|
||||
TextureFormat::DXT1 => wgpu::TextureFormat::Bc1RgbaUnorm,
|
||||
TextureFormat::DXT3 => wgpu::TextureFormat::Bc3RgbaUnorm,
|
||||
TextureFormat::DXT5 => wgpu::TextureFormat::Bc5RgUnorm,
|
||||
TextureFormat::BPTC => wgpu::TextureFormat::Bc7RgbaUnorm,
|
||||
_ => todo!(),
|
||||
};
|
||||
let texture = gpu.device.create_texture_with_data(
|
||||
&gpu.queue,
|
||||
&wgpu::TextureDescriptor {
|
||||
label: Some(label),
|
||||
size: wgpu::Extent3d { width, height, depth_or_array_layers: 1 },
|
||||
size: extent,
|
||||
mip_level_count: mips,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: match format {
|
||||
TextureFormat::RGBA8 => wgpu::TextureFormat::Rgba8Unorm,
|
||||
TextureFormat::R8 => wgpu::TextureFormat::R8Unorm,
|
||||
TextureFormat::R32Float => wgpu::TextureFormat::R32Float,
|
||||
TextureFormat::DXT1 => wgpu::TextureFormat::Bc1RgbaUnorm,
|
||||
TextureFormat::DXT3 => wgpu::TextureFormat::Bc3RgbaUnorm,
|
||||
TextureFormat::DXT5 => wgpu::TextureFormat::Bc5RgUnorm,
|
||||
TextureFormat::BPTC => wgpu::TextureFormat::Bc7RgbaUnorm,
|
||||
_ => todo!(),
|
||||
},
|
||||
format: wgpu_format,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING,
|
||||
},
|
||||
data,
|
||||
@@ -71,7 +78,60 @@ pub(crate) fn create_static_texture_2d(
|
||||
|
||||
// Store texture and return reference
|
||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
||||
state.textures.insert(id, TextureWithView::new(texture));
|
||||
match state.textures.entry(id) {
|
||||
Occupied(entry) => panic!("Hash collision ({:x}) on texture creation: {}", id, label),
|
||||
Vacant(entry) => {
|
||||
state.textures.insert(id, TextureWithView::new(texture, wgpu_format, extent));
|
||||
}
|
||||
}
|
||||
TextureRef { id, render: false }
|
||||
}
|
||||
|
||||
pub(crate) fn create_dynamic_texture_2d(
|
||||
width: u32,
|
||||
height: u32,
|
||||
mips: u32,
|
||||
format: TextureFormat,
|
||||
label: &str,
|
||||
) -> TextureRef {
|
||||
let gpu = &get_app().gpu;
|
||||
let extent = wgpu::Extent3d { width, height, depth_or_array_layers: 1 };
|
||||
let wgpu_format = match format {
|
||||
TextureFormat::RGBA8 => wgpu::TextureFormat::Rgba8Unorm,
|
||||
TextureFormat::R8 => wgpu::TextureFormat::R8Unorm,
|
||||
TextureFormat::R32Float => wgpu::TextureFormat::R32Float,
|
||||
TextureFormat::DXT1 => wgpu::TextureFormat::Bc1RgbaUnorm,
|
||||
TextureFormat::DXT3 => wgpu::TextureFormat::Bc3RgbaUnorm,
|
||||
TextureFormat::DXT5 => wgpu::TextureFormat::Bc5RgUnorm,
|
||||
TextureFormat::BPTC => wgpu::TextureFormat::Bc7RgbaUnorm,
|
||||
_ => todo!(),
|
||||
};
|
||||
let texture = gpu.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some(label),
|
||||
size: extent,
|
||||
mip_level_count: mips,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu_format,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
});
|
||||
|
||||
// Generate texture hash as ID
|
||||
let mut hasher = XxHash32::with_seed(format.into());
|
||||
width.hash(&mut hasher);
|
||||
height.hash(&mut hasher);
|
||||
mips.hash(&mut hasher);
|
||||
label.hash(&mut hasher);
|
||||
let id = hasher.finish() as u32;
|
||||
|
||||
// Store texture and return reference
|
||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
||||
match state.textures.entry(id) {
|
||||
Occupied(entry) => panic!("Hash collision ({:x}) on texture creation: {}", id, label),
|
||||
Vacant(entry) => {
|
||||
state.textures.insert(id, TextureWithView::new(texture, wgpu_format, extent));
|
||||
}
|
||||
}
|
||||
TextureRef { id, render: false }
|
||||
}
|
||||
|
||||
@@ -85,28 +145,38 @@ pub(crate) fn create_render_texture(
|
||||
) -> TextureRef {
|
||||
let gpu = &get_app().gpu;
|
||||
let color_texture = if color_bind_count > 0 {
|
||||
Some(TextureWithView::new(gpu.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some(format!("{} Color", label).as_str()),
|
||||
size: wgpu::Extent3d { width, height, depth_or_array_layers: color_bind_count },
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: gpu.config.color_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
})))
|
||||
let extent = wgpu::Extent3d { width, height, depth_or_array_layers: color_bind_count };
|
||||
Some(TextureWithView::new(
|
||||
gpu.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some(format!("{} Color", label).as_str()),
|
||||
size: extent,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: gpu.config.color_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
}),
|
||||
gpu.config.color_format,
|
||||
extent,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let depth_texture = if depth_bind_count > 0 {
|
||||
Some(TextureWithView::new(gpu.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some(format!("{} Depth", label).as_str()),
|
||||
size: wgpu::Extent3d { width, height, depth_or_array_layers: depth_bind_count },
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: gpu.config.depth_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
})))
|
||||
let extent = wgpu::Extent3d { width, height, depth_or_array_layers: depth_bind_count };
|
||||
Some(TextureWithView::new(
|
||||
gpu.device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some(format!("{} Depth", label).as_str()),
|
||||
size: extent,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: gpu.config.depth_format,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
}),
|
||||
gpu.config.depth_format,
|
||||
extent,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -146,10 +216,46 @@ pub(crate) fn create_render_texture(
|
||||
|
||||
// Store texture and return reference
|
||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
||||
state.render_textures.insert(id, RenderTexture { color_texture, depth_texture });
|
||||
match state.textures.entry(id) {
|
||||
Occupied(entry) => panic!("Hash collision ({:x}) on texture creation: {}", id, label),
|
||||
Vacant(entry) => {
|
||||
state.render_textures.insert(id, RenderTexture { color_texture, depth_texture });
|
||||
}
|
||||
}
|
||||
TextureRef { id, render: true }
|
||||
}
|
||||
|
||||
pub(crate) fn write_texture(handle: TextureRef, data: &[u8]) {
|
||||
if handle.render {
|
||||
panic!("Can't write to render texture");
|
||||
}
|
||||
|
||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
||||
if let Some(TextureWithView { texture, format, extent, .. }) = state.textures.get(&handle.id) {
|
||||
state.queue.write_texture(
|
||||
texture.as_image_copy(),
|
||||
data,
|
||||
ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: match format {
|
||||
wgpu::TextureFormat::Rgba8Unorm | wgpu::TextureFormat::R32Float => {
|
||||
NonZeroU32::new(extent.width * 4)
|
||||
}
|
||||
wgpu::TextureFormat::R8Unorm => NonZeroU32::new(extent.width),
|
||||
_ => todo!("Unimplemented format for write_texture: {:?}", format),
|
||||
},
|
||||
rows_per_image: match extent.depth_or_array_layers {
|
||||
1 => None,
|
||||
_ => todo!("Unimplemented write_texture for 3D/2DArray textures"),
|
||||
},
|
||||
},
|
||||
*extent,
|
||||
);
|
||||
} else {
|
||||
panic!("Failed to find texture {}", handle.id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn drop_texture(handle: TextureRef) {
|
||||
let state = unsafe { STATE.as_mut().unwrap_unchecked() };
|
||||
if handle.render {
|
||||
|
||||
Reference in New Issue
Block a user