Skip to content

Commit b6a2125

Browse files
authored
ohos: Add generic surface support (#310)
* ohos: Add generic surface support OpenHarmony API-12 adds compatibility for functions like `eglGetNativeClientBufferANDROID` so we can mostly use the same code as on android for the textures Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com> * ohos: dynamically link eglGetNativeClientBufferANDROID Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com> --------- Signed-off-by: Jonathan Schwender <jonathan.schwender@huawei.com>
1 parent 5a21df0 commit b6a2125

File tree

3 files changed

+248
-12
lines changed

3 files changed

+248
-12
lines changed

src/platform/egl/context.rs

-2
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,6 @@ impl Device {
212212
..
213213
}) => (egl_surface, egl_surface),
214214
Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read),
215-
#[cfg(android_platform)]
216215
Framebuffer::Surface(Surface {
217216
objects: SurfaceObjects::HardwareBuffer { .. },
218217
..
@@ -367,7 +366,6 @@ impl Device {
367366
..
368367
}) => (egl_surface, egl_surface),
369368
Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read),
370-
#[cfg(android_platform)]
371369
Framebuffer::Surface(Surface {
372370
objects: SurfaceObjects::HardwareBuffer { .. },
373371
..

src/platform/egl/ohos_ffi.rs

+85
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
#![allow(non_snake_case)]
44
#![allow(unused)]
55

6+
use std::ffi::c_void;
7+
8+
/// From `eglext.h` on OpenHarmony.
9+
pub(crate) const EGL_NATIVE_BUFFER_OHOS: u32 = 0x34E1;
10+
11+
use crate::egl::types::EGLClientBuffer;
12+
613
#[repr(C)]
714
pub struct NativeWindow {
815
_unused: [u8; 0],
@@ -45,3 +52,81 @@ extern "C" {
4552
...
4653
) -> i32;
4754
}
55+
56+
#[link(name = "EGL")]
57+
extern "C" {
58+
/// Get the native Client buffer
59+
///
60+
/// The extension function `eglGetNativeClientBufferANDROID` is available starting with OpenHarmony 5.0.
61+
/// Its availability is documented here: https://docs.openharmony.cn/pages/v5.0/en/application-dev/reference/native-lib/egl-symbol.md
62+
/// However it is not available in `EGL_EXTENSION_FUNCTIONS`, since `eglGetProcAddress()` does not find
63+
/// the function and returns NULL.
64+
pub(crate) fn eglGetNativeClientBufferANDROID(buffer: *const c_void) -> EGLClientBuffer;
65+
}
66+
67+
// Bindings to `native_buffer` components we use. Official Documentation:
68+
// https://docs.openharmony.cn/pages/v5.0/en/application-dev/graphics/native-buffer-guidelines.md
69+
70+
#[repr(C)]
71+
pub struct OH_NativeBuffer {
72+
_opaque: [u8; 0],
73+
}
74+
75+
#[repr(C)]
76+
#[derive(Debug, Clone)]
77+
pub struct OH_NativeBuffer_Config {
78+
/// Width in pixels
79+
pub width: i32,
80+
/// Height in pixels
81+
pub height: i32,
82+
/// One of PixelFormat
83+
pub format: OH_NativeBuffer_Format,
84+
/// Combination of buffer usage
85+
pub usage: OH_NativeBuffer_Usage,
86+
/// the stride of memory
87+
pub stride: i32,
88+
}
89+
90+
#[repr(transparent)]
91+
#[derive(Debug, Copy, Clone)]
92+
pub struct OH_NativeBuffer_Format(core::ffi::c_int);
93+
94+
impl OH_NativeBuffer_Format {
95+
/// RGBA8888 format
96+
pub const RGBA_8888: OH_NativeBuffer_Format = OH_NativeBuffer_Format(12);
97+
}
98+
99+
bitflags! {
100+
#[repr(transparent)]
101+
#[derive(Debug, Copy, Clone)]
102+
pub struct OH_NativeBuffer_Usage: core::ffi::c_int {
103+
/// CPU read buffer
104+
const CPU_READ = 1;
105+
/// CPU write memory
106+
const CPU_WRITE = 1 << 1;
107+
/// Direct memory access (DMA) buffer
108+
const MEM_DMA = 1 << 3;
109+
/// For GPU write case
110+
const HW_RENDER = 1 << 8;
111+
/// For GPU read case
112+
const HW_TEXTURE = 1 << 9;
113+
/// Often be mapped for direct CPU reads
114+
const CPU_READ_OFTEN = 1 << 16;
115+
/// 512 bytes alignment
116+
const ALIGNMENT_512 = 1 << 18;
117+
}
118+
}
119+
120+
#[link(name = "native_buffer")]
121+
extern "C" {
122+
/// Allocate an `OH_NativeBuffer`` that matches the passed config.
123+
///
124+
/// A new `OH_NativeBuffer` instance is created each time this function is called.
125+
/// NULL is returned if allocation fails.
126+
pub fn OH_NativeBuffer_Alloc(config: *const OH_NativeBuffer_Config) -> *mut OH_NativeBuffer;
127+
/// Decreases the reference count of a OH_NativeBuffer and, when the reference count reaches 0,
128+
/// destroys this OH_NativeBuffer.
129+
///
130+
/// Since API-9
131+
pub fn OH_NativeBuffer_Unreference(buffer: *mut OH_NativeBuffer) -> i32;
132+
}

src/platform/egl/surface/ohos_surface.rs

+163-10
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,42 @@ use euclid::default::Size2D;
1010
use log::info;
1111

1212
use crate::egl;
13-
use crate::egl::types::EGLSurface;
13+
use crate::egl::types::{EGLSurface, EGLint};
1414
use crate::gl;
1515
use crate::gl::types::{GLenum, GLuint};
16+
use crate::gl_utils;
17+
use crate::platform::egl::ohos_ffi::{eglGetNativeClientBufferANDROID, EGL_NATIVE_BUFFER_OHOS};
18+
use crate::platform::generic;
1619
use crate::platform::generic::egl::device::EGL_FUNCTIONS;
20+
use crate::platform::generic::egl::ffi::EGLImageKHR;
1721
use crate::platform::generic::egl::ffi::EGL_EXTENSION_FUNCTIONS;
22+
use crate::platform::generic::egl::ffi::EGL_IMAGE_PRESERVED_KHR;
1823
use crate::platform::generic::egl::ffi::EGL_NO_IMAGE_KHR;
24+
use crate::renderbuffers::Renderbuffers;
1925
use crate::{Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType};
2026

2127
use super::super::context::{Context, GL_FUNCTIONS};
2228
use super::super::device::Device;
2329
use super::super::ohos_ffi::{
24-
NativeWindowOperation, OHNativeWindow, OH_NativeWindow_NativeWindowHandleOpt,
30+
NativeWindowOperation, OHNativeWindow, OH_NativeBuffer, OH_NativeBuffer_Alloc,
31+
OH_NativeBuffer_Config, OH_NativeBuffer_Format, OH_NativeBuffer_Unreference,
32+
OH_NativeBuffer_Usage, OH_NativeWindow_NativeWindowHandleOpt,
2533
};
2634
use super::{Surface, SurfaceTexture};
2735

2836
const SURFACE_GL_TEXTURE_TARGET: GLenum = gl::TEXTURE_2D;
2937

3038
pub(crate) enum SurfaceObjects {
31-
Window { egl_surface: EGLSurface },
39+
HardwareBuffer {
40+
hardware_buffer: *mut OH_NativeBuffer,
41+
egl_image: EGLImageKHR,
42+
framebuffer_object: GLuint,
43+
texture_object: GLuint,
44+
renderbuffers: Renderbuffers,
45+
},
46+
Window {
47+
egl_surface: EGLSurface,
48+
},
3249
}
3350

3451
/// An OHOS native window.
@@ -58,10 +75,65 @@ impl Device {
5875

5976
fn create_generic_surface(
6077
&mut self,
61-
_context: &Context,
62-
_size: &Size2D<i32>,
78+
context: &Context,
79+
size: &Size2D<i32>,
6380
) -> Result<Surface, Error> {
64-
Err(Error::Unimplemented)
81+
let _guard = self.temporarily_make_context_current(context)?;
82+
83+
let usage = OH_NativeBuffer_Usage::HW_RENDER | OH_NativeBuffer_Usage::HW_TEXTURE;
84+
85+
let config = OH_NativeBuffer_Config {
86+
width: size.width,
87+
height: size.height,
88+
format: OH_NativeBuffer_Format::RGBA_8888,
89+
usage: usage,
90+
stride: 10, // used same magic number as android. I have no idea
91+
};
92+
93+
GL_FUNCTIONS.with(|gl| {
94+
unsafe {
95+
let hardware_buffer = OH_NativeBuffer_Alloc(&config as *const _);
96+
assert!(!hardware_buffer.is_null(), "Failed to create native buffer");
97+
98+
// Create an EGL image, and bind it to a texture.
99+
let egl_image = self.create_egl_image(context, hardware_buffer);
100+
101+
// Initialize and bind the image to the texture.
102+
let texture_object =
103+
generic::egl::surface::bind_egl_image_to_gl_texture(gl, egl_image);
104+
105+
// Create the framebuffer, and bind the texture to it.
106+
let framebuffer_object = gl_utils::create_and_bind_framebuffer(
107+
gl,
108+
SURFACE_GL_TEXTURE_TARGET,
109+
texture_object,
110+
);
111+
112+
// Bind renderbuffers as appropriate.
113+
let context_descriptor = self.context_descriptor(context);
114+
let context_attributes = self.context_descriptor_attributes(&context_descriptor);
115+
let renderbuffers = Renderbuffers::new(gl, size, &context_attributes);
116+
renderbuffers.bind_to_current_framebuffer(gl);
117+
118+
debug_assert_eq!(
119+
gl.CheckFramebufferStatus(gl::FRAMEBUFFER),
120+
gl::FRAMEBUFFER_COMPLETE
121+
);
122+
123+
Ok(Surface {
124+
size: *size,
125+
context_id: context.id,
126+
objects: SurfaceObjects::HardwareBuffer {
127+
hardware_buffer,
128+
egl_image,
129+
framebuffer_object,
130+
texture_object,
131+
renderbuffers,
132+
},
133+
destroyed: false,
134+
})
135+
}
136+
})
65137
}
66138

67139
unsafe fn create_window_surface(
@@ -110,14 +182,34 @@ impl Device {
110182
/// in another context.
111183
///
112184
/// Calling this method on a widget surface returns a `WidgetAttached` error.
113-
/// On OpenHarmony, currently only widget surfaces are implemented in surfman, so
114-
/// this method unconditionally returns the `WidgetAttached` error.
115185
pub fn create_surface_texture(
116186
&self,
117-
_context: &mut Context,
187+
context: &mut Context,
118188
surface: Surface,
119189
) -> Result<SurfaceTexture, (Error, Surface)> {
120-
Err((Error::WidgetAttached, surface))
190+
unsafe {
191+
match surface.objects {
192+
SurfaceObjects::Window { .. } => return Err((Error::WidgetAttached, surface)),
193+
SurfaceObjects::HardwareBuffer {
194+
hardware_buffer, ..
195+
} => GL_FUNCTIONS.with(|gl| {
196+
let _guard = match self.temporarily_make_context_current(context) {
197+
Ok(guard) => guard,
198+
Err(err) => return Err((err, surface)),
199+
};
200+
201+
let local_egl_image = self.create_egl_image(context, hardware_buffer);
202+
let texture_object =
203+
generic::egl::surface::bind_egl_image_to_gl_texture(gl, local_egl_image);
204+
Ok(SurfaceTexture {
205+
surface,
206+
local_egl_image,
207+
texture_object,
208+
phantom: PhantomData,
209+
})
210+
}),
211+
}
212+
}
121213
}
122214

123215
/// Displays the contents of a widget surface on screen.
@@ -138,6 +230,7 @@ impl Device {
138230
egl.SwapBuffers(self.egl_display, egl_surface);
139231
Ok(())
140232
}
233+
SurfaceObjects::HardwareBuffer { .. } => Err(Error::NoWidgetAttached),
141234
}
142235
})
143236
}
@@ -153,6 +246,34 @@ impl Device {
153246
Ok(())
154247
}
155248

249+
#[allow(non_snake_case)]
250+
unsafe fn create_egl_image(
251+
&self,
252+
_: &Context,
253+
hardware_buffer: *mut OH_NativeBuffer,
254+
) -> EGLImageKHR {
255+
let client_buffer = eglGetNativeClientBufferANDROID(hardware_buffer as *const _);
256+
assert!(!client_buffer.is_null());
257+
258+
// Create the EGL image.
259+
let egl_image_attributes = [
260+
EGL_IMAGE_PRESERVED_KHR as EGLint,
261+
egl::TRUE as EGLint,
262+
egl::NONE as EGLint,
263+
0,
264+
];
265+
let egl_image = (EGL_EXTENSION_FUNCTIONS.CreateImageKHR)(
266+
self.egl_display,
267+
egl::NO_CONTEXT,
268+
EGL_NATIVE_BUFFER_OHOS,
269+
client_buffer.cast_mut().cast(),
270+
egl_image_attributes.as_ptr(),
271+
);
272+
assert_ne!(egl_image, EGL_NO_IMAGE_KHR);
273+
info!("surfman created an EGL image succesfully!");
274+
egl_image
275+
}
276+
156277
/// Destroys a surface.
157278
///
158279
/// The supplied context must be the context the surface is associated with, or this returns
@@ -171,6 +292,34 @@ impl Device {
171292

172293
unsafe {
173294
match surface.objects {
295+
SurfaceObjects::HardwareBuffer {
296+
ref mut hardware_buffer,
297+
ref mut egl_image,
298+
ref mut framebuffer_object,
299+
ref mut texture_object,
300+
ref mut renderbuffers,
301+
} => {
302+
GL_FUNCTIONS.with(|gl| {
303+
gl.BindFramebuffer(gl::FRAMEBUFFER, 0);
304+
gl.DeleteFramebuffers(1, framebuffer_object);
305+
*framebuffer_object = 0;
306+
307+
renderbuffers.destroy(gl);
308+
309+
gl.DeleteTextures(1, texture_object);
310+
*texture_object = 0;
311+
312+
let egl_display = self.egl_display;
313+
let result =
314+
(EGL_EXTENSION_FUNCTIONS.DestroyImageKHR)(egl_display, *egl_image);
315+
assert_ne!(result, egl::FALSE);
316+
*egl_image = EGL_NO_IMAGE_KHR;
317+
318+
let res = OH_NativeBuffer_Unreference(*hardware_buffer);
319+
assert_eq!(res, 0, "OH_NativeBuffer_Unreference failed");
320+
*hardware_buffer = ptr::null_mut();
321+
});
322+
}
174323
SurfaceObjects::Window {
175324
ref mut egl_surface,
176325
} => EGL_FUNCTIONS.with(|egl| {
@@ -242,6 +391,9 @@ impl Device {
242391
id: surface.id(),
243392
context_id: surface.context_id,
244393
framebuffer_object: match surface.objects {
394+
SurfaceObjects::HardwareBuffer {
395+
framebuffer_object, ..
396+
} => framebuffer_object,
245397
SurfaceObjects::Window { .. } => 0,
246398
},
247399
}
@@ -267,6 +419,7 @@ impl NativeWidget {
267419
impl Surface {
268420
pub(super) fn id(&self) -> SurfaceID {
269421
match self.objects {
422+
SurfaceObjects::HardwareBuffer { egl_image, .. } => SurfaceID(egl_image as usize),
270423
SurfaceObjects::Window { egl_surface } => SurfaceID(egl_surface as usize),
271424
}
272425
}

0 commit comments

Comments
 (0)