feat: Get iced-video working
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
use crate::id;
|
||||
use iced_wgpu::primitive::Pipeline;
|
||||
use iced_wgpu::wgpu;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::{Arc, atomic::AtomicBool};
|
||||
use std::sync::{Arc, Mutex, atomic::AtomicBool};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VideoPrimitive {
|
||||
texture: wgpu::TextureView,
|
||||
ready: Arc<AtomicBool>,
|
||||
pub struct VideoFrame {
|
||||
pub id: id::Id,
|
||||
pub size: wgpu::Extent3d,
|
||||
pub ready: Arc<AtomicBool>,
|
||||
pub frame: Arc<Mutex<Vec<u8>>>,
|
||||
}
|
||||
impl iced_wgpu::Primitive for VideoPrimitive {
|
||||
|
||||
impl iced_wgpu::Primitive for VideoFrame {
|
||||
type Pipeline = VideoPipeline;
|
||||
|
||||
fn prepare(
|
||||
@@ -19,11 +23,102 @@ impl iced_wgpu::Primitive for VideoPrimitive {
|
||||
bounds: &iced_wgpu::core::Rectangle,
|
||||
viewport: &iced_wgpu::graphics::Viewport,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn draw(&self, _pipeline: &Self::Pipeline, _render_pass: &mut wgpu::RenderPass<'_>) -> bool {
|
||||
false
|
||||
let video = pipeline.videos.entry(self.id.clone()).or_insert_with(|| {
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("iced-video-texture"),
|
||||
size: self.size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: pipeline.format,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
view_formats: &[],
|
||||
});
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("iced-video-texture-bind-group"),
|
||||
layout: &pipeline.bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(
|
||||
&texture.create_view(&wgpu::TextureViewDescriptor::default()),
|
||||
),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&pipeline.sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
VideoTextures {
|
||||
id: self.id.clone(),
|
||||
texture,
|
||||
bind_group,
|
||||
ready: Arc::clone(&self.ready),
|
||||
}
|
||||
});
|
||||
// dbg!(&self.size, video.texture.size());
|
||||
if self.size != video.texture.size() {
|
||||
// Resize the texture if the size has changed.
|
||||
let new_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("iced-video-texture-resized"),
|
||||
size: self.size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: pipeline.format,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
view_formats: &[],
|
||||
});
|
||||
let new_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some("iced-video-texture-bind-group-resized"),
|
||||
layout: &pipeline.bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(
|
||||
&new_texture.create_view(&wgpu::TextureViewDescriptor::default()),
|
||||
),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&pipeline.sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
video.texture = new_texture;
|
||||
video.bind_group = new_bind_group;
|
||||
}
|
||||
// BUG: This causes a panic because the texture size is not correct for some reason.
|
||||
if video.ready.load(std::sync::atomic::Ordering::SeqCst) {
|
||||
let frame = self.frame.lock().expect("BUG: Mutex poisoned");
|
||||
if frame.len() != (4 * self.size.width * self.size.height) as usize {
|
||||
tracing::warn!(
|
||||
"Frame size mismatch: expected {}, got {}",
|
||||
4 * self.size.width * self.size.height,
|
||||
frame.len()
|
||||
);
|
||||
return;
|
||||
}
|
||||
queue.write_texture(
|
||||
wgpu::TexelCopyTextureInfo {
|
||||
texture: &video.texture,
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
&frame,
|
||||
wgpu::TexelCopyBufferLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(4 * video.texture.size().width),
|
||||
rows_per_image: Some(video.texture.size().height),
|
||||
},
|
||||
self.size,
|
||||
);
|
||||
video
|
||||
.ready
|
||||
.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
fn render(
|
||||
@@ -33,35 +128,41 @@ impl iced_wgpu::Primitive for VideoPrimitive {
|
||||
target: &wgpu::TextureView,
|
||||
clip_bounds: &iced_wgpu::core::Rectangle<u32>,
|
||||
) {
|
||||
if self.ready.load(std::sync::atomic::Ordering::SeqCst) {
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("iced-video-render-pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: target,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
depth_slice: None,
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
let Some(video) = pipeline.videos.get(&self.id) else {
|
||||
return;
|
||||
};
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("iced-video-render-pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: target,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||
r: 0.1,
|
||||
g: 0.2,
|
||||
b: 0.3,
|
||||
a: 1.0,
|
||||
}),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
depth_slice: None,
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&pipeline.pipeline);
|
||||
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||
render_pass.draw(0..3, 0..1);
|
||||
self.ready
|
||||
.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
render_pass.set_pipeline(&pipeline.pipeline);
|
||||
render_pass.set_bind_group(0, &video.bind_group, &[]);
|
||||
render_pass.draw(0..3, 0..1);
|
||||
// self.ready
|
||||
// .store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VideoTextures {
|
||||
id: u64,
|
||||
id: id::Id,
|
||||
texture: wgpu::Texture,
|
||||
bind_group: wgpu::BindGroup,
|
||||
ready: Arc<AtomicBool>,
|
||||
@@ -72,7 +173,8 @@ pub struct VideoPipeline {
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
bind_group_layout: wgpu::BindGroupLayout,
|
||||
sampler: wgpu::Sampler,
|
||||
videos: BTreeMap<u64, VideoTextures>,
|
||||
videos: BTreeMap<id::Id, VideoTextures>,
|
||||
format: wgpu::TextureFormat,
|
||||
}
|
||||
|
||||
pub trait HdrTextureFormatExt {
|
||||
@@ -171,6 +273,7 @@ impl Pipeline for VideoPipeline {
|
||||
pipeline,
|
||||
bind_group_layout,
|
||||
sampler,
|
||||
format,
|
||||
videos: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user