wgpu_native_texture_interop/raw_gl/
linux.rs1use ash::vk;
10use dpi::PhysicalSize;
11use glow::HasContext;
12use std::ffi::c_void;
13
14use crate::{HostWgpuContext, InteropError, gl_bindings as gl};
15
16pub fn import_gl_framebuffer_vulkan(
29 gl: &glow::Context,
30 gl_extension_loader: &dyn Fn(&str) -> *const c_void,
31 source_fbo: u32,
32 size: PhysicalSize<u32>,
33 host: &HostWgpuContext,
34) -> Result<wgpu::Texture, InteropError> {
35 use gl::Gles2 as Gl;
36
37 unsafe {
38 let hal_device = host.device.as_hal::<wgpu::wgc::api::Vulkan>().ok_or(
39 InteropError::BackendMismatch {
40 expected: "Vulkan",
41 actual: "non-Vulkan",
42 },
43 )?;
44 let vulkan_device = hal_device.raw_device().clone();
45 let vulkan_instance = hal_device.shared_instance().raw_instance();
46 let physical_device = hal_device.raw_physical_device();
47
48 let mut external_memory_image_info = vk::ExternalMemoryImageCreateInfo::default()
49 .handle_types(vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD);
50
51 let vulkan_image = vulkan_device
52 .create_image(
53 &vk::ImageCreateInfo::default()
54 .image_type(vk::ImageType::TYPE_2D)
55 .format(vk::Format::R8G8B8A8_UNORM)
56 .extent(vk::Extent3D {
57 width: size.width,
58 height: size.height,
59 depth: 1,
60 })
61 .mip_levels(1)
62 .array_layers(1)
63 .samples(vk::SampleCountFlags::TYPE_1)
64 .tiling(vk::ImageTiling::OPTIMAL)
65 .usage(vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::COLOR_ATTACHMENT)
66 .sharing_mode(vk::SharingMode::EXCLUSIVE)
67 .initial_layout(vk::ImageLayout::UNDEFINED)
68 .push_next(&mut external_memory_image_info),
69 None,
70 )
71 .map_err(|err| InteropError::Vulkan(err.to_string()))?;
72
73 let memory_requirements = vulkan_device.get_image_memory_requirements(vulkan_image);
74 let memory_properties =
75 vulkan_instance.get_physical_device_memory_properties(physical_device);
76 let memory_type_index = memory_properties.memory_types
77 [..memory_properties.memory_type_count as usize]
78 .iter()
79 .enumerate()
80 .position(|(i, mem_type)| {
81 (memory_requirements.memory_type_bits & (1 << i)) != 0
82 && mem_type
83 .property_flags
84 .contains(vk::MemoryPropertyFlags::DEVICE_LOCAL)
85 })
86 .ok_or_else(|| {
87 InteropError::Vulkan("no DEVICE_LOCAL memory type compatible with image".into())
88 })? as u32;
89
90 let mut dedicated_allocate_info =
91 vk::MemoryDedicatedAllocateInfo::default().image(vulkan_image);
92 let mut export_info = vk::ExportMemoryAllocateInfo::default()
93 .handle_types(vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD);
94
95 let memory = vulkan_device
96 .allocate_memory(
97 &vk::MemoryAllocateInfo::default()
98 .allocation_size(memory_requirements.size)
99 .memory_type_index(memory_type_index)
100 .push_next(&mut dedicated_allocate_info)
101 .push_next(&mut export_info),
102 None,
103 )
104 .map_err(|err| InteropError::Vulkan(err.to_string()))?;
105
106 vulkan_device
107 .bind_image_memory(vulkan_image, memory, 0)
108 .map_err(|err| InteropError::Vulkan(err.to_string()))?;
109
110 let external_memory_fd_api =
111 ash::khr::external_memory_fd::Device::new(&vulkan_instance, &vulkan_device);
112 let memory_handle = external_memory_fd_api
113 .get_memory_fd(
114 &vk::MemoryGetFdInfoKHR::default()
115 .memory(memory)
116 .handle_type(vk::ExternalMemoryHandleTypeFlags::OPAQUE_FD),
117 )
118 .map_err(|err| InteropError::Vulkan(err.to_string()))?;
119
120 let gl_with_extensions = Gl::load_with(|function_name| gl_extension_loader(function_name));
121
122 if !gl_with_extensions.CreateMemoryObjectsEXT.is_loaded()
123 || !gl_with_extensions.ImportMemoryFdEXT.is_loaded()
124 || !gl_with_extensions.TexStorageMem2DEXT.is_loaded()
125 {
126 vulkan_device.destroy_image(vulkan_image, None);
127 vulkan_device.free_memory(memory, None);
128 return Err(InteropError::OpenGl(
129 "GL_EXT_memory_object_fd not available — the GL driver does not \
130 support external memory extensions"
131 .into(),
132 ));
133 }
134
135 let mut memory_object = 0;
136 gl_with_extensions.CreateMemoryObjectsEXT(1, &mut memory_object);
137 gl_with_extensions.MemoryObjectParameterivEXT(
138 memory_object,
139 gl::DEDICATED_MEMORY_OBJECT_EXT,
140 &1,
141 );
142 gl_with_extensions.ImportMemoryFdEXT(
143 memory_object,
144 memory_requirements.size,
145 gl::HANDLE_TYPE_OPAQUE_FD_EXT,
146 memory_handle,
147 );
148
149 let texture = gl.create_texture().map_err(InteropError::OpenGl)?;
150 gl.bind_texture(gl::TEXTURE_2D, Some(texture));
151 gl_with_extensions.TexStorageMem2DEXT(
152 gl::TEXTURE_2D,
153 1,
154 gl::RGBA8,
155 size.width as i32,
156 size.height as i32,
157 memory_object,
158 0,
159 );
160
161 let draw_framebuffer = gl.create_framebuffer().map_err(InteropError::OpenGl)?;
162
163 let read_framebuffer = if source_fbo == 0 {
164 None
165 } else {
166 Some(glow::NativeFramebuffer(
167 std::num::NonZeroU32::new(source_fbo)
168 .ok_or(InteropError::InvalidFrame("invalid FBO id"))?,
169 ))
170 };
171
172 gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, Some(draw_framebuffer));
173 gl.framebuffer_texture_2d(
174 gl::DRAW_FRAMEBUFFER,
175 gl::COLOR_ATTACHMENT0,
176 gl::TEXTURE_2D,
177 Some(texture),
178 0,
179 );
180
181 gl.bind_framebuffer(gl::READ_FRAMEBUFFER, read_framebuffer);
182 gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, Some(draw_framebuffer));
183 gl.blit_framebuffer(
184 0,
185 0,
186 size.width as i32,
187 size.height as i32,
188 0,
189 size.height as i32,
190 size.width as i32,
191 0,
192 gl::COLOR_BUFFER_BIT,
193 gl::NEAREST,
194 );
195 gl.flush();
196 gl.delete_framebuffer(draw_framebuffer);
197 gl.delete_texture(texture);
198 gl_with_extensions.DeleteMemoryObjectsEXT(1, &memory_object);
199
200 let imported = host
201 .device
202 .create_texture_from_hal::<wgpu::wgc::api::Vulkan>(
203 hal_device.texture_from_raw(
204 vulkan_image,
205 &wgpu_hal::TextureDescriptor {
206 label: None,
207 size: wgpu::Extent3d {
208 width: size.width,
209 height: size.height,
210 depth_or_array_layers: 1,
211 },
212 format: wgpu::TextureFormat::Rgba8Unorm,
213 dimension: wgpu::TextureDimension::D2,
214 mip_level_count: 1,
215 sample_count: 1,
216 usage: wgpu::TextureUses::RESOURCE | wgpu::TextureUses::COLOR_TARGET,
217 view_formats: Vec::new(),
218 memory_flags: wgpu_hal::MemoryFlags::empty(),
219 },
220 Some(Box::new(move || {
221 vulkan_device.destroy_image(vulkan_image, None);
222 vulkan_device.free_memory(memory, None);
223 })),
224 wgpu_hal::vulkan::TextureMemory::External,
225 ),
226 &wgpu::TextureDescriptor {
227 label: Some("gl-frame-vulkan-import"),
228 size: wgpu::Extent3d {
229 width: size.width,
230 height: size.height,
231 depth_or_array_layers: 1,
232 },
233 format: wgpu::TextureFormat::Rgba8Unorm,
234 dimension: wgpu::TextureDimension::D2,
235 mip_level_count: 1,
236 sample_count: 1,
237 usage: wgpu::TextureUsages::TEXTURE_BINDING
238 | wgpu::TextureUsages::RENDER_ATTACHMENT,
239 view_formats: &[],
240 },
241 );
242
243 Ok(imported)
244 }
245}