diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7acfd130..43ed74b3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,6 +41,9 @@ jobs: - os: windows-latest target: "aarch64-pc-windows-msvc" rust: stable + - os: ubuntu-22.04 + target: "aarch64-unknown-linux-ohos" + rust: stable steps: - uses: actions/checkout@v4 - name: Install deps on linux diff --git a/surfman/Cargo.toml b/surfman/Cargo.toml index 55d20bf6..9b819800 100644 --- a/surfman/Cargo.toml +++ b/surfman/Cargo.toml @@ -40,7 +40,7 @@ log = "0.4" sparkle = { version = "0.1", optional = true } osmesa-sys = { version = "0.1", optional = true } rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true } -rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } +rwh_06 = { package = "raw-window-handle", version = "0.6.2", features = ["std"], optional = true } [dev-dependencies] clap = "2" @@ -61,11 +61,11 @@ mach2 = "0.4" metal = "0.24" objc = "0.2" -[target.'cfg(all(unix, not(any(target_os = "macos", target_os = "android"))))'.dependencies.wayland-sys] +[target.'cfg(all(unix, not(any(target_os = "macos", target_os = "android", target_env = "ohos"))))'.dependencies.wayland-sys] version = "0.30" features = ["client", "dlopen", "egl"] -[target.'cfg(all(unix, not(any(target_os = "macos", target_os = "android"))))'.dependencies.x11] +[target.'cfg(all(unix, not(any(target_os = "macos", target_os = "android", target_env = "ohos"))))'.dependencies.x11] version = "2.3.0" features = ["xlib"] optional = true diff --git a/surfman/build.rs b/surfman/build.rs index f0088384..b3504cb7 100644 --- a/surfman/build.rs +++ b/surfman/build.rs @@ -13,12 +13,13 @@ fn main() { cfg_aliases! { // Platforms android_platform: { target_os = "android" }, + ohos_platform: { target_env = "ohos" }, web_platform: { all(target_family = "wasm", target_os = "unknown") }, macos_platform: { target_os = "macos" }, ios_platform: { target_os = "ios" }, windows_platform: { target_os = "windows" }, apple: { any(target_os = "ios", target_os = "macos") }, - free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) }, + free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten"), not(ohos_platform)) }, // Native displays. x11_platform: { all(free_unix, feature = "sm-x11") }, @@ -36,11 +37,13 @@ fn main() { let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let target_family = env::var("CARGO_CFG_TARGET_FAMILY").ok(); + let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); // Generate EGL bindings. if target_os == "android" || (target_os == "windows" && cfg!(feature = "sm-angle")) + || target_env == "ohos" || target_family.as_ref().map_or(false, |f| f == "unix") { let mut file = File::create(dest.join("egl_bindings.rs")).unwrap(); @@ -49,7 +52,7 @@ fn main() { } // Generate GL bindings. - if target_os == "android" { + if target_os == "android" || target_env == "ohos" { let mut file = File::create(dest.join("gl_bindings.rs")).unwrap(); let registry = Registry::new(Api::Gles2, (3, 0), Profile::Core, Fallbacks::All, []); registry.write_bindings(StructGenerator, &mut file).unwrap(); diff --git a/surfman/src/context.rs b/surfman/src/context.rs index db87a6e8..ab8cba3a 100644 --- a/surfman/src/context.rs +++ b/surfman/src/context.rs @@ -72,12 +72,12 @@ impl ContextAttributes { } } -#[cfg(target_os = "android")] +#[cfg(any(target_os = "android", target_env = "ohos"))] pub(crate) fn current_context_uses_compatibility_profile(_gl: &Gl) -> bool { false } -#[cfg(not(target_os = "android"))] +#[cfg(not(any(target_os = "android", target_env = "ohos")))] #[allow(dead_code)] pub(crate) fn current_context_uses_compatibility_profile(gl: &Gl) -> bool { unsafe { diff --git a/surfman/src/lib.rs b/surfman/src/lib.rs index da31316e..d1de5fe5 100644 --- a/surfman/src/lib.rs +++ b/surfman/src/lib.rs @@ -55,9 +55,9 @@ pub use crate::surface::{SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType, Sys pub mod macros; -#[cfg(not(target_os = "android"))] +#[cfg(not(any(target_os = "android", target_env = "ohos")))] pub(crate) use crate::gl::Gl; -#[cfg(target_os = "android")] +#[cfg(any(target_os = "android", target_env = "ohos"))] pub(crate) use crate::gl::Gles2 as Gl; mod gl_utils; @@ -70,6 +70,7 @@ mod gl { #[cfg(any( target_os = "android", + target_env = "ohos", all(target_os = "windows", feature = "sm-angle"), unix ))] diff --git a/surfman/src/platform/android/ffi.rs b/surfman/src/platform/egl/android_ffi.rs similarity index 95% rename from surfman/src/platform/android/ffi.rs rename to surfman/src/platform/egl/android_ffi.rs index 8825c176..9a862032 100644 --- a/surfman/src/platform/android/ffi.rs +++ b/surfman/src/platform/egl/android_ffi.rs @@ -1,4 +1,4 @@ -// surfman/surfman/src/platform/android/ffi.rs +// surfman/surfman/src/platform/egl/android_ffi.rs use std::os::raw::c_int; diff --git a/surfman/src/platform/android/connection.rs b/surfman/src/platform/egl/connection.rs similarity index 72% rename from surfman/src/platform/android/connection.rs rename to surfman/src/platform/egl/connection.rs index 1fa8ef41..4605354b 100644 --- a/surfman/src/platform/android/connection.rs +++ b/surfman/src/platform/egl/connection.rs @@ -1,15 +1,19 @@ -// surfman/surfman/src/platform/android/connection.rs +// surfman/surfman/src/platform/egl/connection.rs // //! A no-op connection for Android. //! //! FIXME(pcwalton): Should this instead wrap `EGLDisplay`? Is that thread-safe on Android? use super::device::{Adapter, Device, NativeDevice}; -use super::ffi::ANativeWindow; use super::surface::NativeWidget; use crate::Error; use crate::GLApi; +#[cfg(android_platform)] +use super::android_ffi::ANativeWindow; +#[cfg(ohos_platform)] +use super::ohos_ffi::OHNativeWindow; + use euclid::default::Size2D; use std::os::raw::c_void; @@ -109,24 +113,34 @@ impl Connection { Ok(Connection) } + #[cfg(android_platform)] + fn create_native_widget_from_ptr_impl(raw: *mut c_void) -> NativeWidget { + NativeWidget { + native_window: raw as *mut ANativeWindow, + } + } + + #[cfg(ohos_platform)] + fn create_native_widget_from_ptr_impl(raw: *mut c_void) -> NativeWidget { + NativeWidget { + native_window: raw as *mut OHNativeWindow, + } + } + /// Create a native widget from a raw pointer pub unsafe fn create_native_widget_from_ptr( &self, raw: *mut c_void, _size: Size2D, ) -> NativeWidget { - NativeWidget { - native_window: raw as *mut ANativeWindow, - } + debug_assert!(!raw.is_null()); + Self::create_native_widget_from_ptr_impl(raw) } - /// Create a native widget type from the given `RawWindowHandle`. - #[cfg(feature = "sm-raw-window-handle-05")] + #[cfg(all(feature = "sm-raw-window-handle-05", android_platform))] #[inline] - pub fn create_native_widget_from_raw_window_handle( - &self, + fn create_native_widget_from_rwh_05_handle( raw_handle: rwh_05::RawWindowHandle, - _size: Size2D, ) -> Result { use rwh_05::RawWindowHandle::AndroidNdk; @@ -138,13 +152,29 @@ impl Connection { } } - /// Create a native widget type from the given `WindowHandle`. - #[cfg(feature = "sm-raw-window-handle-06")] + #[cfg(all(feature = "sm-raw-window-handle-05", ohos_platform))] #[inline] - pub fn create_native_widget_from_window_handle( + fn create_native_widget_from_rwh_05_handle( + _raw_handle: rwh_05::RawWindowHandle, + ) -> Result { + Err(Error::IncompatibleNativeWidget) + } + + /// Create a native widget type from the given `RawWindowHandle`. + #[cfg(feature = "sm-raw-window-handle-05")] + #[inline] + pub fn create_native_widget_from_raw_window_handle( &self, - handle: rwh_06::WindowHandle, + raw_handle: rwh_05::RawWindowHandle, _size: Size2D, + ) -> Result { + create_native_widget_from_rwh_05_handle(raw_handle) + } + + #[cfg(all(feature = "sm-raw-window-handle-06", android_platform))] + #[inline] + fn create_native_widget_from_rwh_06_handle( + handle: rwh_06::WindowHandle, ) -> Result { use rwh_06::RawWindowHandle::AndroidNdk; @@ -155,6 +185,32 @@ impl Connection { _ => Err(Error::IncompatibleNativeWidget), } } + + #[cfg(all(feature = "sm-raw-window-handle-06", ohos_platform))] + #[inline] + fn create_native_widget_from_rwh_06_handle( + handle: rwh_06::WindowHandle, + ) -> Result { + use rwh_06::RawWindowHandle::OhosNdk; + + match handle.as_raw() { + OhosNdk(handle) => Ok(NativeWidget { + native_window: handle.native_window.as_ptr().cast(), + }), + _ => Err(Error::IncompatibleNativeWidget), + } + } + + /// Create a native widget type from the given `WindowHandle`. + #[cfg(feature = "sm-raw-window-handle-06")] + #[inline] + pub fn create_native_widget_from_window_handle( + &self, + handle: rwh_06::WindowHandle, + _size: Size2D, + ) -> Result { + Self::create_native_widget_from_rwh_06_handle(handle) + } } impl NativeConnection { diff --git a/surfman/src/platform/android/context.rs b/surfman/src/platform/egl/context.rs similarity index 97% rename from surfman/src/platform/android/context.rs rename to surfman/src/platform/egl/context.rs index 6ca26840..bdd96176 100644 --- a/surfman/src/platform/android/context.rs +++ b/surfman/src/platform/egl/context.rs @@ -1,4 +1,4 @@ -// surfman/surfman/src/platform/android/context.rs +// surfman/surfman/src/platform/egl/context.rs // //! OpenGL rendering contexts. @@ -212,11 +212,12 @@ impl Device { .. }) => (egl_surface, egl_surface), Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read), + #[cfg(android_platform)] Framebuffer::Surface(Surface { objects: SurfaceObjects::HardwareBuffer { .. }, .. - }) - | Framebuffer::None => (context.pbuffer, context.pbuffer), + }) => (context.pbuffer, context.pbuffer), + Framebuffer::None => (context.pbuffer, context.pbuffer), }; EGL_FUNCTIONS.with(|egl| { @@ -366,11 +367,12 @@ impl Device { .. }) => (egl_surface, egl_surface), Framebuffer::External(ExternalEGLSurfaces { draw, read }) => (draw, read), + #[cfg(android_platform)] Framebuffer::Surface(Surface { objects: SurfaceObjects::HardwareBuffer { .. }, .. - }) - | Framebuffer::None => (context.pbuffer, context.pbuffer), + }) => (context.pbuffer, context.pbuffer), + Framebuffer::None => (context.pbuffer, context.pbuffer), }; NativeContext { diff --git a/surfman/src/platform/android/device.rs b/surfman/src/platform/egl/device.rs similarity index 98% rename from surfman/src/platform/android/device.rs rename to surfman/src/platform/egl/device.rs index a6964068..42210902 100644 --- a/surfman/src/platform/android/device.rs +++ b/surfman/src/platform/egl/device.rs @@ -1,4 +1,4 @@ -// surfman/surfman/src/platform/android/device.rs +// surfman/surfman/src/platform/egl/device.rs // //! A thread-local handle to the device. diff --git a/surfman/src/platform/android/mod.rs b/surfman/src/platform/egl/mod.rs similarity index 66% rename from surfman/src/platform/android/mod.rs rename to surfman/src/platform/egl/mod.rs index c23cad47..bdff8c7c 100644 --- a/surfman/src/platform/android/mod.rs +++ b/surfman/src/platform/egl/mod.rs @@ -1,4 +1,4 @@ -// surfman/surfman/src/platform/android/mod.rs +// surfman/surfman/src/platform/egl/mod.rs // //! Bindings to EGL on Android. @@ -7,7 +7,11 @@ pub mod context; pub mod device; pub mod surface; -mod ffi; +#[cfg(android_platform)] +mod android_ffi; + +#[cfg(ohos_platform)] +mod ohos_ffi; #[path = "../../implementation/mod.rs"] mod implementation; diff --git a/surfman/src/platform/egl/ohos_ffi.rs b/surfman/src/platform/egl/ohos_ffi.rs new file mode 100644 index 00000000..726b4931 --- /dev/null +++ b/surfman/src/platform/egl/ohos_ffi.rs @@ -0,0 +1,47 @@ +#![allow(non_camel_case_types)] +#![allow(non_upper_case_globals)] +#![allow(non_snake_case)] +#![allow(unused)] + +#[repr(C)] +pub struct NativeWindow { + _unused: [u8; 0], +} + +pub type OHNativeWindow = NativeWindow; + +#[repr(transparent)] +pub(crate) struct NativeWindowOperation(core::ffi::c_int); + +impl NativeWindowOperation { + pub const GET_BUFFER_GEOMETRY: Self = Self(1); +} + +/// According to the [native window guidelines], users need to link against +/// both the NDK and `native_window`. +/// [native window guidelines]: +#[link(name = "native_window")] +#[link(name = "ace_ndk.z")] +extern "C" { + /// Sets or obtains the attributes of a native window + /// + /// Can be used to query information like height and width. + /// See the official [Documentation] for detailed usage information. + /// + /// # Safety + /// + /// - The `window` handle must be valid. + /// - The variable arguments which must be passed to this function vary depending on the + /// value of `code`. + /// - For `NativeWindowOperation::GET_BUFFER_GEOMETRY` the function takes two output + /// i32 pointers, `height: *mut i32` and `width: *mut i32` which are passed as variadic + /// arguments. + /// + /// + /// [Documentation]: + pub(crate) fn OH_NativeWindow_NativeWindowHandleOpt( + window: *mut OHNativeWindow, + code: NativeWindowOperation, + ... + ) -> i32; +} diff --git a/surfman/src/platform/android/surface.rs b/surfman/src/platform/egl/surface/android_surface.rs similarity index 82% rename from surfman/src/platform/android/surface.rs rename to surfman/src/platform/egl/surface/android_surface.rs index d5b87711..6af938cc 100644 --- a/surfman/src/platform/android/surface.rs +++ b/surfman/src/platform/egl/surface/android_surface.rs @@ -1,15 +1,21 @@ -// surfman/surfman/src/platform/android/surface.rs +// surfman/surfman/src/platform/egl/android_surface.rs // //! Surface management for Android using the `GraphicBuffer` class and EGL. -use super::context::{Context, GL_FUNCTIONS}; -use super::device::Device; -use super::ffi::{AHardwareBuffer, AHardwareBuffer_Desc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE}; -use super::ffi::{AHardwareBuffer_allocate, AHardwareBuffer_release, ANativeWindow}; -use super::ffi::{ANativeWindow_getHeight, ANativeWindow_getWidth}; -use super::ffi::{AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, AHARDWAREBUFFER_USAGE_CPU_READ_NEVER}; -use super::ffi::{AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER}; -use crate::context::ContextID; +use super::super::android_ffi::{ + AHardwareBuffer, AHardwareBuffer_Desc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, +}; +use super::super::android_ffi::{AHardwareBuffer_allocate, AHardwareBuffer_release, ANativeWindow}; +use super::super::android_ffi::{ANativeWindow_getHeight, ANativeWindow_getWidth}; +use super::super::android_ffi::{ + AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, AHARDWAREBUFFER_USAGE_CPU_READ_NEVER, +}; +use super::super::android_ffi::{ + AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, +}; +use super::super::context::{Context, GL_FUNCTIONS}; +use super::super::device::Device; +use super::{Surface, SurfaceTexture}; use crate::egl; use crate::egl::types::{EGLSurface, EGLint}; use crate::gl; @@ -26,54 +32,11 @@ use crate::renderbuffers::Renderbuffers; use crate::{Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType, WindowingApiError}; use euclid::default::Size2D; -use std::fmt::{self, Debug, Formatter}; use std::marker::PhantomData; use std::os::raw::c_void; use std::ptr; -use std::thread; - -pub use crate::platform::generic::egl::context::ContextDescriptor; - -const SURFACE_GL_TEXTURE_TARGET: GLenum = gl::TEXTURE_2D; - -/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either -/// displayed in a native widget or bound to a texture for reading. -/// -/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a -/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, -/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a -/// widget but not bound to a texture. -/// -/// Surfaces are specific to a given context and cannot be rendered to from any context other than -/// the one they were created with. However, they can be *read* from any context on any thread (as -/// long as that context shares the same adapter and connection), by wrapping them in a -/// `SurfaceTexture`. -/// -/// Depending on the platform, each surface may be internally double-buffered. -/// -/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. -pub struct Surface { - pub(crate) context_id: ContextID, - pub(crate) size: Size2D, - pub(crate) objects: SurfaceObjects, - pub(crate) destroyed: bool, -} -/// Represents an OpenGL texture that wraps a surface. -/// -/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to -/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that -/// framebuffer). -/// -/// Surface textures are local to a context, but that context does not have to be the same context -/// as that associated with the underlying surface. The texture must be destroyed with the -/// `destroy_surface_texture()` method, or a panic will occur. -pub struct SurfaceTexture { - pub(crate) surface: Surface, - pub(crate) local_egl_image: EGLImageKHR, - pub(crate) texture_object: GLuint, - pub(crate) phantom: PhantomData<*const ()>, -} +const SURFACE_GL_TEXTURE_TARGET: GLenum = crate::gl::TEXTURE_2D; pub(crate) enum SurfaceObjects { HardwareBuffer { @@ -88,28 +51,6 @@ pub(crate) enum SurfaceObjects { }, } -unsafe impl Send for Surface {} - -impl Debug for Surface { - fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { - write!(formatter, "Surface({:x})", self.id().0) - } -} - -impl Drop for Surface { - fn drop(&mut self) { - if !self.destroyed && !thread::panicking() { - panic!("Should have destroyed the surface first with `destroy_surface()`!") - } - } -} - -impl Debug for SurfaceTexture { - fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - write!(f, "SurfaceTexture({:?})", self.surface) - } -} - /// An Android native window. pub struct NativeWidget { pub(crate) native_window: *mut ANativeWindow, @@ -480,7 +421,7 @@ impl NativeWidget { } impl Surface { - fn id(&self) -> SurfaceID { + pub(super) fn id(&self) -> SurfaceID { match self.objects { SurfaceObjects::HardwareBuffer { egl_image, .. } => SurfaceID(egl_image as usize), SurfaceObjects::Window { egl_surface } => SurfaceID(egl_surface as usize), diff --git a/surfman/src/platform/egl/surface/mod.rs b/surfman/src/platform/egl/surface/mod.rs new file mode 100644 index 00000000..020490ae --- /dev/null +++ b/surfman/src/platform/egl/surface/mod.rs @@ -0,0 +1,87 @@ +// surfman/surfman/src/platform/egl/surface/mod.rs +// +//! Surface management for Android and OpenHarmony using the `GraphicBuffer` class and EGL. + +use crate::context::ContextID; +use crate::gl::types::GLuint; +use crate::platform::generic::egl::ffi::EGLImageKHR; + +use euclid::default::Size2D; +use std::fmt::{self, Debug, Formatter}; +use std::marker::PhantomData; +use std::thread; + +pub use crate::platform::generic::egl::context::ContextDescriptor; + +#[cfg(android_platform)] +mod android_surface; + +#[cfg(android_platform)] +pub use android_surface::*; + +#[cfg(ohos_platform)] +mod ohos_surface; + +#[cfg(ohos_platform)] +pub use ohos_surface::*; + +/// Represents a hardware buffer of pixels that can be rendered to via the CPU or GPU and either +/// displayed in a native widget or bound to a texture for reading. +/// +/// Surfaces come in two varieties: generic and widget surfaces. Generic surfaces can be bound to a +/// texture but cannot be displayed in a widget (without using other APIs such as Core Animation, +/// DirectComposition, or XPRESENT). Widget surfaces are the opposite: they can be displayed in a +/// widget but not bound to a texture. +/// +/// Surfaces are specific to a given context and cannot be rendered to from any context other than +/// the one they were created with. However, they can be *read* from any context on any thread (as +/// long as that context shares the same adapter and connection), by wrapping them in a +/// `SurfaceTexture`. +/// +/// Depending on the platform, each surface may be internally double-buffered. +/// +/// Surfaces must be destroyed with the `destroy_surface()` method, or a panic will occur. +pub struct Surface { + pub(crate) context_id: ContextID, + pub(crate) size: Size2D, + pub(crate) objects: SurfaceObjects, + pub(crate) destroyed: bool, +} + +/// Represents an OpenGL texture that wraps a surface. +/// +/// Reading from the associated OpenGL texture reads from the surface. It is undefined behavior to +/// write to such a texture (e.g. by binding it to a framebuffer and rendering to that +/// framebuffer). +/// +/// Surface textures are local to a context, but that context does not have to be the same context +/// as that associated with the underlying surface. The texture must be destroyed with the +/// `destroy_surface_texture()` method, or a panic will occur. +pub struct SurfaceTexture { + pub(crate) surface: Surface, + pub(crate) local_egl_image: EGLImageKHR, + pub(crate) texture_object: GLuint, + pub(crate) phantom: PhantomData<*const ()>, +} + +unsafe impl Send for Surface {} + +impl Debug for Surface { + fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { + write!(formatter, "Surface({:x})", self.id().0) + } +} + +impl Drop for Surface { + fn drop(&mut self) { + if !self.destroyed && !thread::panicking() { + panic!("Should have destroyed the surface first with `destroy_surface()`!") + } + } +} + +impl Debug for SurfaceTexture { + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + write!(f, "SurfaceTexture({:?})", self.surface) + } +} diff --git a/surfman/src/platform/egl/surface/ohos_surface.rs b/surfman/src/platform/egl/surface/ohos_surface.rs new file mode 100644 index 00000000..76f85c59 --- /dev/null +++ b/surfman/src/platform/egl/surface/ohos_surface.rs @@ -0,0 +1,278 @@ +// surfman/surfman/src/platform/egl/ohos_surface.rs +// +//! Surface management for OpenHarmony OS using EGL. + +use std::marker::PhantomData; +use std::os::raw::c_void; +use std::ptr; + +use euclid::default::Size2D; +use log::info; + +use crate::egl; +use crate::egl::types::EGLSurface; +use crate::gl; +use crate::gl::types::{GLenum, GLuint}; +use crate::platform::generic::egl::device::EGL_FUNCTIONS; +use crate::platform::generic::egl::ffi::EGL_EXTENSION_FUNCTIONS; +use crate::platform::generic::egl::ffi::EGL_NO_IMAGE_KHR; +use crate::{Error, SurfaceAccess, SurfaceID, SurfaceInfo, SurfaceType}; + +use super::super::context::{Context, GL_FUNCTIONS}; +use super::super::device::Device; +use super::super::ohos_ffi::{ + NativeWindowOperation, OHNativeWindow, OH_NativeWindow_NativeWindowHandleOpt, +}; +use super::{Surface, SurfaceTexture}; + +const SURFACE_GL_TEXTURE_TARGET: GLenum = gl::TEXTURE_2D; + +pub(crate) enum SurfaceObjects { + Window { egl_surface: EGLSurface }, +} + +/// An OHOS native window. +pub struct NativeWidget { + pub(crate) native_window: *mut OHNativeWindow, +} + +impl Device { + /// Creates either a generic or a widget surface, depending on the supplied surface type. + /// + /// Only the given context may ever render to the surface, but generic surfaces can be wrapped + /// up in a `SurfaceTexture` for reading by other contexts. + pub fn create_surface( + &mut self, + context: &Context, + _: SurfaceAccess, + surface_type: SurfaceType, + ) -> Result { + info!("Device create_surface with Context"); + match surface_type { + SurfaceType::Generic { size } => self.create_generic_surface(context, &size), + SurfaceType::Widget { native_widget } => unsafe { + self.create_window_surface(context, native_widget) + }, + } + } + + fn create_generic_surface( + &mut self, + _context: &Context, + _size: &Size2D, + ) -> Result { + Err(Error::Unimplemented) + } + + unsafe fn create_window_surface( + &mut self, + context: &Context, + native_widget: NativeWidget, + ) -> Result { + let mut height: i32 = 0; + let mut width: i32 = 0; + // Safety: `OH_NativeWindow_NativeWindowHandleOpt` takes two output i32 pointers as + // variable arguments when called with `GET_BUFFER_GEOMETRY`. + let result = unsafe { + OH_NativeWindow_NativeWindowHandleOpt( + native_widget.native_window, + NativeWindowOperation::GET_BUFFER_GEOMETRY, + &mut height as *mut i32, + &mut width as *mut i32, + ) + }; + assert_eq!(result, 0, "Failed to determine size of native window"); + EGL_FUNCTIONS.with(|egl| { + let egl_surface = egl.CreateWindowSurface( + self.egl_display, + self.context_to_egl_config(context), + native_widget.native_window as *const c_void, + ptr::null(), + ); + assert_ne!(egl_surface, egl::NO_SURFACE); + + Ok(Surface { + context_id: context.id, + size: Size2D::new(width, height), + objects: SurfaceObjects::Window { egl_surface }, + destroyed: false, + }) + }) + } + + /// Creates a surface texture from an existing generic surface for use with the given context. + /// + /// The surface texture is local to the supplied context and takes ownership of the surface. + /// Destroying the surface texture allows you to retrieve the surface again. + /// + /// *The supplied context does not have to be the same context that the surface is associated + /// with.* This allows you to render to a surface in one context and sample from that surface + /// in another context. + /// + /// Calling this method on a widget surface returns a `WidgetAttached` error. + /// On OpenHarmony, currently only widget surfaces are implemented in surfman, so + /// this method unconditionally returns the `WidgetAttached` error. + pub fn create_surface_texture( + &self, + _context: &mut Context, + surface: Surface, + ) -> Result { + Err((Error::WidgetAttached, surface)) + } + + /// Displays the contents of a widget surface on screen. + /// + /// Widget surfaces are internally double-buffered, so changes to them don't show up in their + /// associated widgets until this method is called. + /// + /// The supplied context must match the context the surface was created with, or an + /// `IncompatibleSurface` error is returned. + pub fn present_surface(&self, context: &Context, surface: &mut Surface) -> Result<(), Error> { + if context.id != surface.context_id { + return Err(Error::IncompatibleSurface); + } + + EGL_FUNCTIONS.with(|egl| unsafe { + match surface.objects { + SurfaceObjects::Window { egl_surface } => { + egl.SwapBuffers(self.egl_display, egl_surface); + Ok(()) + } + } + }) + } + + /// Resizes a widget surface. + pub fn resize_surface( + &self, + _context: &Context, + surface: &mut Surface, + size: Size2D, + ) -> Result<(), Error> { + surface.size = size; + Ok(()) + } + + /// Destroys a surface. + /// + /// The supplied context must be the context the surface is associated with, or this returns + /// an `IncompatibleSurface` error. + /// + /// You must explicitly call this method to dispose of a surface. Otherwise, a panic occurs in + /// the `drop` method. + pub fn destroy_surface( + &self, + context: &mut Context, + surface: &mut Surface, + ) -> Result<(), Error> { + if context.id != surface.context_id { + return Err(Error::IncompatibleSurface); + } + + unsafe { + match surface.objects { + SurfaceObjects::Window { + ref mut egl_surface, + } => EGL_FUNCTIONS.with(|egl| { + egl.DestroySurface(self.egl_display, *egl_surface); + *egl_surface = egl::NO_SURFACE; + }), + } + } + + surface.destroyed = true; + Ok(()) + } + + /// Destroys a surface texture and returns the underlying surface. + /// + /// The supplied context must be the same context the surface texture was created with, or an + /// `IncompatibleSurfaceTexture` error is returned. + /// + /// All surface textures must be explicitly destroyed with this function, or a panic will + /// occur. + pub fn destroy_surface_texture( + &self, + context: &mut Context, + mut surface_texture: SurfaceTexture, + ) -> Result { + let _guard = self.temporarily_make_context_current(context); + GL_FUNCTIONS.with(|gl| { + unsafe { + gl.DeleteTextures(1, &surface_texture.texture_object); + surface_texture.texture_object = 0; + + let egl_display = self.egl_display; + let result = (EGL_EXTENSION_FUNCTIONS.DestroyImageKHR)( + egl_display, + surface_texture.local_egl_image, + ); + assert_ne!(result, egl::FALSE); + surface_texture.local_egl_image = EGL_NO_IMAGE_KHR; + } + + Ok(surface_texture.surface) + }) + } + + /// Returns a pointer to the underlying surface data for reading or writing by the CPU. + #[inline] + pub fn lock_surface_data<'s>(&self, _: &'s mut Surface) -> Result, Error> { + error!("lock_surface_data not implemented yet for OHOS"); + Err(Error::Unimplemented) + } + + /// Returns the OpenGL texture target needed to read from this surface texture. + /// + /// This will be `GL_TEXTURE_2D` or `GL_TEXTURE_RECTANGLE`, depending on platform. + #[inline] + pub fn surface_gl_texture_target(&self) -> GLenum { + SURFACE_GL_TEXTURE_TARGET + } + + /// Returns various information about the surface, including the framebuffer object needed to + /// render to this surface. + /// + /// Before rendering to a surface attached to a context, you must call `glBindFramebuffer()` + /// on the framebuffer object returned by this function. This framebuffer object may or not be + /// 0, the default framebuffer, depending on platform. + pub fn surface_info(&self, surface: &Surface) -> SurfaceInfo { + SurfaceInfo { + size: surface.size, + id: surface.id(), + context_id: surface.context_id, + framebuffer_object: match surface.objects { + SurfaceObjects::Window { .. } => 0, + }, + } + } + + /// Returns the OpenGL texture object containing the contents of this surface. + /// + /// It is only legal to read from, not write to, this texture object. + #[inline] + pub fn surface_texture_object(&self, surface_texture: &SurfaceTexture) -> GLuint { + surface_texture.texture_object + } +} + +impl NativeWidget { + /// Creates a native widget type from an `OHNativeWindow`. + #[inline] + pub unsafe fn from_native_window(native_window: *mut OHNativeWindow) -> NativeWidget { + NativeWidget { native_window } + } +} + +impl Surface { + pub(super) fn id(&self) -> SurfaceID { + match self.objects { + SurfaceObjects::Window { egl_surface } => SurfaceID(egl_surface as usize), + } + } +} + +/// Represents the CPU view of the pixel data of this surface. +pub struct SurfaceDataGuard<'a> { + phantom: PhantomData<&'a ()>, +} diff --git a/surfman/src/platform/generic/mod.rs b/surfman/src/platform/generic/mod.rs index 81ec81d4..98a67364 100644 --- a/surfman/src/platform/generic/mod.rs +++ b/surfman/src/platform/generic/mod.rs @@ -2,7 +2,7 @@ // //! Backends that are not specific to any operating system. -#[cfg(any(android_platform, angle, free_unix))] +#[cfg(any(android_platform, angle, free_unix, ohos_platform))] pub(crate) mod egl; pub mod multi; diff --git a/surfman/src/platform/mod.rs b/surfman/src/platform/mod.rs index 6721e3b0..395754a1 100644 --- a/surfman/src/platform/mod.rs +++ b/surfman/src/platform/mod.rs @@ -4,10 +4,10 @@ pub mod generic; -#[cfg(android_platform)] -pub mod android; -#[cfg(android_platform)] -pub use android as default; +#[cfg(any(android_platform, ohos_platform))] +pub mod egl; +#[cfg(any(android_platform, ohos_platform))] +pub use egl as default; #[cfg(macos_platform)] pub mod macos; diff --git a/surfman/src/tests.rs b/surfman/src/tests.rs index 6f5cf47b..db725372 100644 --- a/surfman/src/tests.rs +++ b/surfman/src/tests.rs @@ -732,7 +732,7 @@ pub fn test_surface_texture_right_side_up() { } } -#[cfg(not(target_os = "android"))] +#[cfg(not(any(target_os = "android", target_env = "ohos")))] #[cfg_attr(not(feature = "sm-test"), test)] #[serial] pub fn test_depth_and_stencil() {