diff --git a/packages/skia/android/CMakeLists.txt b/packages/skia/android/CMakeLists.txt index e1efb49fb8..a3da6f5b0b 100644 --- a/packages/skia/android/CMakeLists.txt +++ b/packages/skia/android/CMakeLists.txt @@ -54,7 +54,7 @@ if(SK_GRAPHITE) else() add_definitions(-DSK_GL -DSK_GANESH) set(BACKEND_SOURCES - "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp" + "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/OpenGLWindowContext.cpp" "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/GrAHardwareBufferUtils.cpp" ) endif() @@ -74,6 +74,7 @@ add_library( "${PROJECT_SOURCE_DIR}/cpp/jni/JniSkiaManager.cpp" "${PROJECT_SOURCE_DIR}/cpp/jni/JniPlatformContext.cpp" + "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/opengl/Error.cpp" "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp" "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/AHardwareBufferUtils.cpp" "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/RNSkAndroidVideo.cpp" diff --git a/packages/skia/android/cpp/rnskia-android/OpenGLContext.h b/packages/skia/android/cpp/rnskia-android/OpenGLContext.h index 015de205e9..723e5eda81 100644 --- a/packages/skia/android/cpp/rnskia-android/OpenGLContext.h +++ b/packages/skia/android/cpp/rnskia-android/OpenGLContext.h @@ -1,9 +1,18 @@ #pragma once -#include "SkiaOpenGLSurfaceFactory.h" -#include "WindowContext.h" +#include "GrAHardwareBufferUtils.h" +#include "OpenGLWindowContext.h" +#include "opengl/Display.h" +#include "include/core/SkCanvas.h" +#include "include/core/SkColorSpace.h" #include "include/core/SkSurface.h" +#include "include/gpu/ganesh/GrDirectContext.h" +#include "include/gpu/ganesh/SkImageGanesh.h" +#include "include/gpu/ganesh/gl/GrGLBackendSurface.h" +#include "include/gpu/ganesh/gl/GrGLDirectContext.h" +#include "include/gpu/ganesh/gl/GrGLInterface.h" +#include "src/gpu/ganesh/gl/GrGLDefines.h" class OpenGLContext { public: @@ -16,25 +25,127 @@ class OpenGLContext { } sk_sp MakeOffscreen(int width, int height) { - return RNSkia::SkiaOpenGLSurfaceFactory::makeOffscreenSurface( - &_context, width, height); + auto colorType = kRGBA_8888_SkColorType; + + SkSurfaceProps props(0, kUnknown_SkPixelGeometry); + + auto result = _ctx->makeCurrent(*_surface); + if (!result) { + return nullptr; + } + + // Create texture + auto texture = _directContext->createBackendTexture( + width, height, colorType, skgpu::Mipmapped::kNo, GrRenderable::kYes); + + if (!texture.isValid()) { + RNSkia::RNSkLogger::logToConsole( + "couldn't create offscreen texture %dx%d", width, height); + } + + struct ReleaseContext { + GrDirectContext *directContext; + GrBackendTexture texture; + }; + + auto releaseCtx = new ReleaseContext{.directContext = _directContext.get(), + .texture = texture}; + + // Create a SkSurface from the GrBackendTexture + return SkSurfaces::WrapBackendTexture( + _directContext.get(), texture, kTopLeft_GrSurfaceOrigin, 0, colorType, + nullptr, &props, + [](void *addr) { + auto releaseCtx = reinterpret_cast(addr); + releaseCtx->directContext->deleteBackendTexture(releaseCtx->texture); + delete releaseCtx; + }, + releaseCtx); } - sk_sp MakeImageFromBuffer(void *buffer) { - return RNSkia::SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer( - &_context, buffer); + sk_sp MakeImageFromBuffer(void *buffer, + bool requireKnownFormat = false) { +#if __ANDROID_API__ >= 26 + const AHardwareBuffer *hardwareBuffer = + static_cast(buffer); + RNSkia::DeleteImageProc deleteImageProc = nullptr; + RNSkia::UpdateImageProc updateImageProc = nullptr; + RNSkia::TexImageCtx deleteImageCtx = nullptr; + + AHardwareBuffer_Desc description; + AHardwareBuffer_describe(hardwareBuffer, &description); + GrBackendFormat format; + switch (description.format) { + // TODO: find out if we can detect, which graphic buffers support + // GR_GL_TEXTURE_2D + case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: + format = GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL); + case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: + format = GrBackendFormats::MakeGL(GR_GL_RGBA16F, GR_GL_TEXTURE_EXTERNAL); + case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: + format = GrBackendFormats::MakeGL(GR_GL_RGB565, GR_GL_TEXTURE_EXTERNAL); + case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: + format = GrBackendFormats::MakeGL(GR_GL_RGB10_A2, GR_GL_TEXTURE_EXTERNAL); + case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: + format = GrBackendFormats::MakeGL(GR_GL_RGB8, GR_GL_TEXTURE_EXTERNAL); +#if __ANDROID_API__ >= 33 + case AHARDWAREBUFFER_FORMAT_R8_UNORM: + format = GrBackendFormats::MakeGL(GR_GL_R8, GR_GL_TEXTURE_EXTERNAL); +#endif + default: + if (requireKnownFormat) { + format = GrBackendFormat(); + } else { + format = GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL); + } + } + + auto backendTex = RNSkia::MakeGLBackendTexture( + _directContext.get(), const_cast(hardwareBuffer), + description.width, description.height, &deleteImageProc, + &updateImageProc, &deleteImageCtx, false, format, false); + if (!backendTex.isValid()) { + RNSkia::RNSkLogger::logToConsole( + "Failed to convert HardwareBuffer to OpenGL Texture!"); + return nullptr; + } + sk_sp image = SkImages::BorrowTextureFrom( + _directContext.get(), backendTex, kTopLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, kOpaque_SkAlphaType, nullptr, deleteImageProc, + deleteImageCtx); + return image; +#else + throw std::runtime_error( + "HardwareBuffers are only supported on Android API 26 or higher! Set " + "your minSdk to 26 (or higher) and try again."); +#endif } std::unique_ptr MakeWindow(ANativeWindow *window, int width, int height) { - return RNSkia::SkiaOpenGLSurfaceFactory::makeContext(&_context, window, - width, height); + return std::make_unique( + _config, _display.get(), _ctx.get(), _directContext.get(), window, + width, height); } private: - RNSkia::SkiaOpenGLContext _context; + EGLConfig _config; + std::unique_ptr _display; + std::unique_ptr _ctx; + std::unique_ptr _surface; + sk_sp _directContext; OpenGLContext() { - RNSkia::SkiaOpenGLHelper::createSkiaDirectContextIfNecessary(&_context); + _display = std::make_unique(); + _config = _display->chooseConfig(); + _ctx = _display->makeContext(_config, nullptr); + _surface = _display->makePixelBufferSurface(_config, 1, 1); + _ctx->makeCurrent(*_surface); + auto backendInterface = GrGLMakeNativeInterface(); + _directContext = GrDirectContexts::MakeGL(backendInterface); + + if (_directContext == nullptr) { + throw std::runtime_error("GrDirectContexts::MakeGL failed"); + } } }; diff --git a/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.cpp b/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.cpp new file mode 100644 index 0000000000..cab7bef7e2 --- /dev/null +++ b/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.cpp @@ -0,0 +1,73 @@ +#include "OpenGLWindowContext.h" +#include "GrAHardwareBufferUtils.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" + +#include "include/gpu/ganesh/SkImageGanesh.h" +#include "include/gpu/ganesh/gl/GrGLBackendSurface.h" +#include "src/gpu/ganesh/gl/GrGLDefines.h" + +#pragma clang diagnostic pop + +namespace RNSkia { + +sk_sp OpenGLWindowContext::getSurface() { + if (_skSurface == nullptr) { + + struct ReleaseContext { + std::unique_ptr surface = nullptr; + }; + + auto releaseCtx = new ReleaseContext(); + releaseCtx->surface = _display->makeWindowSurface(_config, _window); + _surface = releaseCtx->surface.get(); + + // Now make this one current + _context->makeCurrent(*releaseCtx->surface); + + // Set up parameters for the render target so that it + // matches the underlying OpenGL context. + GrGLFramebufferInfo fboInfo; + + // We pass 0 as the framebuffer id, since the + // underlying Skia GrGlGpu will read this when wrapping the context in the + // render target and the GrGlGpu object. + fboInfo.fFBOID = 0; + fboInfo.fFormat = 0x8058; // GL_RGBA8 + + GLint stencil; + glGetIntegerv(GL_STENCIL_BITS, &stencil); + + GLint samples; + glGetIntegerv(GL_SAMPLES, &samples); + + auto colorType = kN32_SkColorType; + + auto maxSamples = + _directContext->maxSurfaceSampleCountForColorType(colorType); + + if (samples > maxSamples) { + samples = maxSamples; + } + + auto renderTarget = GrBackendRenderTargets::MakeGL(_width, _height, samples, + stencil, fboInfo); + + SkSurfaceProps props(0, kUnknown_SkPixelGeometry); + + + // Create surface object + _skSurface = SkSurfaces::WrapBackendRenderTarget( + _directContext, renderTarget, kBottomLeft_GrSurfaceOrigin, colorType, + nullptr, &props, + [](void *addr) { + auto releaseCtx = reinterpret_cast(addr); + delete releaseCtx; + }, + reinterpret_cast(releaseCtx)); + } + return _skSurface; +} + +} // namespace RNSkia diff --git a/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.h b/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.h new file mode 100644 index 0000000000..31cd6bdc81 --- /dev/null +++ b/packages/skia/android/cpp/rnskia-android/OpenGLWindowContext.h @@ -0,0 +1,74 @@ +#pragma once + +#include "RNSkLog.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "WindowContext.h" +#include "opengl/Display.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdocumentation" + +#include "include/core/SkCanvas.h" +#include "include/core/SkColorSpace.h" +#include "include/core/SkSurface.h" +#include "include/gpu/ganesh/GrBackendSurface.h" +#include "include/gpu/ganesh/GrDirectContext.h" +#include "include/gpu/ganesh/SkSurfaceGanesh.h" +#include "include/gpu/ganesh/gl/GrGLInterface.h" + +#pragma clang diagnostic pop + +namespace RNSkia { + +class OpenGLWindowContext : public WindowContext { +public: + OpenGLWindowContext(EGLConfig &config, Display *display, Context *context, + GrDirectContext *directContext, ANativeWindow *window, + int width, int height) + : _config(config), _display(display), _directContext(directContext), + _context(context), _window(window), _width(width), _height(height) {} + + ~OpenGLWindowContext() { ANativeWindow_release(_window); } + + sk_sp getSurface() override; + + void present() override { + _context->makeCurrent(*_surface); + _directContext->flushAndSubmit(); + _surface->Present(); + } + + void resize(int width, int height) override { + _skSurface = nullptr; + _width = width; + _height = height; + } + + int getWidth() override { return _width; }; + + int getHeight() override { return _height; }; + +private: + ANativeWindow *_window; + sk_sp _skSurface = nullptr; + EGLConfig _config; + Context *_context; + Surface* _surface; + Display *_display; + GrDirectContext *_directContext; + int _width = 0; + int _height = 0; +}; + +} // namespace RNSkia diff --git a/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp b/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp index 660f9ecef9..e932e35fcf 100644 --- a/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp +++ b/packages/skia/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp @@ -71,7 +71,6 @@ bool RNSkOpenGLCanvasProvider::renderToCanvas( return false; } } - return false; } diff --git a/packages/skia/android/cpp/rnskia-android/SkiaOpenGLHelper.h b/packages/skia/android/cpp/rnskia-android/SkiaOpenGLHelper.h deleted file mode 100644 index d68119e4ea..0000000000 --- a/packages/skia/android/cpp/rnskia-android/SkiaOpenGLHelper.h +++ /dev/null @@ -1,310 +0,0 @@ -#pragma once - -#include "EGL/egl.h" -#include "GLES2/gl2.h" -#include -#include - -#include - -#include "RNSkLog.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdocumentation" - -#include "include/core/SkCanvas.h" -#include "include/core/SkColorSpace.h" -#include "include/core/SkSurface.h" -#include "include/gpu/ganesh/GrDirectContext.h" -#include "include/gpu/ganesh/gl/GrGLDirectContext.h" -#include "include/gpu/ganesh/gl/GrGLInterface.h" - -#pragma clang diagnostic pop - -namespace RNSkia { - -/** - * Singleton holding the default display and shared eglContext that will be the - * first context we create so that we can share data between contexts. - */ -class OpenGLResourceHolder { -private: - OpenGLResourceHolder() { - // Initialize OpenGL - glDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (glDisplay == EGL_NO_DISPLAY) { - RNSkLogger::logToConsole("eglGetDisplay failed : %i", glGetError()); - return; - } - - EGLint major; - EGLint minor; - if (eglInitialize(glDisplay, &major, &minor) != EGL_TRUE) { - RNSkLogger::logToConsole("eglInitialize failed : %i", glGetError()); - return; - } - - // Create a default shared context - glConfig = getConfig(glDisplay); - - // Create OpenGL context attributes - EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - - // Initialize the offscreen context for this thread - glContext = - eglCreateContext(glDisplay, glConfig, glContext, contextAttribs); - if (glContext == EGL_NO_CONTEXT) { - RNSkLogger::logToConsole("eglCreateContext failed : %i", glGetError()); - } - } - - ~OpenGLResourceHolder() { - if (glContext != EGL_NO_CONTEXT) { - eglDestroyContext(glDisplay, glContext); - glContext = EGL_NO_CONTEXT; - } - - if (glDisplay != EGL_NO_DISPLAY) { - eglTerminate(glDisplay); - glDisplay = EGL_NO_DISPLAY; - } - } - /* Explicitly disallow copying. */ - OpenGLResourceHolder(const OpenGLResourceHolder &) = delete; - OpenGLResourceHolder &operator=(const OpenGLResourceHolder &) = delete; - -public: - static OpenGLResourceHolder &getInstance() { - static OpenGLResourceHolder Instance; - return Instance; - } - - /** - * The first context created will be considered the parent / shared context - * and will be used as the parent / shareable context when creating subsequent - * contexts. - */ - std::atomic glContext = EGL_NO_CONTEXT; - /** - * Shared egl display - */ - std::atomic glDisplay = EGL_NO_DISPLAY; - - /** - * Shared eglConfig - */ - std::atomic glConfig = 0; - -private: - /** - * Finds the correct EGL Config for the given parameters - * @param glDisplay - * @return Config or zero if no matching context could be found. - */ - static EGLConfig getConfig(EGLDisplay glDisplay) { - - EGLint att[] = {EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES2_BIT, - EGL_ALPHA_SIZE, - 8, - EGL_BLUE_SIZE, - 8, - EGL_GREEN_SIZE, - 8, - EGL_RED_SIZE, - 8, - EGL_DEPTH_SIZE, - 0, - EGL_STENCIL_SIZE, - 0, - EGL_SAMPLE_BUFFERS, - 0, - EGL_NONE}; - - EGLint numConfigs; - EGLConfig glConfig = 0; - if (eglChooseConfig(glDisplay, att, &glConfig, 1, &numConfigs) != - EGL_TRUE || - numConfigs == 0) { - RNSkLogger::logToConsole( - "Failed to choose a config for %s surface. Error code: %d\n", - eglGetError()); - return 0; - } - - return glConfig; - } -}; - -struct SkiaOpenGLContext { - SkiaOpenGLContext() { - glContext = EGL_NO_CONTEXT; - gl1x1Surface = EGL_NO_SURFACE; - directContext = nullptr; - } - ~SkiaOpenGLContext() { - if (gl1x1Surface != EGL_NO_SURFACE) { - eglDestroySurface(OpenGLResourceHolder::getInstance().glDisplay, - gl1x1Surface); - gl1x1Surface = EGL_NO_SURFACE; - } - - if (directContext) { - directContext->releaseResourcesAndAbandonContext(); - directContext = nullptr; - } - - if (glContext != EGL_NO_CONTEXT) { - eglDestroyContext(OpenGLResourceHolder::getInstance().glDisplay, - glContext); - glContext = EGL_NO_CONTEXT; - } - } - EGLContext glContext; - EGLSurface gl1x1Surface; - sk_sp directContext; -}; - -class SkiaOpenGLHelper { -public: - /** - * Calls eglMakeCurrent on the surface provided using the provided - * thread context. - * @param context Skia OpenGL context to use - * @param surface Surface to set as current - * @return true if eglMakeCurrent was successfull - */ - static bool makeCurrent(SkiaOpenGLContext *context, EGLSurface glSurface) { - // We don't need to call make current if we already are current: - if (eglGetCurrentSurface(EGL_DRAW) != glSurface || - eglGetCurrentSurface(EGL_READ) != glSurface || - eglGetCurrentContext() != context->glContext) { - - // Make current! - if (eglMakeCurrent(OpenGLResourceHolder::getInstance().glDisplay, - glSurface, glSurface, - context->glContext) != EGL_TRUE) { - RNSkLogger::logToConsole("eglMakeCurrent failed: %d\n", eglGetError()); - return false; - } - } - return true; - } - - /** - * Creates a new windowed surface - * @param window ANativeWindow to create surface in - * @return EGLSurface or EGL_NO_SURFACE if the call failed - */ - static EGLSurface createWindowedSurface(ANativeWindow *window) { - const EGLint attribs[] = {EGL_NONE}; - return eglCreateWindowSurface(OpenGLResourceHolder::getInstance().glDisplay, - OpenGLResourceHolder::getInstance().glConfig, - window, attribs); - } - - /** - * Destroys an egl surface - * @param glSurface - * @return - */ - static bool destroySurface(EGLSurface glSurface) { - if (eglMakeCurrent(OpenGLResourceHolder::getInstance().glDisplay, - EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT) != EGL_TRUE) { - RNSkLogger::logToConsole( - "destroySurface: Could not clear selected surface"); - return false; - } - return eglDestroySurface(OpenGLResourceHolder::getInstance().glDisplay, - glSurface) == EGL_TRUE; - } - - /** - * Calls the eglSwapBuffer in the current thread with the provided surface - * @param context Thread context - * @param glSurface surface to present - * @return true if eglSwapBuffers succeeded. - */ - static bool swapBuffers(SkiaOpenGLContext *context, EGLSurface glSurface) { - if (eglSwapBuffers(OpenGLResourceHolder::getInstance().glDisplay, - glSurface) != EGL_TRUE) { - RNSkLogger::logToConsole("eglSwapBuffers failed: %d\n", eglGetError()); - return false; - } - return true; - } - - /*** - * Creates a new Skia direct context backed by the provided eglContext in the - * SkiaOpenGLContext. - * @param context Context to store results in - * @param sharedContext Shared Context - * @return true if the call to create a skia direct context suceeded. - */ - static bool createSkiaDirectContextIfNecessary(SkiaOpenGLContext *context) { - if (context->directContext == nullptr) { - - // Create OpenGL context - createOpenGLContext(context); - - // Create attributes for a simple 1x1 pbuffer surface that we can - // use to activate and create Skia direct context for - const EGLint offScreenSurfaceAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, - EGL_NONE}; - - context->gl1x1Surface = - eglCreatePbufferSurface(OpenGLResourceHolder::getInstance().glDisplay, - OpenGLResourceHolder::getInstance().glConfig, - offScreenSurfaceAttribs); - - if (context->gl1x1Surface == EGL_NO_SURFACE) { - RNSkLogger::logToConsole("Failed creating a 1x1 pbuffer surface"); - return false; - } - - // Activate - if (!makeCurrent(context, context->gl1x1Surface)) { - return false; - } - - // Create the Skia context - auto backendInterface = GrGLMakeNativeInterface(); - context->directContext = GrDirectContexts::MakeGL(backendInterface); - - if (context->directContext == nullptr) { - RNSkLogger::logToConsole("GrDirectContexts::MakeGL failed"); - return false; - } - } - - // It all went well! - return true; - } - -private: - /** - * Creates a new GLContext. - * @param context Context to save results in - * @return True if the call to eglCreateContext returned a valid OpenGL - * Context or if the context already is setup. - */ - static bool createOpenGLContext(SkiaOpenGLContext *context) { - // Create OpenGL context attributes - EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - - // Initialize the offscreen context for this thread - context->glContext = eglCreateContext( - OpenGLResourceHolder::getInstance().glDisplay, - OpenGLResourceHolder::getInstance().glConfig, - OpenGLResourceHolder::getInstance().glContext, contextAttribs); - - if (context->glContext == EGL_NO_CONTEXT) { - RNSkLogger::logToConsole("eglCreateContext failed: %d\n", eglGetError()); - return EGL_NO_CONTEXT; - } - - return true; - } -}; -} // namespace RNSkia diff --git a/packages/skia/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp b/packages/skia/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp deleted file mode 100644 index f536a441c1..0000000000 --- a/packages/skia/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include "SkiaOpenGLSurfaceFactory.h" -#include "GrAHardwareBufferUtils.h" -#include "SkiaOpenGLHelper.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdocumentation" - -#include "include/gpu/ganesh/SkImageGanesh.h" -#include "include/gpu/ganesh/gl/GrGLBackendSurface.h" -#include "src/gpu/ganesh/gl/GrGLDefines.h" - -#pragma clang diagnostic pop - -namespace RNSkia { - -sk_sp SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer( - SkiaOpenGLContext *context, void *buffer, bool requireKnownFormat) { -#if __ANDROID_API__ >= 26 - const AHardwareBuffer *hardwareBuffer = - static_cast(buffer); - DeleteImageProc deleteImageProc = nullptr; - UpdateImageProc updateImageProc = nullptr; - TexImageCtx deleteImageCtx = nullptr; - - AHardwareBuffer_Desc description; - AHardwareBuffer_describe(hardwareBuffer, &description); - GrBackendFormat format; - switch (description.format) { - // TODO: find out if we can detect, which graphic buffers support - // GR_GL_TEXTURE_2D - case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: - format = GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL); - case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: - format = GrBackendFormats::MakeGL(GR_GL_RGBA16F, GR_GL_TEXTURE_EXTERNAL); - case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: - format = GrBackendFormats::MakeGL(GR_GL_RGB565, GR_GL_TEXTURE_EXTERNAL); - case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: - format = GrBackendFormats::MakeGL(GR_GL_RGB10_A2, GR_GL_TEXTURE_EXTERNAL); - case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: - format = GrBackendFormats::MakeGL(GR_GL_RGB8, GR_GL_TEXTURE_EXTERNAL); -#if __ANDROID_API__ >= 33 - case AHARDWAREBUFFER_FORMAT_R8_UNORM: - format = GrBackendFormats::MakeGL(GR_GL_R8, GR_GL_TEXTURE_EXTERNAL); -#endif - default: - if (requireKnownFormat) { - format = GrBackendFormat(); - } else { - format = GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL); - } - } - - auto backendTex = MakeGLBackendTexture( - context->directContext.get(), - const_cast(hardwareBuffer), description.width, - description.height, &deleteImageProc, &updateImageProc, &deleteImageCtx, - false, format, false); - if (!backendTex.isValid()) { - RNSkLogger::logToConsole( - "Failed to convert HardwareBuffer to OpenGL Texture!"); - return nullptr; - } - sk_sp image = SkImages::BorrowTextureFrom( - context->directContext.get(), backendTex, kTopLeft_GrSurfaceOrigin, - kRGBA_8888_SkColorType, kOpaque_SkAlphaType, nullptr, deleteImageProc, - deleteImageCtx); - return image; -#else - throw std::runtime_error( - "HardwareBuffers are only supported on Android API 26 or higher! Set " - "your minSdk to 26 (or higher) and try again."); -#endif -} - -sk_sp -SkiaOpenGLSurfaceFactory::makeOffscreenSurface(SkiaOpenGLContext *context, - int width, int height) { - - auto colorType = kN32_SkColorType; - - SkSurfaceProps props(0, kUnknown_SkPixelGeometry); - - if (!SkiaOpenGLHelper::makeCurrent(context, context->gl1x1Surface)) { - RNSkLogger::logToConsole( - "Could not create EGL Surface from native window / surface. Could " - "not set new surface as current surface."); - return nullptr; - } - - // Create texture - auto texture = context->directContext->createBackendTexture( - width, height, colorType, skgpu::Mipmapped::kNo, GrRenderable::kYes); - - if (!texture.isValid()) { - RNSkLogger::logToConsole("couldn't create offscreen texture %dx%d", width, - height); - } - - struct ReleaseContext { - SkiaOpenGLContext *context; - GrBackendTexture texture; - }; - - auto releaseCtx = new ReleaseContext({context, texture}); - - // Create a SkSurface from the GrBackendTexture - return SkSurfaces::WrapBackendTexture( - context->directContext.get(), texture, kTopLeft_GrSurfaceOrigin, 0, - colorType, nullptr, &props, - [](void *addr) { - auto releaseCtx = reinterpret_cast(addr); - - releaseCtx->context->directContext->deleteBackendTexture( - releaseCtx->texture); - }, - releaseCtx); -} - -sk_sp AndroidSkiaContext::getSurface() { - if (_skSurface == nullptr) { - - // Now we can create a surface - _glSurface = SkiaOpenGLHelper::createWindowedSurface(_window); - if (_glSurface == EGL_NO_SURFACE) { - RNSkLogger::logToConsole( - "Could not create EGL Surface from native window / surface."); - return nullptr; - } - - // Now make this one current - if (!SkiaOpenGLHelper::makeCurrent(_context, _glSurface)) { - RNSkLogger::logToConsole( - "Could not create EGL Surface from native window / surface. Could " - "not set new surface as current surface."); - return nullptr; - } - - // Set up parameters for the render target so that it - // matches the underlying OpenGL context. - GrGLFramebufferInfo fboInfo; - - // We pass 0 as the framebuffer id, since the - // underlying Skia GrGlGpu will read this when wrapping the context in the - // render target and the GrGlGpu object. - fboInfo.fFBOID = 0; - fboInfo.fFormat = 0x8058; // GL_RGBA8 - - GLint stencil; - glGetIntegerv(GL_STENCIL_BITS, &stencil); - - GLint samples; - glGetIntegerv(GL_SAMPLES, &samples); - - auto colorType = kN32_SkColorType; - - auto maxSamples = - _context->directContext->maxSurfaceSampleCountForColorType(colorType); - - if (samples > maxSamples) { - samples = maxSamples; - } - - auto renderTarget = GrBackendRenderTargets::MakeGL(_width, _height, samples, - stencil, fboInfo); - - SkSurfaceProps props(0, kUnknown_SkPixelGeometry); - - struct ReleaseContext { - EGLSurface glSurface; - }; - - auto releaseCtx = new ReleaseContext({_glSurface}); - - // Create surface object - _skSurface = SkSurfaces::WrapBackendRenderTarget( - _context->directContext.get(), renderTarget, - kBottomLeft_GrSurfaceOrigin, colorType, nullptr, &props, - [](void *addr) { - auto releaseCtx = reinterpret_cast(addr); - SkiaOpenGLHelper::destroySurface(releaseCtx->glSurface); - delete releaseCtx; - }, - reinterpret_cast(releaseCtx)); - } - return _skSurface; -} - -} // namespace RNSkia diff --git a/packages/skia/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h b/packages/skia/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h deleted file mode 100644 index 3e2e343fa1..0000000000 --- a/packages/skia/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include "RNSkLog.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "SkiaOpenGLHelper.h" -#include "WindowContext.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdocumentation" - -#include "include/core/SkCanvas.h" -#include "include/core/SkColorSpace.h" -#include "include/core/SkSurface.h" -#include "include/gpu/ganesh/GrBackendSurface.h" -#include "include/gpu/ganesh/GrDirectContext.h" -#include "include/gpu/ganesh/SkSurfaceGanesh.h" -#include "include/gpu/ganesh/gl/GrGLInterface.h" - -#pragma clang diagnostic pop - -namespace RNSkia { - -class AndroidSkiaContext : public WindowContext { -public: - AndroidSkiaContext(SkiaOpenGLContext *context, ANativeWindow *window, - int width, int height) - : _context(context), _window(window), _width(width), _height(height) {} - - ~AndroidSkiaContext() { ANativeWindow_release(_window); } - - sk_sp getSurface() override; - - void present() override { - if (!SkiaOpenGLHelper::makeCurrent(_context, _glSurface)) { - RNSkLogger::logToConsole( - "Could not create EGL Surface from native window / surface. Could " - "not set new surface as current surface."); - return; - } - // Flush and submit the direct context - _context->directContext->flushAndSubmit(); - - // Swap buffers - SkiaOpenGLHelper::swapBuffers(_context, _glSurface); - } - - void resize(int width, int height) override { - _skSurface = nullptr; - _width = width; - _height = height; - } - - int getWidth() override { return _width; }; - - int getHeight() override { return _height; }; - -private: - ANativeWindow *_window; - sk_sp _skSurface = nullptr; - EGLSurface _glSurface = EGL_NO_SURFACE; - SkiaOpenGLContext *_context; - int _width = 0; - int _height = 0; -}; - -class SkiaOpenGLSurfaceFactory { -public: - /** - * Creates a new Skia surface that is backed by a texture. - * @param width Width of surface - * @param height Height of surface - * @return An SkSurface backed by a texture. - */ - static sk_sp makeOffscreenSurface(SkiaOpenGLContext *context, - int width, int height); - - static sk_sp - makeImageFromHardwareBuffer(SkiaOpenGLContext *context, void *buffer, - bool requireKnownFormat = false); - - static std::unique_ptr - makeContext(SkiaOpenGLContext *context, ANativeWindow *surface, int width, - int height) { - return std::make_unique(context, surface, width, - height); - } -}; - -} // namespace RNSkia diff --git a/packages/skia/android/cpp/rnskia-android/opengl/Context.h b/packages/skia/android/cpp/rnskia-android/opengl/Context.h new file mode 100644 index 0000000000..3c73096249 --- /dev/null +++ b/packages/skia/android/cpp/rnskia-android/opengl/Context.h @@ -0,0 +1,77 @@ +#pragma once + +#include "opengl/Error.h" +#include "opengl/Surface.h" + +namespace RNSkia { + +class Surface; +class Display; + +class Context { +public: + ~Context() { + if (_context != EGL_NO_CONTEXT) { + if (eglDestroyContext(_display, _context) != EGL_TRUE) { + LOG_EGL_ERROR; + } + } + } + + bool isValid() const { return _context != EGL_NO_CONTEXT; } + + const EGLContext &getHandle() const { return _context; } + + bool makeCurrent(const Surface &surface) const { + if (_context == EGL_NO_CONTEXT) { + return false; + } + const auto result = + eglMakeCurrentIfNecessary(_display, surface.getHandle(), + surface.getHandle(), _context) == EGL_TRUE; + if (!result) { + LOG_EGL_ERROR; + } + return result; + } + + bool clearCurrent() const { + const auto result = + eglMakeCurrentIfNecessary(_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT) == EGL_TRUE; + if (!result) { + LOG_EGL_ERROR; + } + return result; + } + + bool isCurrent() const { return eglGetCurrentContext() == _context; } + +private: + friend class Display; + + EGLDisplay _display = EGL_NO_DISPLAY; + EGLContext _context = EGL_NO_CONTEXT; + + static EGLBoolean eglMakeCurrentIfNecessary(EGLDisplay display, + EGLSurface draw, EGLSurface read, + EGLContext context) { + if (display != eglGetCurrentDisplay() || + draw != eglGetCurrentSurface(EGL_DRAW) || + read != eglGetCurrentSurface(EGL_READ) || + context != eglGetCurrentContext()) { + return eglMakeCurrent(display, draw, read, context); + } + + return EGL_TRUE; + } + + Context(EGLDisplay display, EGLContext context) + : _display(display), _context(context) {} + + Context(const Context &) = delete; + + Context &operator=(const Context &) = delete; +}; + +} // namespace RNSkia \ No newline at end of file diff --git a/packages/skia/android/cpp/rnskia-android/opengl/Display.h b/packages/skia/android/cpp/rnskia-android/opengl/Display.h new file mode 100644 index 0000000000..06a3c55920 --- /dev/null +++ b/packages/skia/android/cpp/rnskia-android/opengl/Display.h @@ -0,0 +1,117 @@ +#pragma once + +#include + +#include "EGL/egl.h" +#include "GLES2/gl2.h" + +#include "opengl/Context.h" +#include "opengl/Error.h" + +namespace RNSkia { + +class Context; +class Surface; + +class Display { +public: + Display() { + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + if (eglInitialize(display, nullptr, nullptr) != EGL_TRUE) { + LOG_EGL_ERROR; + return; + } + _display = display; + } + + ~Display() { + if (_display != EGL_NO_DISPLAY) { + if (eglTerminate(_display) != EGL_TRUE) { + LOG_EGL_ERROR; + } + } + } + + bool isValid() const { return _display != EGL_NO_DISPLAY; } + + EGLConfig chooseConfig() const { + + EGLint att[] = {EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_ALPHA_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_RED_SIZE, + 8, + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_SAMPLE_BUFFERS, + 0, + EGL_NONE}; + + EGLint numConfigs; + EGLConfig glConfig = 0; + if (eglChooseConfig(_display, att, &glConfig, 1, &numConfigs) != EGL_TRUE || + numConfigs == 0) { + LOG_EGL_ERROR; + return 0; + } + + return glConfig; + } + + std::unique_ptr makeContext(const EGLConfig &config, + const Context *share_context) { + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + auto context = eglCreateContext( + _display, config, + share_context != nullptr ? share_context->getHandle() : nullptr, + contextAttribs); + + if (context == EGL_NO_CONTEXT) { + LOG_EGL_ERROR; + return nullptr; + } + return std::unique_ptr(new Context(_display, context)); + } + + std::unique_ptr makeWindowSurface(const EGLConfig &config, + EGLNativeWindowType window) { + const EGLint attribs[] = {EGL_NONE}; + auto surface = eglCreateWindowSurface(_display, config, window, attribs); + if (surface == EGL_NO_SURFACE) { + LOG_EGL_ERROR; + return nullptr; + } + return std::unique_ptr(new Surface(_display, surface)); + } + + std::unique_ptr makePixelBufferSurface(const EGLConfig &config, + size_t width, size_t height) { + const EGLint attribs[] = {EGL_WIDTH, static_cast(width), EGL_HEIGHT, + static_cast(height), EGL_NONE}; + auto surface = eglCreatePbufferSurface(_display, config, attribs); + if (surface == EGL_NO_SURFACE) { + LOG_EGL_ERROR; + return nullptr; + } + return std::unique_ptr(new Surface(_display, surface)); + } + + const EGLDisplay &getHandle() const { return _display; } + +private: + EGLDisplay _display = EGL_NO_DISPLAY; + + Display(const Display &) = delete; + + Display &operator=(const Display &) = delete; +}; + +} // namespace RNSkia diff --git a/packages/skia/android/cpp/rnskia-android/opengl/Error.cpp b/packages/skia/android/cpp/rnskia-android/opengl/Error.cpp new file mode 100644 index 0000000000..3ceff91b71 --- /dev/null +++ b/packages/skia/android/cpp/rnskia-android/opengl/Error.cpp @@ -0,0 +1,9 @@ +#include "opengl/Error.h" + +#include "RNSkLog.h" + +void LogEGLError(const char *file, int line) { + const auto error = eglGetError(); + RNSkia::RNSkLogger::logToConsole("EGL Error: %s (%d) in %s:%d", + EGLErrorToString(error), error, file, line); +} \ No newline at end of file diff --git a/packages/skia/android/cpp/rnskia-android/opengl/Error.h b/packages/skia/android/cpp/rnskia-android/opengl/Error.h new file mode 100644 index 0000000000..2e08851e95 --- /dev/null +++ b/packages/skia/android/cpp/rnskia-android/opengl/Error.h @@ -0,0 +1,44 @@ +#pragma once + +#include "EGL/egl.h" +#include "GLES2/gl2.h" + +#define LOG_EGL_ERROR LogEGLError(__FILE__, __LINE__); + +static const char *EGLErrorToString(EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "Success"; + case EGL_NOT_INITIALIZED: + return "Not Initialized"; + case EGL_BAD_ACCESS: + return "Bad Access"; + case EGL_BAD_ALLOC: + return "Bad Alloc"; + case EGL_BAD_ATTRIBUTE: + return "Bad Attribute"; + case EGL_BAD_CONTEXT: + return "Bad Context"; + case EGL_BAD_CONFIG: + return "Bad Config"; + case EGL_BAD_CURRENT_SURFACE: + return "Bad Current Surface"; + case EGL_BAD_DISPLAY: + return "Bad Display"; + case EGL_BAD_SURFACE: + return "Bad Surface"; + case EGL_BAD_MATCH: + return "Bad Match"; + case EGL_BAD_PARAMETER: + return "Bad Parameter"; + case EGL_BAD_NATIVE_PIXMAP: + return "Bad Native Pixmap"; + case EGL_BAD_NATIVE_WINDOW: + return "Bad Native Window"; + case EGL_CONTEXT_LOST: + return "Context Lost"; + } + return "Unknown"; +} + +void LogEGLError(const char *file, int line); \ No newline at end of file diff --git a/packages/skia/android/cpp/rnskia-android/opengl/Surface.h b/packages/skia/android/cpp/rnskia-android/opengl/Surface.h new file mode 100644 index 0000000000..38fb5157b6 --- /dev/null +++ b/packages/skia/android/cpp/rnskia-android/opengl/Surface.h @@ -0,0 +1,43 @@ +#pragma once + +#include "opengl/Error.h" + +namespace RNSkia { + +class Surface { +public: + ~Surface() { + if (_surface != EGL_NO_SURFACE) { + if (eglDestroySurface(_display, _surface) != EGL_TRUE) { + LOG_EGL_ERROR; + } + } + } + + bool isValid() { return _surface != EGL_NO_SURFACE; } + + const EGLSurface &getHandle() const { return _surface; } + + bool Present() const { + const auto result = eglSwapBuffers(_display, _surface) == EGL_TRUE; + if (!result) { + LOG_EGL_ERROR; + } + return result; + } + +private: + friend class Display; + + EGLDisplay _display = EGL_NO_DISPLAY; + EGLSurface _surface = EGL_NO_SURFACE; + + Surface(EGLDisplay display, EGLSurface surface) + : _display(display), _surface(surface) {} + + Surface(const Surface &) = delete; + + Surface &operator=(const Surface &) = delete; +}; + +} // Namespace RNSkia \ No newline at end of file diff --git a/packages/skia/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm b/packages/skia/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm index 6e2b6fa064..67a4345518 100644 --- a/packages/skia/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm +++ b/packages/skia/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm @@ -23,16 +23,12 @@ #include #if TARGET_RT_BIG_ENDIAN #define FourCC2Str(fourcc) \ - (const char[]) { \ - *((char *)&fourcc), *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 2), \ - *(((char *)&fourcc) + 3), 0 \ - } + (const char[]){*((char *)&fourcc), *(((char *)&fourcc) + 1), \ + *(((char *)&fourcc) + 2), *(((char *)&fourcc) + 3), 0} #else #define FourCC2Str(fourcc) \ - (const char[]) { \ - *(((char *)&fourcc) + 3), *(((char *)&fourcc) + 2), \ - *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 0), 0 \ - } + (const char[]){*(((char *)&fourcc) + 3), *(((char *)&fourcc) + 2), \ + *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 0), 0} #endif // pragma MARK: TextureHolder diff --git a/packages/skia/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.h b/packages/skia/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.h index 7107066562..53d7010fb3 100644 --- a/packages/skia/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.h +++ b/packages/skia/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.h @@ -109,9 +109,13 @@ class IOSSkiaContext : public RNSkia::WindowContext { void resize(int width, int height) override { _skSurface = nullptr; } - int getWidth() override { return _layer.frame.size.width * _layer.contentsScale; }; + int getWidth() override { + return _layer.frame.size.width * _layer.contentsScale; + }; - int getHeight() override { return _layer.frame.size.height * _layer.contentsScale; }; + int getHeight() override { + return _layer.frame.size.height * _layer.contentsScale; + }; private: SkiaMetalContext *_context;