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