Skip to main content

wgpu_native_texture_interop/raw_gl/
producer.rs

1//! A `FrameProducer` for raw GL applications that don't use surfman.
2//!
3//! This allows any application with a GL context and a framebuffer to import
4//! its rendered content into a host wgpu texture through the standard
5//! `FrameProducer` / `WgpuTextureImporter` pipeline.
6
7use std::ffi::c_void;
8use std::rc::Rc;
9use std::sync::Arc;
10
11use dpi::PhysicalSize;
12
13use crate::{
14    FrameProducer, GlFramebufferSource, GlFramebufferSourceImpl, HostWgpuContext, ImportOptions,
15    ImportedTexture, InteropError, NativeFrame, NativeFrameKind, ProducerCapabilities,
16    SyncMechanism, TextureOrigin, UnsupportedReason,
17};
18
19/// A frame producer for raw GL applications (no surfman dependency).
20///
21/// Wraps a GL context and framebuffer ID, producing frames that can be
22/// imported into wgpu textures via [`crate::WgpuTextureImporter`].
23///
24/// # Platform support
25///
26/// - **Linux/Android**: Imports via Vulkan external memory (requires Vulkan host backend)
27/// - **Windows**: Imports via Vulkan external memory with NT handles (requires Vulkan host backend)
28/// - **Apple**: Not supported (use `raw_gl::metal::MetalImporter` with IOSurface instead)
29///
30/// # Example
31///
32/// ```ignore
33/// use std::sync::Arc;
34/// use wgpu_native_texture_interop::raw_gl::producer::RawGlFrameProducer;
35/// use wgpu_native_texture_interop::{WgpuTextureImporter, HostWgpuContext, ImportOptions};
36/// use dpi::PhysicalSize;
37///
38/// let host = HostWgpuContext::new(device, queue);
39/// let importer = WgpuTextureImporter::new(host);
40///
41/// let mut producer = RawGlFrameProducer::new(
42///     gl,
43///     |name| std::ptr::null(), // replace with eglGetProcAddress or equivalent
44///     7, // your GL FBO id
45///     PhysicalSize::new(1920, 1080),
46/// );
47///
48/// let frame = producer.acquire_frame().unwrap();
49/// let imported = importer.import_frame(&frame, &ImportOptions::default()).unwrap();
50/// // Use imported.texture in your wgpu render pipeline
51/// ```
52pub struct RawGlFrameProducer {
53    gl: Arc<glow::Context>,
54    proc_loader: Rc<dyn Fn(&str) -> *const c_void>,
55    source_fbo: u32,
56    size: PhysicalSize<u32>,
57    generation: u64,
58}
59
60impl RawGlFrameProducer {
61    /// Create a new raw GL frame producer.
62    ///
63    /// # Arguments
64    ///
65    /// * `gl` - A glow GL context for the producer's GL state
66    /// * `proc_loader` - Function to load GL extension entry points (e.g. wrapping
67    ///   `eglGetProcAddress`, `wglGetProcAddress`, or equivalent)
68    /// * `source_fbo` - The GL framebuffer object ID to read from (0 for default framebuffer)
69    /// * `size` - Current framebuffer dimensions
70    pub fn new(
71        gl: Arc<glow::Context>,
72        proc_loader: impl Fn(&str) -> *const c_void + 'static,
73        source_fbo: u32,
74        size: PhysicalSize<u32>,
75    ) -> Self {
76        Self {
77            gl,
78            proc_loader: Rc::new(proc_loader),
79            source_fbo,
80            size,
81            generation: 0,
82        }
83    }
84
85    /// Update the source FBO to read from.
86    pub fn set_source_fbo(&mut self, fbo: u32) {
87        self.source_fbo = fbo;
88    }
89
90    /// Update the framebuffer dimensions (e.g. after a resize).
91    pub fn set_size(&mut self, size: PhysicalSize<u32>) {
92        self.size = size;
93    }
94}
95
96impl FrameProducer for RawGlFrameProducer {
97    fn capabilities(&self) -> ProducerCapabilities {
98        ProducerCapabilities {
99            supported_frames: vec![NativeFrameKind::GlFramebufferSource],
100        }
101    }
102
103    fn acquire_frame(&mut self) -> Result<NativeFrame, InteropError> {
104        self.generation += 1;
105
106        Ok(NativeFrame::GlFramebufferSource(GlFramebufferSource::new(
107            self.size,
108            self.generation,
109            SyncMechanism::None,
110            Rc::new(RawGlImportImpl {
111                gl: self.gl.clone(),
112                proc_loader: self.proc_loader.clone(),
113                source_fbo: self.source_fbo,
114                size: self.size,
115                generation: self.generation,
116            }),
117        )))
118    }
119}
120
121#[allow(dead_code)]
122struct RawGlImportImpl {
123    gl: Arc<glow::Context>,
124    proc_loader: Rc<dyn Fn(&str) -> *const c_void>,
125    source_fbo: u32,
126    size: PhysicalSize<u32>,
127    generation: u64,
128}
129
130impl GlFramebufferSourceImpl for RawGlImportImpl {
131    fn import_into(
132        &self,
133        _frame: &GlFramebufferSource,
134        host: &HostWgpuContext,
135        _options: &ImportOptions,
136    ) -> Result<ImportedTexture, InteropError> {
137        let texture = self.import_texture(host)?;
138
139        Ok(ImportedTexture {
140            texture,
141            format: wgpu::TextureFormat::Rgba8Unorm,
142            size: self.size,
143            origin: TextureOrigin::TopLeft,
144            generation: self.generation,
145            consumer_sync: SyncMechanism::ImplicitGlFlush,
146        })
147    }
148}
149
150impl RawGlImportImpl {
151    #[allow(unused_variables)]
152    fn import_texture(&self, host: &HostWgpuContext) -> Result<wgpu::Texture, InteropError> {
153        #[cfg(any(target_os = "linux", target_os = "android"))]
154        if host.backend == crate::InteropBackend::Vulkan {
155            return super::linux::import_gl_framebuffer_vulkan(
156                &self.gl,
157                &|name| (self.proc_loader)(name),
158                self.source_fbo,
159                self.size,
160                host,
161            );
162        }
163
164        #[cfg(target_os = "windows")]
165        if host.backend == crate::InteropBackend::Vulkan {
166            return super::windows::import_gl_framebuffer_vulkan_win32(
167                &self.gl,
168                &|name| (self.proc_loader)(name),
169                self.source_fbo,
170                self.size,
171                host,
172            );
173        }
174
175        #[cfg(target_os = "windows")]
176        if host.backend == crate::InteropBackend::Dx12 {
177            return super::dx12::import_gl_framebuffer_dx12(
178                &self.gl,
179                &|name| (self.proc_loader)(name),
180                self.source_fbo,
181                self.size,
182                host,
183            );
184        }
185
186        Err(InteropError::Unsupported(
187            UnsupportedReason::HostBackendMismatch,
188        ))
189    }
190}