diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f6304494b..2699a3e78 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,9 +16,9 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 11 + java-version: 17 - name: Check Code Format - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v2.4.2 with: arguments: checkFormat @@ -31,7 +31,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 11 + java-version: 17 - name: Check JNI run: | cd tools/jni-test && \ @@ -50,13 +50,13 @@ jobs: uses: actions/setup-java@v3 with: distribution: temurin - java-version: 11 + java-version: 17 - name: Execute Android Linter - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v2.4.2 with: arguments: lint - name: Build Project - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v2.4.2 with: arguments: assemble${{ inputs.build_type }} - name: Save MiniBrowser Artifacts diff --git a/README.md b/README.md index f893cbe32..763ed8235 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ You can optionally create a debug build of WPEWebKit passing the `--debug` optio ``` Finally, the bootstrap option accepts the `--arch` option to set the target architecture. -Currently supported architectures are `arm64`, `armv7`, `x86` and `x86_64`. +Currently supported architectures are `arm64` and `x86_64`. ### Android Studio diff --git a/build.gradle b/build.gradle index 10d336974..a2bea1c27 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'com.android.application' version '8.0.2' apply false - id 'com.android.library' version '8.0.2' apply false + id 'com.android.application' version '8.1.2' apply false + id 'com.android.library' version '8.1.2' apply false } tasks.register("clean", Delete) { diff --git a/tools/mediaplayer/build.gradle b/tools/mediaplayer/build.gradle index 4deef9896..6c5097308 100644 --- a/tools/mediaplayer/build.gradle +++ b/tools/mediaplayer/build.gradle @@ -4,19 +4,18 @@ plugins { android { namespace 'com.wpe.tools.mediaplayer' - compileSdk 33 - buildToolsVersion '33.0.1' + compileSdk 34 defaultConfig { - minSdk 29 - targetSdk 33 + minSdk 31 + targetSdk 34 versionCode 1 versionName '1.0' } buildTypes { release { - minifyEnabled true + minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt') } } diff --git a/tools/mediaplayer/src/main/java/com/wpe/tools/mediaplayer/MainActivity.java b/tools/mediaplayer/src/main/java/com/wpe/tools/mediaplayer/MainActivity.java index 21ad4214e..20c6e14b4 100644 --- a/tools/mediaplayer/src/main/java/com/wpe/tools/mediaplayer/MainActivity.java +++ b/tools/mediaplayer/src/main/java/com/wpe/tools/mediaplayer/MainActivity.java @@ -76,18 +76,8 @@ public void onWindowFocusChanged(boolean hasFocus) { if (hasFocus) { View rootView = findViewById(android.R.id.content); if (rootView != null) { - if (Build.VERSION.SDK_INT >= 30) { - rootView.getWindowInsetsController().hide(WindowInsets.Type.statusBars() | - WindowInsets.Type.navigationBars()); - } else { - rootView = rootView.getRootView(); - if (rootView != null) { - rootView.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - } + rootView.getWindowInsetsController().hide(WindowInsets.Type.statusBars() | + WindowInsets.Type.navigationBars()); } } } diff --git a/tools/minibrowser/build.gradle b/tools/minibrowser/build.gradle index ee6eaa241..cbc7b6323 100644 --- a/tools/minibrowser/build.gradle +++ b/tools/minibrowser/build.gradle @@ -6,12 +6,11 @@ plugins { android { namespace 'com.wpe.tools.minibrowser' - compileSdk 33 - buildToolsVersion '33.0.1' + compileSdk 34 defaultConfig { - minSdk 29 - targetSdk 33 + minSdk 31 + targetSdk 34 versionCode 1 versionName '1.0' } @@ -46,12 +45,12 @@ android { dependencies { implementation project(':wpe') implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.9.0' - implementation 'androidx.core:core-ktx:1.10.1' - implementation 'androidx.fragment:fragment-ktx:1.6.0' - implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0' - implementation 'androidx.navigation:navigation-ui-ktx:2.6.0' - implementation 'androidx.preference:preference-ktx:1.2.0' + implementation 'com.google.android.material:material:1.10.0' + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.fragment:fragment-ktx:1.6.1' + implementation 'androidx.navigation:navigation-fragment-ktx:2.7.4' + implementation 'androidx.navigation:navigation-ui-ktx:2.7.4' + implementation 'androidx.preference:preference-ktx:1.2.1' modules { module("org.jetbrains.kotlin:kotlin-stdlib-jdk7") { diff --git a/tools/scripts/bootstrap.py b/tools/scripts/bootstrap.py index 18e88069b..8a8d94bfb 100755 --- a/tools/scripts/bootstrap.py +++ b/tools/scripts/bootstrap.py @@ -448,10 +448,6 @@ def install_deps(self): if self._arch == "arm64": android_abi = "arm64-v8a" - elif self._arch == "armv7": - android_abi = "armeabi-v7a" - elif self._arch == "x86": - android_abi = "x86" elif self._arch == "x86_64": android_abi = "x86_64" else: @@ -493,7 +489,7 @@ def run(self): ) parser.add_argument("-a", "--arch", metavar="architecture", required=False, default=Bootstrap.default_arch, - choices=["arm64", "armv7", "x86", "x86_64", "all"], help="The target architecture") + choices=["arm64", "x86_64", "all"], help="The target architecture") parser.add_argument("-v", "--version", metavar="version", required=False, default=Bootstrap.default_version, help="Specify the wpewebkit version to use (ignored if using --cerbero or --build, " "in these cases the version is taken from the Cerbero build)") @@ -510,7 +506,7 @@ def run(self): args = parser.parse_args() if args.arch == "all": - for arch in ["arm64", "armv7", "x86", "x86_64"]: + for arch in ["arm64", "x86_64"]: args.arch = arch print(args) Bootstrap(args).run() diff --git a/tools/scripts/build-patch.py b/tools/scripts/build-patch.py index c5a0669f3..874af4ea8 100755 --- a/tools/scripts/build-patch.py +++ b/tools/scripts/build-patch.py @@ -108,7 +108,7 @@ def run(self): ) parser.add_argument("-a", "--arch", metavar="architecture", required=False, default=Bootstrap.default_arch, - choices=["arm64", "armv7", "x86", "x86_64"], help="The target architecture") + choices=["arm64", "x86_64"], help="The target architecture") parser.add_argument("-r", "--recipe", metavar="recipe", required=False, default=BuildPatch.default_recipe, help="Specify the Cerbero recipe to build") parser.add_argument("-c", "--cerbero", metavar="path", required=False, help="Path to an external Cerbero build") diff --git a/wpe/build.gradle b/wpe/build.gradle index 15f2dffd4..90fa544c2 100644 --- a/wpe/build.gradle +++ b/wpe/build.gradle @@ -4,13 +4,16 @@ plugins { android { namespace 'com.wpe.wpe' - compileSdk 33 - buildToolsVersion '34.0.0' + compileSdk 34 ndkVersion '25.2.9519653' defaultConfig { - minSdk 29 - targetSdk 33 + minSdk 31 + targetSdk 34 + } + + lintOptions { + disable 'ChromeOsAbiSupport' } buildTypes { @@ -86,13 +89,13 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.6.0' + implementation 'androidx.annotation:annotation:1.7.0' } gradle.afterProject { project -> if (project == getProject()) { def abiList = [] - for (abi in ['x86_64', 'x86', 'arm64-v8a', 'armeabi-v7a']) { + for (abi in ['x86_64', 'arm64-v8a']) { if (file("src/main/jniLibs/$abi").isDirectory()) abiList.add(abi) } diff --git a/wpe/src/main/cpp/Browser/Fence.cpp b/wpe/src/main/cpp/Browser/Fence.cpp new file mode 100644 index 000000000..05158c494 --- /dev/null +++ b/wpe/src/main/cpp/Browser/Fence.cpp @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2023 Igalia S.L. + * Author: Jani Hautakangas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "Fence.h" + +#include +#include +#include +#include + +using sync_file_info_data = struct sync_file_info; + +Fence::FenceStatus Fence::getStatus(int fileDescriptor) +{ + auto info = std::unique_ptr { + sync_file_info(fileDescriptor), sync_file_info_free}; + if (info == nullptr) { + return FenceStatus::Invalid; + } + + if (info->status != 1) { + return FenceStatus::NotSignaled; + } + + __u64 timestamp = 0U; + auto* fenceInfo = sync_get_fence_info(info.get()); + for (size_t i = 0; i < info->num_fences; i++) { + timestamp = std::max(timestamp, fenceInfo->timestamp_ns); + } + + return FenceStatus::Signaled; +} diff --git a/wpe/src/main/cpp/Browser/Fence.h b/wpe/src/main/cpp/Browser/Fence.h new file mode 100644 index 000000000..9160edef7 --- /dev/null +++ b/wpe/src/main/cpp/Browser/Fence.h @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2023 Igalia S.L. + * Author: Jani Hautakangas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +class Fence { +public: + enum FenceStatus { + Signaled, + NotSignaled, + Invalid + }; + + static FenceStatus getStatus(int fileDescriptor); +}; diff --git a/wpe/src/main/cpp/Browser/Page.cpp b/wpe/src/main/cpp/Browser/Page.cpp index f4f95d304..a5a8b3c5a 100644 --- a/wpe/src/main/cpp/Browser/Page.cpp +++ b/wpe/src/main/cpp/Browser/Page.cpp @@ -24,10 +24,21 @@ #include "Browser.h" #include "Logging.h" -#include "RendererASurfaceTransaction.h" +#include "RendererSurfaceControl.h" #include #include +#include + +namespace { + +void handleCommitBuffer(void* context, WPEAndroidBuffer* buffer, int fenceID) +{ + auto* page = static_cast(context); + page->commitBuffer(buffer, fenceID); +} + +} // namespace /*********************************************************************************************************************** * JNI mapping with Java Page class @@ -69,14 +80,14 @@ class JNIPageCache final : public JNI::TypedClass { static bool onFullscreenRequest(Page* page, bool fullscreen) noexcept { - if (page->m_viewBackendExportable != nullptr) { + if (page->m_viewBackend != nullptr) { page->m_isFullscreenRequested = fullscreen; if (fullscreen) { callJavaMethod(getJNIPageCache().m_onEnterFullscreenMode, page->m_pageJavaInstance.get()); } else { callJavaMethod(getJNIPageCache().m_onExitFullscreenMode, page->m_pageJavaInstance.get()); wpe_view_backend_dispatch_did_exit_fullscreen( - wpe_android_view_backend_exportable_get_view_backend(page->m_viewBackendExportable)); + WPEAndroidViewBackend_getWPEViewBackend(page->m_viewBackend)); } } @@ -256,11 +267,11 @@ void JNIPageCache::nativeSurfaceChanged( { Logging::logDebug("Page::nativeSurfaceChanged(%d, %d, %d) [tid %d]", format, width, height, gettid()); Page* page = reinterpret_cast(pagePtr); // NOLINT(performance-no-int-to-ptr) - if ((page != nullptr) && (page->m_viewBackendExportable != nullptr) && page->m_renderer) { + if ((page != nullptr) && (page->m_viewBackend != nullptr) && page->m_renderer) { uint32_t uWidth = std::max(0, width); uint32_t uHeight = std::max(0, height); wpe_view_backend_dispatch_set_size( - wpe_android_view_backend_exportable_get_view_backend(page->m_viewBackendExportable), uWidth, uHeight); + WPEAndroidViewBackend_getWPEViewBackend(page->m_viewBackend), uWidth, uHeight); page->m_renderer->onSurfaceChanged(format, uWidth, uHeight); } } @@ -292,9 +303,8 @@ void JNIPageCache::nativeSetZoomLevel(JNIEnv* /*env*/, jobject /*obj*/, jlong pa void JNIPageCache::nativeOnTouchEvent( JNIEnv* /*env*/, jobject /*obj*/, jlong pagePtr, jlong time, jint type, jfloat xCoord, jfloat yCoord) noexcept { - Logging::logDebug("Page::nativeOnTouchEvent(%ld, %d, %f, %f) [tid %d]", time, type, xCoord, yCoord, gettid()); Page* page = reinterpret_cast(pagePtr); // NOLINT(performance-no-int-to-ptr) - if ((page != nullptr) && (page->m_viewBackendExportable != nullptr)) { + if ((page != nullptr) && (page->m_viewBackend != nullptr)) { wpe_input_touch_event_type touchEventType = wpe_input_touch_event_type_null; switch (type) { case 0: @@ -327,7 +337,7 @@ void JNIPageCache::nativeOnTouchEvent( .modifiers = 0}; wpe_view_backend_dispatch_touch_event( - wpe_android_view_backend_exportable_get_view_backend(page->m_viewBackendExportable), &touchEvent); + WPEAndroidViewBackend_getWPEViewBackend(page->m_viewBackend), &touchEvent); } } @@ -356,9 +366,8 @@ void JNIPageCache::nativeRequestExitFullscreenMode(JNIEnv* /*env*/, jobject /*ob { Logging::logDebug("Page::nativeRequestExitFullscreenMode() [tid %d]", gettid()); Page* page = reinterpret_cast(pagePtr); // NOLINT(performance-no-int-to-ptr) - if ((page != nullptr) && (page->m_viewBackendExportable != nullptr)) { - wpe_view_backend_dispatch_request_exit_fullscreen( - wpe_android_view_backend_exportable_get_view_backend(page->m_viewBackendExportable)); + if ((page != nullptr) && (page->m_viewBackend != nullptr)) { + wpe_view_backend_dispatch_request_exit_fullscreen(WPEAndroidViewBackend_getWPEViewBackend(page->m_viewBackend)); } } @@ -372,22 +381,15 @@ Page::Page(JNIEnv* env, JNIPage jniPage, int width, int height) : m_pageJavaInstance(JNI::createTypedProtectedRef(env, jniPage, true)) , m_inputMethodContext(this) { - static const wpe_android_view_backend_exportable_client s_exportableClient - = {.export_buffer = +[](void* data, AHardwareBuffer* buffer, uint32_t poolId, uint32_t bufferId) noexcept { - Logging::logDebug("s_exportableClient::export_buffer(%p, %u, %u)", buffer, poolId, bufferId); - Page* page = reinterpret_cast(data); - page->handleExportedBuffer(std::make_shared(buffer, poolId, bufferId)); - }}; - uint32_t uWidth = std::max(0, width); uint32_t uHeight = std::max(0, height); - m_viewBackendExportable = wpe_android_view_backend_exportable_create(&s_exportableClient, this, uWidth, uHeight); - m_renderer = std::make_shared(m_viewBackendExportable, uWidth, uHeight); + m_viewBackend = WPEAndroidViewBackend_create(uWidth, uHeight); + m_renderer = std::make_shared(m_viewBackend, uWidth, uHeight); - wpe_view_backend* wpeBackend = wpe_android_view_backend_exportable_get_view_backend(m_viewBackendExportable); - WebKitWebViewBackend* viewBackend = webkit_web_view_backend_new(wpeBackend, - reinterpret_cast(wpe_android_view_backend_exportable_destroy), m_viewBackendExportable); + WPEViewBackend* wpeBackend = WPEAndroidViewBackend_getWPEViewBackend(m_viewBackend); + WebKitWebViewBackend* viewBackend = webkit_web_view_backend_new( + wpeBackend, reinterpret_cast(WPEAndroidViewBackend_destroy), m_viewBackend); m_webView = webkit_web_view_new_with_context(viewBackend, Browser::instance().webContext()); webkit_web_view_set_input_method_context(m_webView, m_inputMethodContext.webKitInputMethodContext()); @@ -403,6 +405,8 @@ Page::Page(JNIEnv* env, JNIPage jniPage, int width, int height) wpe_view_backend_set_fullscreen_handler( wpeBackend, reinterpret_cast(JNIPageCache::onFullscreenRequest), this); + + WPEAndroidViewBackend_setCommitBufferHandler(m_viewBackend, this, handleCommitBuffer); } void Page::close() noexcept @@ -419,7 +423,7 @@ void Page::close() noexcept webkit_web_view_try_close(m_webView); - m_viewBackendExportable = nullptr; + m_viewBackend = nullptr; g_object_unref(m_webView); m_webView = nullptr; } @@ -429,20 +433,19 @@ void Page::onInputMethodContextIn() noexcept { getJNIPageCache().onInputMethodCo void Page::onInputMethodContextOut() noexcept { getJNIPageCache().onInputMethodContextOut(m_pageJavaInstance.get()); } -void Page::handleExportedBuffer(const std::shared_ptr& exportedBuffer) noexcept +void Page::commitBuffer(WPEAndroidBuffer* buffer, int fenceFD) noexcept { - if (m_renderer && (m_viewBackendExportable != nullptr)) { - Logging::logDebug("Page::handleExportedBuffer(%p) - Size (%ux%u)", exportedBuffer.get(), - exportedBuffer->width(), exportedBuffer->height()); + if (m_renderer && (m_viewBackend != nullptr)) { + auto scopedBuffer = std::make_shared(buffer); + auto scopedFenceFD = std::make_shared(fenceFD); - m_renderer->handleExportedBuffer(exportedBuffer); - - if (m_isFullscreenRequested && (exportedBuffer->width() == m_renderer->width()) - && (exportedBuffer->height() == m_renderer->height())) { + if (m_isFullscreenRequested && (scopedBuffer->width() == m_renderer->width()) + && (scopedBuffer->height() == m_renderer->height())) { Logging::logDebug("Fullscreen ready"); m_isFullscreenRequested = false; - wpe_view_backend_dispatch_did_enter_fullscreen( - wpe_android_view_backend_exportable_get_view_backend(m_viewBackendExportable)); + wpe_view_backend_dispatch_did_enter_fullscreen(WPEAndroidViewBackend_getWPEViewBackend(m_viewBackend)); } + + m_renderer->commitBuffer(scopedBuffer, scopedFenceFD); } } diff --git a/wpe/src/main/cpp/Browser/Page.h b/wpe/src/main/cpp/Browser/Page.h index 2227c3095..878e4b640 100644 --- a/wpe/src/main/cpp/Browser/Page.h +++ b/wpe/src/main/cpp/Browser/Page.h @@ -27,10 +27,11 @@ #include "Renderer.h" #include -#include DECLARE_JNI_CLASS_SIGNATURE(JNIPage, "com/wpe/wpe/Page"); +struct WPEAndroidViewBackend; + class Page final : public InputMethodContextObserver { public: static void configureJNIMappings(); @@ -49,6 +50,8 @@ class Page final : public InputMethodContextObserver { void onInputMethodContextIn() noexcept override; void onInputMethodContextOut() noexcept override; + void commitBuffer(WPEAndroidBuffer* buffer, int fenceFD) noexcept; + private: friend class JNIPageCache; @@ -58,10 +61,8 @@ class Page final : public InputMethodContextObserver { InputMethodContext m_inputMethodContext; std::shared_ptr m_renderer; - wpe_android_view_backend_exportable* m_viewBackendExportable = nullptr; + WPEAndroidViewBackend* m_viewBackend = nullptr; WebKitWebView* m_webView = nullptr; std::vector m_signalHandlers; bool m_isFullscreenRequested = false; - - void handleExportedBuffer(const std::shared_ptr& exportedBuffer) noexcept; }; diff --git a/wpe/src/main/cpp/Browser/Renderer.h b/wpe/src/main/cpp/Browser/Renderer.h index 911fee37b..01ba906d4 100644 --- a/wpe/src/main/cpp/Browser/Renderer.h +++ b/wpe/src/main/cpp/Browser/Renderer.h @@ -21,11 +21,13 @@ #pragma once -#include "ExportedBuffer.h" +#include "ScopedWPEAndroidBuffer.h" #include #include +#include "ScopedFD.h" + class Renderer { public: Renderer() = default; @@ -44,5 +46,5 @@ class Renderer { virtual void onSurfaceRedrawNeeded() noexcept = 0; virtual void onSurfaceDestroyed() noexcept = 0; - virtual void handleExportedBuffer(std::shared_ptr buffer) noexcept = 0; + virtual void commitBuffer(std::shared_ptr buffer, std::shared_ptr fenceFD) = 0; }; diff --git a/wpe/src/main/cpp/Browser/RendererASurfaceTransaction.cpp b/wpe/src/main/cpp/Browser/RendererASurfaceTransaction.cpp deleted file mode 100644 index 619f4d5b6..000000000 --- a/wpe/src/main/cpp/Browser/RendererASurfaceTransaction.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright (C) 2022 Igalia S.L. - * Author: Zan Dobersek - * Author: Loïc Le Page - * Author: Jani Hautakangas - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "RendererASurfaceTransaction.h" - -#include "Browser.h" -#include "Logging.h" - -#include - -RendererASurfaceTransaction::RendererASurfaceTransaction( - wpe_android_view_backend_exportable* viewBackendExportable, uint32_t width, uint32_t height) - : m_viewBackendExportable(viewBackendExportable) - , m_size({width, height}) -{ - Logging::logDebug( - "RendererASurfaceTransaction(%p, %u, %u)", m_viewBackendExportable, m_size.m_width, m_size.m_height); -} - -RendererASurfaceTransaction::~RendererASurfaceTransaction() -{ - Logging::logDebug("~RendererASurfaceTransaction()"); - if (m_surfaceControl != nullptr) { - ASurfaceControl_release(m_surfaceControl); - m_surfaceControl = nullptr; - } - - // Release the stored exported buffer, if any, and if different from the locked buffer. - // If the same, the buffer will be released via the locked buffer. - if (m_state.m_exportedBuffer && (m_state.m_exportedBuffer != m_state.m_lockedBuffer)) - releaseExportedBuffer(*m_state.m_exportedBuffer); - - // If locked buffer still exists, release it. - if (m_state.m_lockedBuffer) - releaseExportedBuffer(*m_state.m_lockedBuffer); - - m_state = {}; -} - -void RendererASurfaceTransaction::onSurfaceCreated(ANativeWindow* window) noexcept -{ - m_surfaceControl = ASurfaceControl_createFromWindow(window, "RendererASurfaceTransaction"); -} - -void RendererASurfaceTransaction::onSurfaceChanged(int /*format*/, uint32_t width, uint32_t height) noexcept -{ - m_size.m_width = width; - m_size.m_height = height; -} - -void RendererASurfaceTransaction::onSurfaceRedrawNeeded() noexcept -{ - // Nothing is doable if there's no ASurfaceControl. - if ((m_surfaceControl != nullptr) && m_state.m_exportedBuffer) { - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory, bugprone-unhandled-exception-at-new) - scheduleFrame(new TransactionContext {shared_from_this(), m_state.m_exportedBuffer}); - } -} - -void RendererASurfaceTransaction::onSurfaceDestroyed() noexcept -{ - ASurfaceControl_release(m_surfaceControl); - m_surfaceControl = nullptr; -} - -void RendererASurfaceTransaction::handleExportedBuffer(std::shared_ptr buffer) noexcept -{ - // If there's an exported buffer being held that's different from the locked one, it has to - // be released here since it won't be released otherwise. - if (m_state.m_exportedBuffer && (m_state.m_exportedBuffer != m_state.m_lockedBuffer)) - releaseExportedBuffer(*m_state.m_exportedBuffer); - - m_state.m_exportedBuffer = std::move(buffer); - - // Each buffer export requires a corresponding frame-complete callback. This is signalled here. - m_state.m_dispatchFrameCompleteCallback = true; - - // Nothing is doable if there's no ASurfaceControl. - if (m_surfaceControl != nullptr) { - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory, bugprone-unhandled-exception-at-new) - scheduleFrame(new TransactionContext {shared_from_this(), m_state.m_exportedBuffer}); - } -} - -void RendererASurfaceTransaction::scheduleFrame(TransactionContext* transactionContext) -{ - assert(m_surfaceControl); - - // Take the TransactionContext and its buffer and form a transaction for it. - // Upon transaction completion, the buffer will be presented on the surface. - ASurfaceTransaction* transaction = ASurfaceTransaction_create(); - ASurfaceTransaction_setVisibility(transaction, m_surfaceControl, ASURFACE_TRANSACTION_VISIBILITY_SHOW); - ASurfaceTransaction_setZOrder(transaction, m_surfaceControl, 0); - - ASurfaceTransaction_setBuffer(transaction, m_surfaceControl, transactionContext->m_buffer->buffer()); - - ASurfaceTransaction_setOnComplete(transaction, transactionContext, onTransactionCompleteOnAnyThread); - ASurfaceTransaction_apply(transaction); - ASurfaceTransaction_delete(transaction); -} - -void RendererASurfaceTransaction::finishFrame(std::shared_ptr buffer) -{ - Logging::logDebug("RendererASurfaceTransaction::finishFrame(%p)", buffer.get()); - - // If the current locked buffer is different from the current exported one, it should be released here. - if (m_state.m_lockedBuffer && (m_state.m_exportedBuffer != m_state.m_lockedBuffer)) - releaseExportedBuffer(*m_state.m_lockedBuffer); - - // The just-presented buffer is now also the locked one. - m_state.m_lockedBuffer = std::move(buffer); - - // If the frame-complete callback dispatch was requested, it's invoked here. - if (m_state.m_dispatchFrameCompleteCallback) { - wpe_android_view_backend_exportable_dispatch_frame_complete(m_viewBackendExportable); - m_state.m_dispatchFrameCompleteCallback = false; - } -} - -void RendererASurfaceTransaction::releaseExportedBuffer(const ExportedBuffer& buffer) const noexcept -{ - wpe_android_view_backend_exportable_dispatch_release_buffer( - m_viewBackendExportable, buffer.buffer(), buffer.poolId(), buffer.bufferId()); -} - -void RendererASurfaceTransaction::onTransactionCompleteOnAnyThread( - void* context, ASurfaceTransactionStats* /*stats*/) noexcept -{ - Logging::logDebug("RendererASurfaceTransaction::onTransactionCompleteOnAnyThread(%p)", context); - - // API documentation states that this callback can be dispatched from any thread. - // Let's relay the transaction completion to the UI thread. - Browser::instance().invokeOnUiThread( - +[](void* userData) { - auto* transactionContext = reinterpret_cast(userData); - if (auto renderer = transactionContext->m_renderer.lock()) - renderer->finishFrame(transactionContext->m_buffer); - }, - // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - +[](void* userData) { delete reinterpret_cast(userData); }, context); -} diff --git a/wpe/src/main/cpp/Browser/RendererASurfaceTransaction.h b/wpe/src/main/cpp/Browser/RendererASurfaceTransaction.h deleted file mode 100644 index 3ca9adc86..000000000 --- a/wpe/src/main/cpp/Browser/RendererASurfaceTransaction.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (C) 2022 Igalia S.L. - * Author: Zan Dobersek - * Author: Loïc Le Page - * Author: Jani Hautakangas - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#include "Renderer.h" - -#include -#include -#include - -class RendererASurfaceTransaction final : public Renderer, - public std::enable_shared_from_this { -public: - RendererASurfaceTransaction( - wpe_android_view_backend_exportable* viewBackendExportable, uint32_t width, uint32_t height); - ~RendererASurfaceTransaction() override; - - RendererASurfaceTransaction(RendererASurfaceTransaction&&) = delete; - RendererASurfaceTransaction& operator=(RendererASurfaceTransaction&&) = delete; - RendererASurfaceTransaction(const RendererASurfaceTransaction&) = delete; - RendererASurfaceTransaction& operator=(const RendererASurfaceTransaction&) = delete; - - uint32_t width() const noexcept override { return m_size.m_width; } - uint32_t height() const noexcept override { return m_size.m_height; } - - void onSurfaceCreated(ANativeWindow* window) noexcept override; - void onSurfaceChanged(int format, uint32_t width, uint32_t height) noexcept override; - void onSurfaceRedrawNeeded() noexcept override; - void onSurfaceDestroyed() noexcept override; - - void handleExportedBuffer(std::shared_ptr buffer) noexcept override; - -private: - struct TransactionContext { - std::weak_ptr m_renderer {}; - std::shared_ptr m_buffer {}; - }; - - void scheduleFrame(TransactionContext* transactionContext); - void finishFrame(std::shared_ptr buffer); - void releaseExportedBuffer(const ExportedBuffer& buffer) const noexcept; - - static void onTransactionCompleteOnAnyThread(void* context, ASurfaceTransactionStats* stats) noexcept; - - wpe_android_view_backend_exportable* m_viewBackendExportable = nullptr; - ASurfaceControl* m_surfaceControl = nullptr; - - struct { - uint32_t m_width; - uint32_t m_height; - } m_size = {}; - - struct { - bool m_dispatchFrameCompleteCallback = false; - std::shared_ptr m_exportedBuffer {}; - std::shared_ptr m_lockedBuffer {}; - } m_state; -}; diff --git a/wpe/src/main/cpp/Browser/RendererSurfaceControl.cpp b/wpe/src/main/cpp/Browser/RendererSurfaceControl.cpp new file mode 100644 index 000000000..7b910349c --- /dev/null +++ b/wpe/src/main/cpp/Browser/RendererSurfaceControl.cpp @@ -0,0 +1,159 @@ +/** + * Copyright (C) 2022 Igalia S.L. + * Author: Zan Dobersek + * Author: Loïc Le Page + * Author: Jani Hautakangas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "RendererSurfaceControl.h" + +#include +#include + +#include "Browser.h" +#include "Fence.h" +#include "Logging.h" +#include "SurfaceControl.h" + +RendererSurfaceControl::RendererSurfaceControl(WPEAndroidViewBackend* viewBackend, uint32_t width, uint32_t height) + : m_viewBackend(viewBackend) + , m_size({width, height}) +{ + Logging::logDebug("RendererSurfaceControl(%p, %u, %u)", m_viewBackend, m_size.m_width, m_size.m_height); +} + +RendererSurfaceControl::~RendererSurfaceControl() { Logging::logDebug("RendererSurfaceControl()"); } + +void RendererSurfaceControl::onSurfaceCreated(ANativeWindow* window) noexcept +{ + m_surface = std::make_shared(window, "Surface"); +} + +void RendererSurfaceControl::onSurfaceChanged(int /*format*/, uint32_t width, uint32_t height) noexcept +{ + m_size.m_width = width; + m_size.m_height = height; +} + +void RendererSurfaceControl::onSurfaceRedrawNeeded() noexcept +{ + /* + // Nothing is doable if there's no ASurfaceControl. + if ((m_surfaceControl != nullptr) && m_state.m_exportedBuffer) { + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory, bugprone-unhandled-exception-at-new) + scheduleFrame(new TransactionContext {shared_from_this(), m_state.m_exportedBuffer}); + } + */ +} + +void RendererSurfaceControl::onSurfaceDestroyed() noexcept { m_surface = nullptr; } + +void RendererSurfaceControl::commitBuffer( + std::shared_ptr buffer, std::shared_ptr fenceFD) +{ + SurfaceControl::Transaction transaction; + transaction.setVisibility(*m_surface, ASURFACE_TRANSACTION_VISIBILITY_SHOW); + transaction.setZOrder(*m_surface, 0); + transaction.setBuffer(*m_surface, buffer->buffer(), fenceFD->release()); + + ResourceRefs resourcesToRelease; + resourcesToRelease.swap(m_currentFrameResources); + m_currentFrameResources.clear(); + + auto& resourceRef = m_currentFrameResources[m_surface->surfaceControl()]; + resourceRef.m_surface = m_surface; + resourceRef.m_scopedBuffer = buffer; + + auto onCompleteCallback = [this, resources = std::move(resourcesToRelease)](auto&& stats) { + onTransActionAckOnBrowserThread(resources, std::forward(stats)); + }; + transaction.setOnCompleteCallback(std::move(onCompleteCallback)); + + auto onCommitCallback = [this] { onTransactionCommittedOnBrowserThread(); }; + transaction.setOnCommitCallback(std::move(onCommitCallback)); + + if (m_numTransactionCommitOrAckPending > 0) { + m_pendingTransactionQueue.push(std::move(transaction)); + } else { + m_numTransactionCommitOrAckPending++; + transaction.apply(); + } +} + +void RendererSurfaceControl::onTransActionAckOnBrowserThread( + ResourceRefs releasedResources, SurfaceControl::TransactionStats stats) +{ + for (auto& surfaceStat : stats.m_surfaceStats) { + auto resourceIterator = releasedResources.find(surfaceStat.m_surface); + if (resourceIterator == releasedResources.end()) { + continue; + } + + resourceIterator->second.m_scopedBuffer->setReleaseFenceFD(surfaceStat.m_fence); + m_releaseBufferQueue.push(std::move(resourceIterator->second.m_scopedBuffer)); + } + releasedResources.clear(); + + // Following is from Android ASurfaceControl documentation in surface_control.h + // + // Each time a buffer is set through ASurfaceTransaction_setBuffer() on a transaction + // which is applied, the framework takes a ref on this buffer. The framework treats the + // addition of a buffer to a particular surface as a unique ref. When a transaction updates or + // removes a buffer from a surface, or removes the surface itself from the tree, this ref is + // guaranteed to be released in the OnComplete callback for this transaction. The + // ASurfaceControlStats provided in the callback for this surface may contain an optional fence + // which must be signaled before the ref is assumed to be released. + // + // The client must ensure that all pending refs on a buffer are released before attempting to reuse + // this buffer, otherwise synchronization errors may occur. + // + // TBD: Based on above description it seems that fence must be checked and waited if present before reusing the + // buffer. But in practice it seems to have no significant effect if check is done or not done + while (!m_releaseBufferQueue.empty()) { + auto& pendingBuffer = m_releaseBufferQueue.front(); + auto status = pendingBuffer->getReleaseFenceFD() != -1 ? Fence::getStatus(pendingBuffer->getReleaseFenceFD()) + : Fence::Invalid; + + if (status == Fence::NotSignaled) + break; + + WPEAndroidViewBackend_dispatchReleaseBuffer(m_viewBackend, pendingBuffer->wpeBuffer()); + m_releaseBufferQueue.pop(); + } +} + +void RendererSurfaceControl::onTransactionCommittedOnBrowserThread() +{ + // We can notify WebProcess already at this point to start rendering next frame. + // This causes WPEBackend-android to use triple buffering but performance is better this way + // + // If this dispatch_frame_complete is called in onTransActionAckOnBrowserThread then WPEBackend-android + // stays in double buffering + WPEAndroidViewBackend_dispatchFrameComplete(m_viewBackend); + + processTransactionQueue(); +} + +void RendererSurfaceControl::processTransactionQueue() +{ + m_numTransactionCommitOrAckPending--; + if (!m_pendingTransactionQueue.empty()) { + m_numTransactionCommitOrAckPending++; + m_pendingTransactionQueue.front().apply(); + m_pendingTransactionQueue.pop(); + } +} diff --git a/wpe/src/main/cpp/Browser/RendererSurfaceControl.h b/wpe/src/main/cpp/Browser/RendererSurfaceControl.h new file mode 100644 index 000000000..9d60328a5 --- /dev/null +++ b/wpe/src/main/cpp/Browser/RendererSurfaceControl.h @@ -0,0 +1,87 @@ +/** + * Copyright (C) 2022 Igalia S.L. + * Author: Zan Dobersek + * Author: Loïc Le Page + * Author: Jani Hautakangas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "Renderer.h" + +#include +#include +#include + +#include "SurfaceControl.h" + +class RendererSurfaceControl final : public Renderer, public std::enable_shared_from_this { +public: + RendererSurfaceControl(WPEAndroidViewBackend* viewBackend, uint32_t width, uint32_t height); + ~RendererSurfaceControl() override; + + RendererSurfaceControl(RendererSurfaceControl&&) = delete; + RendererSurfaceControl& operator=(RendererSurfaceControl&&) = delete; + RendererSurfaceControl(const RendererSurfaceControl&) = delete; + RendererSurfaceControl& operator=(const RendererSurfaceControl&) = delete; + + uint32_t width() const noexcept override { return m_size.m_width; } + uint32_t height() const noexcept override { return m_size.m_height; } + + void onSurfaceCreated(ANativeWindow* window) noexcept override; + void onSurfaceChanged(int format, uint32_t width, uint32_t height) noexcept override; + void onSurfaceRedrawNeeded() noexcept override; + void onSurfaceDestroyed() noexcept override; + + void commitBuffer(std::shared_ptr buffer, std::shared_ptr fenceFD) override; + +private: + struct ResourceRef { + ResourceRef() = default; + ~ResourceRef() = default; + + ResourceRef(const ResourceRef& other) = default; + ResourceRef& operator=(const ResourceRef& other) = default; + + ResourceRef(ResourceRef&& other) = default; + ResourceRef& operator=(ResourceRef&& other) = default; + + std::shared_ptr m_surface; + std::shared_ptr m_scopedBuffer; + }; + using ResourceRefs = std::map; + + void onTransActionAckOnBrowserThread(ResourceRefs releasedResources, SurfaceControl::TransactionStats stats); + void onTransactionCommittedOnBrowserThread(); + + void processTransactionQueue(); + + WPEAndroidViewBackend* m_viewBackend = nullptr; + std::shared_ptr m_surface; + + struct { + uint32_t m_width; + uint32_t m_height; + } m_size = {}; + + std::queue m_pendingTransactionQueue; + uint32_t m_numTransactionCommitOrAckPending = 0U; + + ResourceRefs m_currentFrameResources; + + std::queue> m_releaseBufferQueue; +}; diff --git a/wpe/src/main/cpp/Browser/ScopedFD.h b/wpe/src/main/cpp/Browser/ScopedFD.h new file mode 100644 index 000000000..5457a2d55 --- /dev/null +++ b/wpe/src/main/cpp/Browser/ScopedFD.h @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2023 Igalia S.L. + * Author: Jani Hautakangas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include + +class ScopedFD final { +public: + ScopedFD() + : m_fd(-1) + { + } + explicit ScopedFD(int fileDescriptor) + : m_fd(fileDescriptor) + { + } + ~ScopedFD() + { + if (m_fd != -1) { + close(); + } + } + + ScopedFD(ScopedFD&& other) = delete; + ScopedFD& operator=(ScopedFD&& other) = delete; + ScopedFD(const ScopedFD&) = delete; + ScopedFD& operator=(const ScopedFD&) = delete; + + int get() const { return m_fd; } + + int release() + { + int releasedFD = m_fd; + m_fd = -1; + return releasedFD; + } + +private: + void close() + { + if (m_fd >= 0) + ::close(m_fd); + m_fd = -1; + } + + int m_fd; +}; diff --git a/wpe/src/main/cpp/Browser/ExportedBuffer.h b/wpe/src/main/cpp/Browser/ScopedWPEAndroidBuffer.h similarity index 53% rename from wpe/src/main/cpp/Browser/ExportedBuffer.h rename to wpe/src/main/cpp/Browser/ScopedWPEAndroidBuffer.h index 25d0857a3..1cb31c67e 100644 --- a/wpe/src/main/cpp/Browser/ExportedBuffer.h +++ b/wpe/src/main/cpp/Browser/ScopedWPEAndroidBuffer.h @@ -21,43 +21,49 @@ #pragma once #include +#include +#include -class ExportedBuffer final { +#include "ScopedFD.h" + +class ScopedWPEAndroidBuffer final { public: - ExportedBuffer(AHardwareBuffer* buffer, uint32_t poolId, uint32_t bufferId) + ScopedWPEAndroidBuffer(WPEAndroidBuffer* buffer) : m_buffer(buffer) - , m_poolId(poolId) - , m_bufferId(bufferId) { if (m_buffer != nullptr) { - AHardwareBuffer_acquire(m_buffer); + m_hardwareBuffer = WPEAndroidBuffer_getAHardwareBuffer(m_buffer); + AHardwareBuffer_acquire(m_hardwareBuffer); AHardwareBuffer_Desc desc = {}; - AHardwareBuffer_describe(m_buffer, &desc); + AHardwareBuffer_describe(m_hardwareBuffer, &desc); m_size = {desc.width, desc.height}; } } - ExportedBuffer(ExportedBuffer&&) = delete; - ExportedBuffer& operator=(ExportedBuffer&&) = delete; - ExportedBuffer(const ExportedBuffer&) = delete; - ExportedBuffer& operator=(const ExportedBuffer&) = delete; + ScopedWPEAndroidBuffer(ScopedWPEAndroidBuffer&&) = delete; + ScopedWPEAndroidBuffer& operator=(ScopedWPEAndroidBuffer&&) = delete; + ScopedWPEAndroidBuffer(const ScopedWPEAndroidBuffer&) = delete; + ScopedWPEAndroidBuffer& operator=(const ScopedWPEAndroidBuffer&) = delete; - ~ExportedBuffer() + ~ScopedWPEAndroidBuffer() { if (m_buffer != nullptr) - AHardwareBuffer_release(m_buffer); + AHardwareBuffer_release(m_hardwareBuffer); } - AHardwareBuffer* buffer() const noexcept { return m_buffer; } - uint32_t poolId() const noexcept { return m_poolId; } - uint32_t bufferId() const noexcept { return m_bufferId; } + WPEAndroidBuffer* wpeBuffer() const { return m_buffer; } + AHardwareBuffer* buffer() const noexcept { return WPEAndroidBuffer_getAHardwareBuffer(m_buffer); } uint32_t width() const noexcept { return m_size.m_width; } uint32_t height() const noexcept { return m_size.m_height; } + int getReleaseFenceFD() const { return m_releaseFenceFD->get(); } + void setReleaseFenceFD(std::shared_ptr releaseFenceFD) { m_releaseFenceFD = std::move(releaseFenceFD); } + private: - AHardwareBuffer* m_buffer; - uint32_t m_poolId; - uint32_t m_bufferId; + WPEAndroidBuffer* m_buffer = nullptr; + ; + AHardwareBuffer* m_hardwareBuffer = nullptr; + std::shared_ptr m_releaseFenceFD; struct { uint32_t m_width; uint32_t m_height; diff --git a/wpe/src/main/cpp/Browser/SurfaceControl.cpp b/wpe/src/main/cpp/Browser/SurfaceControl.cpp new file mode 100644 index 000000000..d953643a0 --- /dev/null +++ b/wpe/src/main/cpp/Browser/SurfaceControl.cpp @@ -0,0 +1,185 @@ +/** + * Copyright (C) 2023 Igalia S.L. + * Author: Jani Hautakangas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "SurfaceControl.h" + +#include "Browser.h" +#include "ScopedWPEAndroidBuffer.h" + +namespace { + +struct TransactionContext { + SurfaceControl::Transaction::OnCompleteCallback m_completeCallback; + SurfaceControl::Transaction::OnCommitCallback m_commitCallback; + SurfaceControl::TransactionStats m_stats; +}; + +SurfaceControl::TransactionStats toTransactionStats(ASurfaceTransactionStats* stats) +{ + SurfaceControl::TransactionStats transactionStats; + if (stats == nullptr) + return transactionStats; + + ASurfaceControl** surfaceControls = nullptr; + size_t size = 0U; + ASurfaceTransactionStats_getASurfaceControls(stats, &surfaceControls, &size); + transactionStats.m_surfaceStats.resize(size); + for (size_t i = 0U; i < size; ++i) { + transactionStats.m_surfaceStats[i].m_surface = surfaceControls[i]; + int const fenceFD = ASurfaceTransactionStats_getPreviousReleaseFenceFd(stats, surfaceControls[i]); + if (fenceFD != -1) { + transactionStats.m_surfaceStats[i].m_fence = std::make_shared(fenceFD); + } + } + ASurfaceTransactionStats_releaseASurfaceControls(surfaceControls); + + return transactionStats; +} + +void onTransactionCommitedOnAnyThread(void* context, ASurfaceTransactionStats* /*stats*/) +{ + auto* ackCtx = static_cast(context); + + // API documentation states that this callback can be dispatched from any thread. + // Let's relay the transaction completion to the UI thread. + Browser::instance().invokeOnUiThread( + +[](void* userData) { + auto* ackCtx = reinterpret_cast(userData); + std::move(ackCtx->m_commitCallback)(); + }, + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + +[](void* userData) { delete reinterpret_cast(userData); }, ackCtx); +} + +void onTransactionCompletedOnAnyThread(void* context, ASurfaceTransactionStats* stats) +{ + auto* ackCtx = static_cast(context); + ackCtx->m_stats = toTransactionStats(stats); + + // API documentation states that this callback can be dispatched from any thread. + // Let's relay the transaction completion to the UI thread. + Browser::instance().invokeOnUiThread( + +[](void* userData) { + auto* ackCtx = reinterpret_cast(userData); + std::move(ackCtx->m_completeCallback)(std::move(ackCtx->m_stats)); + }, + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + +[](void* userData) { delete reinterpret_cast(userData); }, ackCtx); +} + +} // namespace + +SurfaceControl::Surface::Surface(ANativeWindow* parent, const char* name) +{ + m_surfaceControl = ASurfaceControl_createFromWindow(parent, name); +} + +SurfaceControl::Surface::~Surface() +{ + ASurfaceControl_release(m_surfaceControl); + m_surfaceControl = nullptr; +} + +SurfaceControl::Surface::Surface(Surface&& other) noexcept + : m_surfaceControl(other.m_surfaceControl) +{ + other.m_surfaceControl = nullptr; +} + +SurfaceControl::Surface& SurfaceControl::Surface::operator=(Surface&& other) noexcept +{ + if (this == &other) + return *this; + + m_surfaceControl = other.m_surfaceControl; + other.m_surfaceControl = nullptr; + return *this; +} + +SurfaceControl::Transaction::Transaction() { m_transaction = ASurfaceTransaction_create(); } + +SurfaceControl::Transaction::~Transaction() { destroyIfNeeded(); } + +void SurfaceControl::Transaction::destroyIfNeeded() +{ + if (m_transaction == nullptr) + return; + ASurfaceTransaction_delete(m_transaction); + m_transaction = nullptr; +} + +SurfaceControl::Transaction::Transaction(Transaction&& other) noexcept + : m_transaction(other.m_transaction) +{ + other.m_transaction = nullptr; +} + +SurfaceControl::Transaction& SurfaceControl::Transaction::operator=(Transaction&& other) noexcept +{ + if (this == &other) + return *this; + + destroyIfNeeded(); + m_transaction = other.m_transaction; + other.m_transaction = nullptr; + return *this; +} + +void SurfaceControl::Transaction::setVisibility(const Surface& surface, int8_t visibility) +{ + ASurfaceTransaction_setVisibility(m_transaction, surface.surfaceControl(), visibility); +} + +void SurfaceControl::Transaction::setZOrder(const Surface& surface, int32_t zOrder) +{ + ASurfaceTransaction_setZOrder(m_transaction, surface.surfaceControl(), zOrder); +} + +void SurfaceControl::Transaction::setBuffer(const Surface& surface, AHardwareBuffer* buffer, int fenceFD) +{ + ASurfaceTransaction_setBuffer(m_transaction, surface.surfaceControl(), buffer, fenceFD); +} + +void SurfaceControl::Transaction::setOnCompleteCallback(OnCompleteCallback callback) +{ + m_onCompleteCallback = std::move(callback); +} + +void SurfaceControl::Transaction::setOnCommitCallback(OnCommitCallback callback) +{ + m_onCommitCallback = std::move(callback); +} + +void SurfaceControl::Transaction::apply() +{ + if (m_onCommitCallback != nullptr) { + auto ackCtx = std::make_unique(); + ackCtx->m_commitCallback = std::move(m_onCommitCallback); + ASurfaceTransaction_setOnCommit(m_transaction, ackCtx.release(), onTransactionCommitedOnAnyThread); + } + + if (m_onCompleteCallback != nullptr) { + auto ackCtx = std::make_unique(); + ackCtx->m_completeCallback = std::move(m_onCompleteCallback); + + ASurfaceTransaction_setOnComplete(m_transaction, ackCtx.release(), onTransactionCompletedOnAnyThread); + } + + ASurfaceTransaction_apply(m_transaction); +} diff --git a/wpe/src/main/cpp/Browser/SurfaceControl.h b/wpe/src/main/cpp/Browser/SurfaceControl.h new file mode 100644 index 000000000..a00e3ddcd --- /dev/null +++ b/wpe/src/main/cpp/Browser/SurfaceControl.h @@ -0,0 +1,110 @@ +/** + * Copyright (C) 2023 Igalia S.L. + * Author: Jani Hautakangas + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include +#include +#include + +#include "ScopedFD.h" + +class ScopedWPEAndroidBuffer; + +class SurfaceControl final { +public: + class Surface final { + public: + Surface(ANativeWindow* parent, const char* name); + + Surface(const Surface&) = delete; + Surface& operator=(const Surface&) = delete; + + ~Surface(); + + Surface(Surface&& other) noexcept; + Surface& operator=(Surface&& other) noexcept; + + ASurfaceControl* surfaceControl() const { return m_surfaceControl; } + + private: + ASurfaceControl* m_surfaceControl = nullptr; + }; + + struct SurfaceStats final { + SurfaceStats() = default; + ~SurfaceStats() = default; + + SurfaceStats(const SurfaceStats&) = delete; + SurfaceStats& operator=(const SurfaceStats&) = delete; + + SurfaceStats(SurfaceStats&& other) = default; + SurfaceStats& operator=(SurfaceStats&& other) = default; + + ASurfaceControl* m_surface = nullptr; + std::shared_ptr m_fence; + }; + + struct TransactionStats final { + public: + TransactionStats() = default; + ~TransactionStats() = default; + + TransactionStats(const TransactionStats&) = delete; + TransactionStats& operator=(const TransactionStats&) = delete; + + TransactionStats(TransactionStats&& other) = default; + TransactionStats& operator=(TransactionStats&& other) = default; + + std::vector m_surfaceStats; + }; + + class Transaction final { + public: + Transaction(); + + Transaction(const Transaction&) = delete; + Transaction& operator=(const Transaction&) = delete; + + ~Transaction(); + + Transaction(Transaction&& other) noexcept; + Transaction& operator=(Transaction&& other) noexcept; + + void setVisibility(const Surface& surface, int8_t visibility); + void setZOrder(const Surface& surface, int32_t zOrder); + void setBuffer(const Surface& surface, AHardwareBuffer* buffer, int fenceFD); + + void apply(); + + using OnCompleteCallback = std::function; + void setOnCompleteCallback(OnCompleteCallback callback); + + using OnCommitCallback = std::function; + void setOnCommitCallback(OnCommitCallback callback); + + private: + void destroyIfNeeded(); + + ASurfaceTransaction* m_transaction = nullptr; + + OnCompleteCallback m_onCompleteCallback; + OnCommitCallback m_onCommitCallback; + }; +}; diff --git a/wpe/src/main/cpp/CMakeLists.txt b/wpe/src/main/cpp/CMakeLists.txt index b59dad15f..5494868e1 100644 --- a/wpe/src/main/cpp/CMakeLists.txt +++ b/wpe/src/main/cpp/CMakeLists.txt @@ -36,6 +36,7 @@ endfunction() find_library(android-lib android REQUIRED) find_library(log-lib log REQUIRED) +find_library(sync-lib sync REQUIRED) # ###################################################################################################################### # Imported external libraries @@ -90,7 +91,7 @@ add_library(WPEAndroidCommon SHARED Common/Environment.cpp Common/Logging.cpp Co Common/JNI/JNIEnv.cpp Common/JNI/JNIString.cpp) target_configure_quality(WPEAndroidCommon) target_include_directories(WPEAndroidCommon INTERFACE Common) -target_link_libraries(WPEAndroidCommon ${android-lib} ${log-lib}) +target_link_libraries(WPEAndroidCommon ${android-lib} ${log-lib} ${sync-lib}) # libWPEAndroidBrowser add_library( @@ -98,12 +99,14 @@ add_library( Browser/Browser.cpp Browser/WPECookieManager.cpp Browser/EntryPoint.cpp + Browser/Fence.cpp Browser/InputMethodContext.cpp Browser/LooperThread.cpp Browser/MessagePump.cpp Browser/Page.cpp Browser/PageSettings.cpp - Browser/RendererASurfaceTransaction.cpp) + Browser/RendererSurfaceControl.cpp + Browser/SurfaceControl.cpp) target_configure_quality(WPEAndroidBrowser) target_compile_definitions(WPEAndroidBrowser PRIVATE WPE_ENABLE_PROCESS) target_link_libraries(