diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index fb6c4be320..b75d662c53 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -42,9 +42,34 @@ */ namespace filament::backend { +/** + * Requests a SwapChain with an alpha channel. + */ static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1; + +/** + * This flag indicates that the swap chain may be used as a source surface + * for reading back render results. This config flag must be set when creating + * any SwapChain that will be used as the source for a blit operation. + */ static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2; + +/** + * Indicates that the native X11 window is an XCB window rather than an XLIB window. + * This is ignored on non-Linux platforms and in builds that support only one X11 API. + */ static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4; + +/** + * Indicates that the native window is a CVPixelBufferRef. + * + * This is only supported by the Metal backend. The CVPixelBuffer must be in the + * kCVPixelFormatType_32BGRA format. + * + * It is not necessary to add an additional retain call before passing the pixel buffer to + * Filament. Filament will call CVPixelBufferRetain during Engine::createSwapChain, and + * CVPixelBufferRelease when the swap chain is destroyed. + */ static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8; static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES. diff --git a/filament/backend/include/backend/platforms/PlatformEGL.h b/filament/backend/include/backend/platforms/PlatformEGL.h index a4505c42ab..e72d3ac246 100644 --- a/filament/backend/include/backend/platforms/PlatformEGL.h +++ b/filament/backend/include/backend/platforms/PlatformEGL.h @@ -97,15 +97,20 @@ protected: EGLSurface mCurrentReadSurface = EGL_NO_SURFACE; EGLSurface mEGLDummySurface = EGL_NO_SURFACE; EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR; - EGLConfig mEGLTransparentConfig = EGL_NO_CONFIG_KHR; // supported extensions detected at runtime struct { - bool OES_EGL_image_external_essl3 = false; + struct { + bool OES_EGL_image_external_essl3 = false; + } gl; + struct { + bool KHR_no_config_context = false; + } egl; } ext; private: void initializeGlExtensions() noexcept; + EGLConfig findSwapChainConfig(uint64_t flags) const; }; } // namespace filament::backend diff --git a/filament/backend/src/opengl/platforms/PlatformEGL.cpp b/filament/backend/src/opengl/platforms/PlatformEGL.cpp index 1fc5ffeb52..9aa6642d76 100644 --- a/filament/backend/src/opengl/platforms/PlatformEGL.cpp +++ b/filament/backend/src/opengl/platforms/PlatformEGL.cpp @@ -102,6 +102,8 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon auto extensions = GLUtils::split(eglQueryString(mEGLDisplay, EGL_EXTENSIONS)); + ext.egl.KHR_no_config_context = extensions.has("EGL_KHR_no_config_context"); + eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress("eglCreateSyncKHR"); eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress("eglDestroySyncKHR"); eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress("eglClientWaitSyncKHR"); @@ -115,7 +117,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon EGL_RED_SIZE, 8, // 2 EGL_GREEN_SIZE, 8, // 4 EGL_BLUE_SIZE, 8, // 6 - EGL_ALPHA_SIZE, 0, // 8 : reserved to set ALPHA_SIZE below + EGL_ALPHA_SIZE, 8, // 8 EGL_DEPTH_SIZE, 24, // 10 EGL_RECORDABLE_ANDROID, 1, // 12 EGL_NONE // 14 @@ -142,66 +144,46 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon } #endif - EGLConfig eglConfig = nullptr; + // config use for creating the context + EGLConfig eglConfig = EGL_NO_CONFIG_KHR; - // find an opaque config + // find a config we can use if we don't have "EGL_KHR_no_config_context" and that we can use + // for the dummy pbuffer surface. if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLConfig, 1, &configsCount)) { logEglError("eglChooseConfig"); goto error; } if (configsCount == 0) { - // warn and retry without EGL_RECORDABLE_ANDROID - logEglError("eglChooseConfig(..., EGL_RECORDABLE_ANDROID) failed. Continuing without it."); - configAttribs[12] = EGL_RECORDABLE_ANDROID; - configAttribs[13] = EGL_DONT_CARE; - if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLConfig, 1, &configsCount) || - configsCount == 0) { - logEglError("eglChooseConfig"); - goto error; - } - } - - // find a transparent config - configAttribs[8] = EGL_ALPHA_SIZE; - configAttribs[9] = 8; - if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLTransparentConfig, 1, &configsCount) || - (configAttribs[13] == EGL_DONT_CARE && configsCount == 0)) { - logEglError("eglChooseConfig"); - goto error; - } - - if (configsCount == 0) { - // warn and retry without EGL_RECORDABLE_ANDROID + // warn and retry without EGL_RECORDABLE_ANDROID logEglError("eglChooseConfig(..., EGL_RECORDABLE_ANDROID) failed. Continuing without it."); - // this is not fatal - configAttribs[12] = EGL_RECORDABLE_ANDROID; - configAttribs[13] = EGL_DONT_CARE; - if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLTransparentConfig, 1, &configsCount) || - configsCount == 0) { - logEglError("eglChooseConfig"); - goto error; - } + // this is not fatal + configAttribs[12] = EGL_RECORDABLE_ANDROID; + configAttribs[13] = EGL_DONT_CARE; + if (!eglChooseConfig(mEGLDisplay, configAttribs, &mEGLConfig, 1, &configsCount) || + configsCount == 0) { + logEglError("eglChooseConfig"); + goto error; + } } - if (!extensions.has("EGL_KHR_no_config_context")) { - // if we have the EGL_KHR_no_config_context, we don't need to worry about the config - // when creating the context, otherwise, we must always pick a transparent config. - eglConfig = mEGLConfig = mEGLTransparentConfig; + if (UTILS_UNLIKELY(!ext.egl.KHR_no_config_context)) { + // if we don't have the EGL_KHR_no_config_context the context must be created with + // the same config as the swapchain, so we have no choice but to create a + // transparent config. + eglConfig = mEGLConfig; } - // the pbuffer dummy surface is always created with a transparent surface because - // either we have EGL_KHR_no_config_context and it doesn't matter, or we don't and - // we must use a transparent surface - mEGLDummySurface = eglCreatePbufferSurface(mEGLDisplay, mEGLTransparentConfig, pbufferAttribs); - if (mEGLDummySurface == EGL_NO_SURFACE) { + // create the dummy surface, just for being able to make the context current. + mEGLDummySurface = eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, pbufferAttribs); + if (UTILS_UNLIKELY(mEGLDummySurface == EGL_NO_SURFACE)) { logEglError("eglCreatePbufferSurface"); goto error; } mEGLContext = eglCreateContext(mEGLDisplay, eglConfig, (EGLContext)sharedContext, contextAttribs); - if (mEGLContext == EGL_NO_CONTEXT && sharedContext && - extensions.has("EGL_KHR_create_context_no_error")) { + if (UTILS_UNLIKELY(mEGLContext == EGL_NO_CONTEXT && sharedContext && + extensions.has("EGL_KHR_create_context_no_error"))) { // context creation could fail because of EGL_CONTEXT_OPENGL_NO_ERROR_KHR // not matching the sharedContext. Try with it. contextAttribs[2] = EGL_CONTEXT_OPENGL_NO_ERROR_KHR; @@ -214,7 +196,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon goto error; } - if (!makeCurrent(mEGLDummySurface, mEGLDummySurface)) { + if (UTILS_UNLIKELY(!makeCurrent(mEGLDummySurface, mEGLDummySurface))) { // eglMakeCurrent failed logEglError("eglMakeCurrent"); goto error; @@ -263,36 +245,91 @@ void PlatformEGL::terminate() noexcept { eglReleaseThread(); } +EGLConfig PlatformEGL::findSwapChainConfig(uint64_t flags) const { + EGLConfig config = EGL_NO_CONFIG_KHR; + EGLint configsCount; + EGLint configAttribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, (flags & SWAP_CHAIN_CONFIG_TRANSPARENT) ? 8 : 0, + EGL_DEPTH_SIZE, 24, + EGL_RECORDABLE_ANDROID, 1, + EGL_NONE + }; + + if (UTILS_UNLIKELY( + !eglChooseConfig(mEGLDisplay, configAttribs, &config, 1, &configsCount))) { + logEglError("eglChooseConfig"); + return EGL_NO_CONFIG_KHR; + } + + if (UTILS_UNLIKELY(configsCount == 0)) { + // warn and retry without EGL_RECORDABLE_ANDROID + logEglError( + "eglChooseConfig(..., EGL_RECORDABLE_ANDROID) failed. Continuing without it."); + configAttribs[12] = EGL_RECORDABLE_ANDROID; + configAttribs[13] = EGL_DONT_CARE; + if (UTILS_UNLIKELY( + !eglChooseConfig(mEGLDisplay, configAttribs, &config, 1, &configsCount) || + configsCount == 0)) { + logEglError("eglChooseConfig"); + return EGL_NO_CONFIG_KHR; + } + } + return config; +} + Platform::SwapChain* PlatformEGL::createSwapChain( void* nativeWindow, uint64_t flags) noexcept { - EGLSurface sur = eglCreateWindowSurface(mEGLDisplay, - (flags & SWAP_CHAIN_CONFIG_TRANSPARENT) ? - mEGLTransparentConfig : mEGLConfig, + + EGLConfig config = EGL_NO_CONFIG_KHR; + if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) { + config = findSwapChainConfig(flags); + } else { + config = mEGLConfig; + } + + if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) { + return nullptr; + } + + EGLSurface sur = eglCreateWindowSurface(mEGLDisplay, config, (EGLNativeWindowType)nativeWindow, nullptr); if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) { logEglError("eglCreateWindowSurface"); return nullptr; } - if (!eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED)) { - logEglError("eglSurfaceAttrib(..., EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED)"); - // this is not fatal - } + + // this is not fatal + eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); + return (SwapChain*)sur; } Platform::SwapChain* PlatformEGL::createSwapChain( uint32_t width, uint32_t height, uint64_t flags) noexcept { - EGLint attribs[] = { + EGLConfig config = EGL_NO_CONFIG_KHR; + if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) { + config = findSwapChainConfig(flags); + } else { + config = mEGLConfig; + } + + if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) { + return nullptr; + } + + const EGLint attribs[] = { EGL_WIDTH, EGLint(width), EGL_HEIGHT, EGLint(height), EGL_NONE }; - EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay, - (flags & SWAP_CHAIN_CONFIG_TRANSPARENT) ? - mEGLTransparentConfig : mEGLConfig, attribs); + EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs); if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) { logEglError("eglCreatePbufferSurface"); @@ -366,7 +403,7 @@ FenceStatus PlatformEGL::waitFence( OpenGLPlatform::ExternalTexture* PlatformEGL::createExternalImageTexture() noexcept { ExternalTexture* outTexture = new ExternalTexture{}; glGenTextures(1, &outTexture->id); - if (UTILS_LIKELY(ext.OES_EGL_image_external_essl3)) { + if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) { outTexture->target = GL_TEXTURE_EXTERNAL_OES; } else { // if texture external is not supported, revert to texture 2d @@ -382,7 +419,7 @@ void PlatformEGL::destroyExternalImage(ExternalTexture* texture) noexcept { bool PlatformEGL::setExternalImage(void* externalImage, UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept { - if (UTILS_LIKELY(ext.OES_EGL_image_external_essl3)) { + if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) { assert_invariant(texture->target == GL_TEXTURE_EXTERNAL_OES); // the texture is guaranteed to be bound here. #ifdef GL_OES_EGL_image @@ -401,7 +438,7 @@ void PlatformEGL::initializeGlExtensions() noexcept { const char * const extension = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); glExtensions.insert(extension); } - ext.OES_EGL_image_external_essl3 = glExtensions.has("GL_OES_EGL_image_external_essl3"); + ext.gl.OES_EGL_image_external_essl3 = glExtensions.has("GL_OES_EGL_image_external_essl3"); } } // namespace filament::backend diff --git a/filament/include/filament/SwapChain.h b/filament/include/filament/SwapChain.h index 2484760750..94d0b7dd67 100644 --- a/filament/include/filament/SwapChain.h +++ b/filament/include/filament/SwapChain.h @@ -148,7 +148,11 @@ public: using FrameScheduledCallback = backend::FrameScheduledCallback; using FrameCompletedCallback = backend::FrameCompletedCallback; + /** + * Requests a SwapChain with an alpha channel. + */ static const uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT; + /** * This flag indicates that the swap chain may be used as a source surface * for reading back render results. This config must be set when creating