1mod generator;
2mod transform;
3mod vertex;
4
5use std::sync::Arc;
6
7use self::vertex::Vertex;
8
9pub use self::generator::{
10 GeneratorError, Texture, Texture2DBuffer, Texture2DRaw, Texture2DResource,
11};
12
13use common::{
14 frame::{VideoFormat, VideoSubFormat},
15 Size,
16};
17use generator::{Generator, GeneratorOptions};
18use pollster::FutureExt;
19use thiserror::Error;
20use wgpu::{
21 util::{BufferInitDescriptor, DeviceExt},
22 Backends, Buffer, BufferUsages, Color, CommandEncoderDescriptor, CompositeAlphaMode, Device,
23 DeviceDescriptor, IndexFormat, Instance, InstanceDescriptor, LoadOp, MemoryHints, Operations,
24 PowerPreference, PresentMode, Queue, RenderPassColorAttachment, RenderPassDescriptor,
25 RequestAdapterOptions, StoreOp, Surface, TextureFormat, TextureUsages, TextureViewDescriptor,
26};
27
28pub use wgpu::{rwh as raw_window_handle, SurfaceTarget};
29
30#[derive(Debug, Error)]
31pub enum GraphicsError {
32 #[error("not found graphics adaper")]
33 NotFoundAdapter,
34 #[error("not found graphics surface default config")]
35 NotFoundSurfaceDefaultConfig,
36 #[error(transparent)]
37 RequestDeviceError(#[from] wgpu::RequestDeviceError),
38 #[error(transparent)]
39 SurfaceGetTextureFailed(#[from] wgpu::SurfaceError),
40 #[error(transparent)]
41 CreateSurfaceError(#[from] wgpu::CreateSurfaceError),
42 #[error(transparent)]
43 GeneratorError(#[from] GeneratorError),
44}
45
46#[derive(Debug)]
47pub struct RendererSurfaceOptions<T> {
48 pub window: T,
49 pub size: Size,
50}
51
52#[derive(Debug)]
53pub struct RendererSourceOptions {
54 pub size: Size,
55 pub format: VideoFormat,
56 pub sub_format: VideoSubFormat,
57}
58
59#[derive(Debug)]
60pub struct RendererOptions<T> {
61 #[cfg(target_os = "windows")]
62 pub direct3d: common::win32::Direct3DDevice,
63 pub surface: RendererSurfaceOptions<T>,
64 pub source: RendererSourceOptions,
65}
66
67pub struct Renderer<'a> {
76 surface: Surface<'a>,
77 device: Arc<Device>,
78 queue: Arc<Queue>,
79 vertex_buffer: Buffer,
80 index_buffer: Buffer,
81 generator: Generator,
82}
83
84impl<'a> Renderer<'a> {
85 pub fn new<T: Into<SurfaceTarget<'a>>>(
86 RendererOptions {
87 #[cfg(target_os = "windows")]
88 direct3d,
89 surface: RendererSurfaceOptions { window, size },
90 source,
91 }: RendererOptions<T>,
92 ) -> Result<Self, GraphicsError> {
93 let instance = Instance::new(InstanceDescriptor {
94 backends: if cfg!(target_os = "windows") {
95 Backends::DX12
96 } else if cfg!(target_os = "linux") {
97 Backends::VULKAN
98 } else {
99 Backends::METAL
100 },
101 ..Default::default()
102 });
103
104 let surface = instance.create_surface(window)?;
105 let adapter = instance
106 .request_adapter(&RequestAdapterOptions {
107 power_preference: PowerPreference::LowPower,
108 force_fallback_adapter: false,
109 compatible_surface: Some(&surface),
110 ..Default::default()
111 })
112 .block_on()
113 .ok_or_else(|| GraphicsError::NotFoundAdapter)?;
114
115 let (device, queue) = adapter
116 .request_device(
117 &DeviceDescriptor {
118 label: None,
119 memory_hints: MemoryHints::MemoryUsage,
120 required_features: adapter.features(),
121 required_limits: adapter.limits(),
122 },
123 None,
124 )
125 .block_on()?;
126
127 let device = Arc::new(device);
128 let queue = Arc::new(queue);
129
130 {
133 let mut config = surface
134 .get_default_config(&adapter, size.width, size.height)
135 .ok_or_else(|| GraphicsError::NotFoundSurfaceDefaultConfig)?;
136
137 config.present_mode = if cfg!(target_os = "windows") {
138 PresentMode::Mailbox
139 } else if cfg!(target_os = "linux") {
140 PresentMode::Fifo
141 } else {
142 PresentMode::Immediate
143 };
144
145 config.format = TextureFormat::Bgra8Unorm;
146 config.alpha_mode = CompositeAlphaMode::Opaque;
147 config.usage = TextureUsages::RENDER_ATTACHMENT;
148 surface.configure(&device, &config);
149 };
150
151 let vertex_buffer = device.create_buffer_init(&BufferInitDescriptor {
152 label: None,
153 contents: bytemuck::cast_slice(Vertex::VERTICES),
154 usage: BufferUsages::VERTEX,
155 });
156
157 let index_buffer = device.create_buffer_init(&BufferInitDescriptor {
158 label: None,
159 contents: bytemuck::cast_slice(Vertex::INDICES),
160 usage: BufferUsages::INDEX,
161 });
162
163 Ok(Self {
164 generator: Generator::new(GeneratorOptions {
165 device: device.clone(),
166 queue: queue.clone(),
167 size: source.size,
168 format: source.format,
169 sub_format: source.sub_format,
170 #[cfg(target_os = "windows")]
171 direct3d,
172 })?,
173 vertex_buffer,
174 index_buffer,
175 surface,
176 device,
177 queue,
178 })
179 }
180
181 pub fn submit(&mut self, texture: Texture) -> Result<(), GraphicsError> {
186 let mut encoder = self
187 .device
188 .create_command_encoder(&CommandEncoderDescriptor { label: None });
189
190 let (pipeline, bind_group) = self.generator.get_view(&mut encoder, texture)?;
191 let output = self.surface.get_current_texture()?;
192 let view = output
193 .texture
194 .create_view(&TextureViewDescriptor::default());
195
196 {
197 let mut render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
198 color_attachments: &[Some(RenderPassColorAttachment {
199 view: &view,
200 resolve_target: None,
201 ops: Operations {
202 load: LoadOp::Clear(Color::BLACK),
203 store: StoreOp::Store,
204 },
205 })],
206 ..Default::default()
207 });
208
209 render_pass.set_pipeline(pipeline);
210 render_pass.set_bind_group(0, Some(&bind_group), &[]);
211 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
212 render_pass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint16);
213 render_pass.draw_indexed(0..Vertex::INDICES.len() as u32, 0, 0..1);
214 }
215
216 self.queue.submit(Some(encoder.finish()));
217 output.present();
218
219 Ok(())
220 }
221}
222
223#[cfg(target_os = "windows")]
224pub mod win32 {
225 use common::{
226 frame::VideoFormat,
227 win32::{
228 windows::Win32::{
229 Foundation::HWND,
230 Graphics::{
231 Direct3D11::{ID3D11RenderTargetView, ID3D11Texture2D, D3D11_VIEWPORT},
232 Dxgi::{
233 Common::DXGI_FORMAT_R8G8B8A8_UNORM, CreateDXGIFactory, IDXGIFactory,
234 IDXGISwapChain, DXGI_PRESENT, DXGI_SWAP_CHAIN_DESC,
235 DXGI_USAGE_RENDER_TARGET_OUTPUT,
236 },
237 },
238 },
239 Direct3DDevice,
240 },
241 };
242
243 use resample::win32::{Resource, VideoResampler, VideoResamplerOptions};
244 use thiserror::Error;
245 use wgpu::SurfaceTarget;
246
247 use crate::{RendererOptions, Texture, Texture2DRaw, Texture2DResource};
248
249 #[derive(Debug, Error)]
250 pub enum D3D11RendererError {
251 #[error(transparent)]
252 WindowsError(#[from] common::win32::windows::core::Error),
253 }
254
255 pub struct D3D11Renderer {
256 direct3d: Direct3DDevice,
257 swap_chain: IDXGISwapChain,
258 render_target_view: ID3D11RenderTargetView,
259 video_processor: VideoResampler,
260 }
261
262 unsafe impl Send for D3D11Renderer {}
263 unsafe impl Sync for D3D11Renderer {}
264
265 impl D3D11Renderer {
266 pub fn new<'a, T: Into<SurfaceTarget<'a>>>(
267 RendererOptions {
268 direct3d,
269 surface,
270 source,
271 }: RendererOptions<T>,
272 ) -> Result<Self, D3D11RendererError> {
273 let swap_chain = unsafe {
274 let dxgi_factory = CreateDXGIFactory::<IDXGIFactory>()?;
275
276 let mut desc = DXGI_SWAP_CHAIN_DESC::default();
277 desc.BufferCount = 1;
278 desc.BufferDesc.Width = surface.size.width;
279 desc.BufferDesc.Height = surface.size.height;
280 desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
281 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
282 desc.SampleDesc.Count = 1;
283 desc.Windowed = true.into();
284 desc.OutputWindow = match surface.window.into() {
285 SurfaceTarget::Window(window) => match window.window_handle().unwrap().as_raw()
286 {
287 crate::raw_window_handle::RawWindowHandle::Win32(window) => {
288 HWND(window.hwnd.get() as _)
289 }
290 _ => unimplemented!(
291 "what happened? why is the dx11 renderer enabled on linux?"
292 ),
293 },
294 _ => {
295 unimplemented!("the renderer does not support non-windowed render targets")
296 }
297 };
298
299 let mut swap_chain = None;
300 dxgi_factory
301 .CreateSwapChain(&direct3d.device, &desc, &mut swap_chain)
302 .ok()?;
303
304 swap_chain.unwrap()
305 };
306
307 let back_buffer = unsafe { swap_chain.GetBuffer::<ID3D11Texture2D>(0)? };
308 let render_target_view = unsafe {
309 let mut render_target_view = None;
310 direct3d.device.CreateRenderTargetView(
311 &back_buffer,
312 None,
313 Some(&mut render_target_view),
314 )?;
315
316 render_target_view.unwrap()
317 };
318
319 unsafe {
320 direct3d
321 .context
322 .OMSetRenderTargets(Some(&[Some(render_target_view.clone())]), None);
323 }
324
325 unsafe {
326 let mut vp = D3D11_VIEWPORT::default();
327 vp.Width = surface.size.width as f32;
328 vp.Height = surface.size.height as f32;
329 vp.MinDepth = 0.0;
330 vp.MaxDepth = 1.0;
331
332 direct3d.context.RSSetViewports(Some(&[vp]));
333 }
334
335 let video_processor = VideoResampler::new(VideoResamplerOptions {
336 direct3d: direct3d.clone(),
337 input: Resource::Default(source.format, source.size),
338 output: Resource::Texture(unsafe { swap_chain.GetBuffer::<ID3D11Texture2D>(0)? }),
339 })?;
340
341 Ok(Self {
342 video_processor,
343 render_target_view,
344 swap_chain,
345 direct3d,
346 })
347 }
348
349 pub fn submit(&mut self, texture: Texture) -> Result<(), D3D11RendererError> {
351 unsafe {
352 self.direct3d
353 .context
354 .ClearRenderTargetView(&self.render_target_view, &[0.0, 0.0, 0.0, 1.0]);
355 }
356
357 let format = match texture {
358 Texture::Nv12(_) => VideoFormat::NV12,
359 Texture::Rgba(_) => VideoFormat::RGBA,
360 Texture::Bgra(_) => VideoFormat::BGRA,
361 Texture::I420(_) => VideoFormat::I420,
362 };
363
364 let view = match texture {
365 Texture::Nv12(resource) | Texture::Rgba(resource) | Texture::Bgra(resource) => {
366 match resource {
367 Texture2DResource::Texture(texture) => match texture {
368 Texture2DRaw::ID3D11Texture2D(texture, index) => {
369 Some(self.video_processor.create_input_view(&texture, index)?)
370 }
371 },
372 Texture2DResource::Buffer(texture) => {
373 self.video_processor.update_input_from_buffer(
374 format,
375 texture.buffers,
376 texture.linesize,
377 )?;
378
379 None
380 }
381 }
382 }
383 Texture::I420(texture) => {
384 self.video_processor.update_input_from_buffer(
385 format,
386 texture.buffers,
387 texture.linesize,
388 )?;
389
390 None
391 }
392 };
393
394 self.video_processor.process(view)?;
395
396 unsafe {
397 self.swap_chain.Present(0, DXGI_PRESENT(0)).ok()?;
398 }
399
400 Ok(())
401 }
402 }
403}