Skip to content

Commit 70df1b4

Browse files
committed
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>
1 parent d45f322 commit 70df1b4

File tree

3 files changed

+238
-12
lines changed

3 files changed

+238
-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

+67
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,70 @@ extern "C" {
4545
...
4646
) -> i32;
4747
}
48+
49+
// Bindings to `native_buffer` components we use. Official Documentation:
50+
// https://docs.openharmony.cn/pages/v5.0/en/application-dev/graphics/native-buffer-guidelines.md
51+
52+
#[repr(C)]
53+
pub struct OH_NativeBuffer {
54+
_opaque: [u8; 0],
55+
}
56+
57+
#[repr(C)]
58+
#[derive(Debug, Clone)]
59+
pub struct OH_NativeBuffer_Config {
60+
/// Width in pixels
61+
pub width: i32,
62+
/// Height in pixels
63+
pub height: i32,
64+
/// One of PixelFormat
65+
pub format: OH_NativeBuffer_Format,
66+
/// Combination of buffer usage
67+
pub usage: OH_NativeBuffer_Usage,
68+
/// the stride of memory
69+
pub stride: i32,
70+
}
71+
72+
#[repr(transparent)]
73+
#[derive(Debug, Copy, Clone)]
74+
pub struct OH_NativeBuffer_Format(core::ffi::c_int);
75+
76+
impl OH_NativeBuffer_Format {
77+
/// RGBA8888 format
78+
pub const RGBA_8888: OH_NativeBuffer_Format = OH_NativeBuffer_Format(12);
79+
}
80+
81+
bitflags! {
82+
#[repr(transparent)]
83+
#[derive(Debug, Copy, Clone)]
84+
pub struct OH_NativeBuffer_Usage: core::ffi::c_int {
85+
/// CPU read buffer
86+
const CPU_READ = 1;
87+
/// CPU write memory
88+
const CPU_WRITE = 1 << 1;
89+
/// Direct memory access (DMA) buffer
90+
const MEM_DMA = 1 << 3;
91+
/// For GPU write case
92+
const HW_RENDER = 1 << 8;
93+
/// For GPU read case
94+
const HW_TEXTURE = 1 << 9;
95+
/// Often be mapped for direct CPU reads
96+
const CPU_READ_OFTEN = 1 << 16;
97+
/// 512 bytes alignment
98+
const ALIGNMENT_512 = 1 << 18;
99+
}
100+
}
101+
102+
#[link(name = "native_buffer")]
103+
extern "C" {
104+
/// Allocate an `OH_NativeBuffer`` that matches the passed config.
105+
///
106+
/// A new `OH_NativeBuffer` instance is created each time this function is called.
107+
/// NULL is returned if allocation fails.
108+
pub fn OH_NativeBuffer_Alloc(config: *const OH_NativeBuffer_Config) -> *mut OH_NativeBuffer;
109+
/// Decreases the reference count of a OH_NativeBuffer and, when the reference count reaches 0,
110+
/// destroys this OH_NativeBuffer.
111+
///
112+
/// Since API-9
113+
pub fn OH_NativeBuffer_Unreference(buffer: *mut OH_NativeBuffer) -> i32;
114+
}

src/platform/egl/surface/ohos_surface.rs

+171-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::generic;
1618
use crate::platform::generic::egl::device::EGL_FUNCTIONS;
19+
use crate::platform::generic::egl::ffi::EGLImageKHR;
1720
use crate::platform::generic::egl::ffi::EGL_EXTENSION_FUNCTIONS;
21+
use crate::platform::generic::egl::ffi::EGL_IMAGE_PRESERVED_KHR;
22+
use crate::platform::generic::egl::ffi::EGL_NATIVE_BUFFER_ANDROID;
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,42 @@ 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+
// Get the native client buffer.
256+
// Note: `eglGetNativeClientBufferANDROID` is available starting with OpenHarmony 5.0.
257+
// Its availability is documented here: https://docs.openharmony.cn/pages/v5.0/en/application-dev/reference/native-lib/egl-symbol.md
258+
let eglGetNativeClientBufferANDROID =
259+
EGL_EXTENSION_FUNCTIONS.GetNativeClientBufferANDROID.expect(
260+
"Where's the `EGL_ANDROID_get_native_client_buffer` \
261+
extension?",
262+
);
263+
let client_buffer = eglGetNativeClientBufferANDROID(hardware_buffer as *const _);
264+
assert!(!client_buffer.is_null());
265+
266+
// Create the EGL image.
267+
let egl_image_attributes = [
268+
EGL_IMAGE_PRESERVED_KHR as EGLint,
269+
egl::TRUE as EGLint,
270+
egl::NONE as EGLint,
271+
0,
272+
];
273+
let egl_image = (EGL_EXTENSION_FUNCTIONS.CreateImageKHR)(
274+
self.egl_display,
275+
egl::NO_CONTEXT,
276+
EGL_NATIVE_BUFFER_ANDROID,
277+
client_buffer,
278+
egl_image_attributes.as_ptr(),
279+
);
280+
assert_ne!(egl_image, EGL_NO_IMAGE_KHR);
281+
info!("surfman created an EGL image succesfully!");
282+
egl_image
283+
}
284+
156285
/// Destroys a surface.
157286
///
158287
/// The supplied context must be the context the surface is associated with, or this returns
@@ -171,6 +300,34 @@ impl Device {
171300

172301
unsafe {
173302
match surface.objects {
303+
SurfaceObjects::HardwareBuffer {
304+
ref mut hardware_buffer,
305+
ref mut egl_image,
306+
ref mut framebuffer_object,
307+
ref mut texture_object,
308+
ref mut renderbuffers,
309+
} => {
310+
GL_FUNCTIONS.with(|gl| {
311+
gl.BindFramebuffer(gl::FRAMEBUFFER, 0);
312+
gl.DeleteFramebuffers(1, framebuffer_object);
313+
*framebuffer_object = 0;
314+
315+
renderbuffers.destroy(gl);
316+
317+
gl.DeleteTextures(1, texture_object);
318+
*texture_object = 0;
319+
320+
let egl_display = self.egl_display;
321+
let result =
322+
(EGL_EXTENSION_FUNCTIONS.DestroyImageKHR)(egl_display, *egl_image);
323+
assert_ne!(result, egl::FALSE);
324+
*egl_image = EGL_NO_IMAGE_KHR;
325+
326+
let res = OH_NativeBuffer_Unreference(*hardware_buffer);
327+
assert_eq!(res, 0, "OH_NativeBuffer_Unreference failed");
328+
*hardware_buffer = ptr::null_mut();
329+
});
330+
}
174331
SurfaceObjects::Window {
175332
ref mut egl_surface,
176333
} => EGL_FUNCTIONS.with(|egl| {
@@ -242,6 +399,9 @@ impl Device {
242399
id: surface.id(),
243400
context_id: surface.context_id,
244401
framebuffer_object: match surface.objects {
402+
SurfaceObjects::HardwareBuffer {
403+
framebuffer_object, ..
404+
} => framebuffer_object,
245405
SurfaceObjects::Window { .. } => 0,
246406
},
247407
}
@@ -267,6 +427,7 @@ impl NativeWidget {
267427
impl Surface {
268428
pub(super) fn id(&self) -> SurfaceID {
269429
match self.objects {
430+
SurfaceObjects::HardwareBuffer { egl_image, .. } => SurfaceID(egl_image as usize),
270431
SurfaceObjects::Window { egl_surface } => SurfaceID(egl_surface as usize),
271432
}
272433
}

0 commit comments

Comments
 (0)