cleanup PlatformEGL config selection code

We now structure the code to assume we have EGL_KHR_no_config_context,
which is the common case. This allows use to decouple context creation
from swapchain creation.

We still handle the case where the extension is not present by always
selecting a transparent config.
This commit is contained in:
Mathias Agopian
2023-01-23 16:59:57 -08:00
committed by Mathias Agopian
parent f22ff6a340
commit 84e999fe57
4 changed files with 132 additions and 61 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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