mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Feature] (EGL+GL): an opt-in OpenGL ES identity for ES contexts with BGRA textures, and contexts take the dispatcher's API, so Chrome's GPU process runs on ANGLE.
This commit is contained in:
@@ -48,6 +48,8 @@
|
||||
|
||||
namespace MobileGL::MG_Impl::EGLImpl {
|
||||
namespace {
|
||||
// SetDispatcherCurrentApiHook.
|
||||
std::atomic<EGLenum (*)()> g_dispatcherCurrentApi{nullptr};
|
||||
using EGLStateContext = MG_State::EGLState::EGLContext;
|
||||
|
||||
EGLStateContext* GetState() {
|
||||
@@ -674,6 +676,12 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
if (!state) {
|
||||
return EGL_NO_CONTEXT;
|
||||
}
|
||||
// The API the context is for, as the dispatcher reports it (SetDispatcherCurrentApiHook):
|
||||
// an ES context made without an eglBindAPI - or with one of the default - is an ES context.
|
||||
if (const auto currentApi = g_dispatcherCurrentApi.load()) {
|
||||
const EGLenum api = currentApi();
|
||||
if (api == EGL_OPENGL_API || api == EGL_OPENGL_ES_API) state->SetBoundAPI(api);
|
||||
}
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// AFTER A DEVICE LOSS THIS IS WHERE AN APPLICATION REBUILDS, so this is where a fresh
|
||||
// session comes up (RecoverLostSession) - before the context's GL state is built, because
|
||||
@@ -1416,6 +1424,8 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
return state->GetSyncAttrib(dpy, sync, attribute, value) ? EGL_TRUE : EGL_FALSE;
|
||||
}
|
||||
|
||||
void SetDispatcherCurrentApiHook(EGLenum (*hook)()) { g_dispatcherCurrentApi.store(hook); }
|
||||
|
||||
Bool SharedImagesAvailable() {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// A split client's backend is the remote one, which forwards the shared-image verbs to
|
||||
|
||||
@@ -66,6 +66,11 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
// a split client's does, a monolith's does not. Decides the dma-buf EGL extensions, the
|
||||
// Wayland linux-dmabuf presentation and GL_OES_EGL_image.
|
||||
Bool SharedImagesAvailable();
|
||||
// Under a dispatching libEGL the dispatcher, not this library, owns eglBindAPI's state: it does
|
||||
// not forward a bind of the API it already reports (EGL_OPENGL_ES_API, EGL's default), so a
|
||||
// context would otherwise be created for whatever this library assumed. The glvnd vendor
|
||||
// installs the dispatcher's own answer here; eglCreateContext asks it.
|
||||
void SetDispatcherCurrentApiHook(EGLenum (*hook)());
|
||||
// The shared image an EGLImage names (EGL_LINUX_DMA_BUF_EXT), for glEGLImageTargetTexture2DOES.
|
||||
Bool LookupSharedImage(EGLImage image, Uint64* id, EGLint* width, EGLint* height);
|
||||
// glEGLImageTargetTexture2DOES bound a shared image to a texture: from now on glFlush/glFinish
|
||||
|
||||
@@ -352,6 +352,12 @@ extern "C" MOBILEGL_EGL_API EGLBoolean __egl_Main(uint32_t version, const __EGLa
|
||||
return EGL_FALSE;
|
||||
}
|
||||
g_eglExports = exports;
|
||||
if (exports->getCurrentApi != nullptr) {
|
||||
MobileGL::MG_Impl::EGLImpl::SetDispatcherCurrentApiHook([]() -> EGLenum {
|
||||
return g_eglExports != nullptr && g_eglExports->getCurrentApi != nullptr ? g_eglExports->getCurrentApi()
|
||||
: EGL_NONE;
|
||||
});
|
||||
}
|
||||
|
||||
imports->getPlatformDisplay = VendorGetPlatformDisplay;
|
||||
imports->getSupportsAPI = VendorGetSupportsAPI;
|
||||
|
||||
@@ -603,12 +603,26 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
// Robust buffer ACCESS is a separate promise (GL_CONTEXT_ROBUST_ACCESS) and is not
|
||||
// implied by any of them.
|
||||
// One list for glGetString and glGetStringi, so the two forms always agree.
|
||||
Bool EsIdentityActive();
|
||||
|
||||
const Vector<const char*>& ClientSideExtensions() {
|
||||
static const Vector<const char*> withImages = {"GL_OES_EGL_image", "GL_KHR_robustness",
|
||||
"GL_ARB_robustness", "GL_EXT_robustness"};
|
||||
static const Vector<const char*> withoutImages = {"GL_KHR_robustness", "GL_ARB_robustness",
|
||||
"GL_EXT_robustness"};
|
||||
return MG_Impl::EGLImpl::SharedImagesAvailable() ? withImages : withoutImages;
|
||||
// Under the ES identity (EsIdentityActive) the ES names of what desktop GL has in core:
|
||||
// BGRA textures, render targets and read-backs. GL_BGRA_EXT / GL_BGRA8_EXT as internal
|
||||
// formats are taken as RGBA storage (TextureEnumConverter), the byte order being the
|
||||
// pixel transfer format's, as on desktop GL.
|
||||
static const Vector<const char*> esWithImages = {
|
||||
"GL_OES_EGL_image", "GL_KHR_robustness", "GL_ARB_robustness",
|
||||
"GL_EXT_robustness", "GL_EXT_texture_format_BGRA8888", "GL_EXT_read_format_bgra"};
|
||||
static const Vector<const char*> esWithoutImages = {"GL_KHR_robustness", "GL_ARB_robustness",
|
||||
"GL_EXT_robustness", "GL_EXT_texture_format_BGRA8888",
|
||||
"GL_EXT_read_format_bgra"};
|
||||
const Bool images = MG_Impl::EGLImpl::SharedImagesAvailable();
|
||||
if (EsIdentityActive()) return images ? esWithImages : esWithoutImages;
|
||||
return images ? withImages : withoutImages;
|
||||
}
|
||||
|
||||
// Robust buffer access as GL 4.5 / KHR_robust_buffer_access_behavior define it: the backend
|
||||
@@ -624,6 +638,22 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
extensions.end();
|
||||
}
|
||||
|
||||
// MOBILEGL_ES_CONTEXT_IDENTITY=1: a context created for the OpenGL ES API names itself OpenGL
|
||||
// ES 3.2 (GL_VERSION, GL_SHADING_LANGUAGE_VERSION, GL_MAJOR/MINOR_VERSION) instead of the
|
||||
// desktop version every context reports by default. Opt-in, for an application that
|
||||
// decides what it may use from that identity: ANGLE's GL-on-EGL backend offers
|
||||
// GL_OES_EGL_image - which Chrome's GPU process presents through - only over a native
|
||||
// driver that calls itself OpenGL ES, and reads the desktop identity's extension names
|
||||
// otherwise.
|
||||
Bool EsIdentityActive() {
|
||||
static const Bool requested = [] {
|
||||
const char* value = std::getenv("MOBILEGL_ES_CONTEXT_IDENTITY");
|
||||
return value != nullptr && value[0] == '1';
|
||||
}();
|
||||
return requested && MG_State::pEGLContext &&
|
||||
MG_State::pEGLContext->GetCurrentContextClientAPI() == EGL_OPENGL_ES_API;
|
||||
}
|
||||
|
||||
Bool CurrentContextHasRobustAccess() {
|
||||
return MG_State::pEGLContext && MG_State::pEGLContext->IsCurrentContextRobustAccessRequested() &&
|
||||
BackendProvidesRobustBufferAccess();
|
||||
@@ -683,6 +713,12 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
if (const auto at = backendName.find("OpenGL ES"); at != String::npos) {
|
||||
backendName.replace(at, sizeof("OpenGL ES") - 1, "GLES");
|
||||
}
|
||||
if (EsIdentityActive()) {
|
||||
versionStr = std::format("OpenGL ES 3.2 {} {}, {} Backend, GIT@" GIT_COMMIT_HASH_SHORT, MG_Config::ProjectName,
|
||||
MG_Config::CoreVersion.toFormattedString(MG_Config::DefaultVersionStringFormatAttrib),
|
||||
backendName);
|
||||
return (const GLubyte*)versionStr.c_str();
|
||||
}
|
||||
versionStr =
|
||||
std::format("{} {} {}, {} Backend, GIT@" GIT_COMMIT_HASH_SHORT,
|
||||
rendererInfo.RendererGLInfo.TargetGLVersion.toString(), MG_Config::ProjectName,
|
||||
@@ -699,6 +735,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return (const GLubyte*)rendererString.c_str();
|
||||
}
|
||||
case GL_SHADING_LANGUAGE_VERSION:
|
||||
if (EsIdentityActive()) return (const GLubyte*)"OpenGL ES GLSL ES 3.20";
|
||||
shadingLanguageVersion =
|
||||
std::format("{} {}", rendererInfo.RendererGLInfo.TargetGLSLVersion.toString({true, false}),
|
||||
MG_Config::ProjectName);
|
||||
@@ -2583,7 +2620,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
GetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, ¶ms[2]);
|
||||
break;
|
||||
case GL_MAJOR_VERSION:
|
||||
*params = rendererInfo.RendererGLInfo.TargetGLVersion.Major;
|
||||
*params = EsIdentityActive() ? 3 : rendererInfo.RendererGLInfo.TargetGLVersion.Major;
|
||||
break;
|
||||
case GL_MAX_3D_TEXTURE_SIZE:
|
||||
*params = dynamicParameters.Max3DTextureSize;
|
||||
@@ -2870,7 +2907,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
*params = static_cast<GLint>(RenderStateParameters::MAX_VIEWPORTS);
|
||||
break;
|
||||
case GL_MINOR_VERSION:
|
||||
*params = rendererInfo.RendererGLInfo.TargetGLVersion.Minor;
|
||||
*params = EsIdentityActive() ? 2 : rendererInfo.RendererGLInfo.TargetGLVersion.Minor;
|
||||
break;
|
||||
case GL_NUM_EXTENSIONS:
|
||||
*params = static_cast<Int>(rendererInfo.RendererGLInfo.Extensions.size() + ClientSideExtensions().size());
|
||||
|
||||
@@ -956,6 +956,16 @@ namespace MobileGL {
|
||||
return ctx ? ctx->ResetNotificationStrategy : EGL_NO_RESET_NOTIFICATION;
|
||||
}
|
||||
|
||||
EGLenum EGLContext::GetCurrentContextClientAPI() const {
|
||||
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
auto currentIt = m_threadCurrents.find(CurrentThreadKey());
|
||||
if (currentIt == m_threadCurrents.end()) {
|
||||
return EGL_OPENGL_API;
|
||||
}
|
||||
const auto* ctx = TryGetContext(currentIt->second.Context);
|
||||
return ctx ? ctx->ClientAPI : EGL_OPENGL_API;
|
||||
}
|
||||
|
||||
Bool EGLContext::IsCurrentContextRobustAccessRequested() const {
|
||||
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
auto currentIt = m_threadCurrents.find(CurrentThreadKey());
|
||||
|
||||
@@ -74,6 +74,9 @@ namespace MobileGL {
|
||||
// robust buffer access.
|
||||
EGLint GetCurrentContextResetNotificationStrategy() const;
|
||||
Bool IsCurrentContextRobustAccessRequested() const;
|
||||
// The client API the calling thread's current context was created for
|
||||
// (EGL_OPENGL_API with no context current).
|
||||
EGLenum GetCurrentContextClientAPI() const;
|
||||
|
||||
// P14 S1. The wire identity of a context, and of the context the CALLING THREAD
|
||||
// has current (0 when it has none - the release edge's value, and the value a
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <mutex>
|
||||
|
||||
extern "C" {
|
||||
void glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
const GLubyte* glGetString(GLenum name);
|
||||
const GLubyte* glGetStringi(GLenum name, GLuint index);
|
||||
void glGetIntegerv(GLenum pname, GLint* data);
|
||||
@@ -922,6 +923,102 @@ namespace {
|
||||
}
|
||||
};
|
||||
|
||||
// THE ES IDENTITY (MOBILEGL_ES_CONTEXT_IDENTITY=1), what Chrome's GPU process runs with: ANGLE
|
||||
// offers GL_OES_EGL_image - the GPU process's dma-buf images - only over a native driver that
|
||||
// calls itself OpenGL ES, and then creates BGRA textures with the ES spelling. And the API a
|
||||
// dispatching libEGL reports wins over the one this library was told (glvnd never forwards a
|
||||
// bind of the API it already reports, EGL's default ES).
|
||||
struct EsIdentityReport {
|
||||
Int32 initialized = 0;
|
||||
Int32 dispatcherApiWins = -1;
|
||||
char esVersion[96] = {};
|
||||
char esGlsl[64] = {};
|
||||
Int32 esMajor = 0;
|
||||
Int32 esMinor = 0;
|
||||
Int32 bgraAdvertised = -1;
|
||||
Int32 bgraIndexed = -1;
|
||||
Uint32 bgraImageError = 0xFFFFFFFFu;
|
||||
Uint32 bgraStorageError = 0xFFFFFFFFu;
|
||||
Uint32 bgraFboStatus = 0;
|
||||
Uint32 bgraPixel = 0;
|
||||
char desktopVersion[96] = {};
|
||||
char note[256] = {};
|
||||
};
|
||||
|
||||
void Note(EsIdentityReport& r, const char* text) {
|
||||
if (r.note[0] == '\0') std::snprintf(r.note, sizeof(r.note), "%s", text);
|
||||
}
|
||||
|
||||
EGLenum g_dispatcherApi = EGL_OPENGL_ES_API;
|
||||
|
||||
void RunEsIdentityClient(EsIdentityReport& r) {
|
||||
::setenv("MOBILEGL_ES_CONTEXT_IDENTITY", "1", 1);
|
||||
Egl egl;
|
||||
RecoveryReport bring{};
|
||||
if (!egl.Bring(bring)) return Note(r, bring.note);
|
||||
// The dispatcher says ES although this library was told GL last (Bring binds the GL API).
|
||||
EGL::SetDispatcherCurrentApiHook([]() -> EGLenum { return g_dispatcherApi; });
|
||||
const EGLContext es = EGL::CreateContext(egl.dpy, egl.config, EGL_NO_CONTEXT, kAngleNativeAttribs);
|
||||
g_dispatcherApi = EGL_OPENGL_API;
|
||||
const EGLContext desktop = EGL::CreateContext(egl.dpy, egl.config, EGL_NO_CONTEXT, kRobustAttribs);
|
||||
EGL::SetDispatcherCurrentApiHook(nullptr);
|
||||
if (es == EGL_NO_CONTEXT || desktop == EGL_NO_CONTEXT) return Note(r, "the contexts were not created");
|
||||
EGLint type = 0;
|
||||
(void)EGL::QueryContext(egl.dpy, es, EGL_CONTEXT_CLIENT_TYPE, &type);
|
||||
r.dispatcherApiWins = type == EGL_OPENGL_ES_API ? 1 : 0;
|
||||
|
||||
if (EGL::MakeCurrent(egl.dpy, egl.surface, egl.surface, es) != EGL_TRUE)
|
||||
return Note(r, "the ES context did not go current");
|
||||
r.initialized = 1;
|
||||
std::snprintf(r.esVersion, sizeof(r.esVersion), "%s", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
|
||||
std::snprintf(r.esGlsl, sizeof(r.esGlsl), "%s",
|
||||
reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &r.esMajor);
|
||||
glGetIntegerv(GL_MINOR_VERSION, &r.esMinor);
|
||||
r.bgraAdvertised = HasToken(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)),
|
||||
"GL_EXT_texture_format_BGRA8888")
|
||||
? 1
|
||||
: 0;
|
||||
GLint count = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &count);
|
||||
r.bgraIndexed = 0;
|
||||
for (GLint i = 0; i < count; ++i) {
|
||||
const auto* name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, static_cast<GLuint>(i)));
|
||||
if (name != nullptr && std::string(name) == "GL_EXT_texture_format_BGRA8888") r.bgraIndexed = 1;
|
||||
}
|
||||
while (glGetError() != GL_NO_ERROR) {
|
||||
}
|
||||
// BGRA as EXT_texture_format_BGRA8888 spells it: GL_BGRA_EXT as the internal format, and
|
||||
// GL_BGRA8_EXT through glTexStorage2D; a BGRA texel reads back in RGBA order.
|
||||
const Uint8 bgra[4] = {0x10, 0x20, 0x30, 0xFF}; // B, G, R, A
|
||||
GLuint textures[2] = {};
|
||||
glGenTextures(2, textures);
|
||||
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, bgra);
|
||||
r.bgraImageError = glGetError();
|
||||
glBindTexture(GL_TEXTURE_2D, textures[1]);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, 0x93A1 /* GL_BGRA8_EXT */, 1, 1);
|
||||
r.bgraStorageError = glGetError();
|
||||
GLuint framebuffer = 0;
|
||||
glGenFramebuffers(1, &framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0], 0);
|
||||
r.bgraFboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
r.bgraPixel = ReadPixel();
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
(void)EGL::MakeCurrent(egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (EGL::MakeCurrent(egl.dpy, egl.surface, egl.surface, desktop) == EGL_TRUE) {
|
||||
std::snprintf(r.desktopVersion, sizeof(r.desktopVersion), "%s",
|
||||
reinterpret_cast<const char*>(glGetString(GL_VERSION)));
|
||||
(void)EGL::MakeCurrent(egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
(void)EGL::DestroyContext(egl.dpy, es);
|
||||
(void)EGL::DestroyContext(egl.dpy, desktop);
|
||||
(void)EGL::DestroySurface(egl.dpy, egl.surface);
|
||||
(void)EGL::Terminate(egl.dpy);
|
||||
}
|
||||
|
||||
RecoveryReport RunClient(const ServerProcess& server, Route route, const std::string& clientLogBase,
|
||||
EGLenum api = EGL_OPENGL_API, const EGLint* attribs = kRobustAttribs) {
|
||||
return RunInClient<RecoveryReport>(server, clientLogBase, [route, api, attribs](RecoveryReport& r) {
|
||||
@@ -1219,3 +1316,45 @@ TEST_F(ClientRecovery, ThePidTargetedKnobLosesOnlyThatClientAndOnlyOnce) {
|
||||
EXPECT_EQ(Count(log, "reaped exit=0"), 2u) << log;
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
// THE ES IDENTITY (RunEsIdentityClient): an ES context names itself OpenGL ES 3.2 and offers BGRA
|
||||
// the ES way, a desktop context of the same process does not change, and the dispatcher's API wins.
|
||||
TEST_F(ClientRecovery, AnEsContextUnderTheEsIdentityIsOpenGlEs32WithBgraTextures) {
|
||||
ServerProcess server;
|
||||
ASSERT_TRUE(LaunchUnixInProcessServer(&server, "esid")) << server.Log();
|
||||
ClientLog client("esid");
|
||||
const EsIdentityReport r = RunInClient<EsIdentityReport>(server, client.base, &RunEsIdentityClient);
|
||||
const std::string diagnostics = "server:\n" + server.Log() + "\nclient:\n" + client.Text();
|
||||
EXPECT_EQ(r.dispatcherApiWins, 1) << "the context was not created for the API the dispatcher reports";
|
||||
ASSERT_EQ(r.initialized, 1) << r.note << "\n" << diagnostics;
|
||||
EXPECT_EQ(std::string(r.esVersion).rfind("OpenGL ES 3.2 ", 0), 0u) << r.esVersion;
|
||||
EXPECT_STREQ(r.esGlsl, "OpenGL ES GLSL ES 3.20");
|
||||
EXPECT_EQ(r.esMajor, 3);
|
||||
EXPECT_EQ(r.esMinor, 2);
|
||||
EXPECT_EQ(r.bgraAdvertised, 1);
|
||||
EXPECT_EQ(r.bgraIndexed, 1);
|
||||
EXPECT_EQ(r.bgraImageError, static_cast<Uint32>(GL_NO_ERROR)) << std::hex << r.bgraImageError;
|
||||
EXPECT_EQ(r.bgraStorageError, static_cast<Uint32>(GL_NO_ERROR)) << std::hex << r.bgraStorageError;
|
||||
EXPECT_EQ(r.bgraFboStatus, static_cast<Uint32>(GL_FRAMEBUFFER_COMPLETE)) << std::hex << r.bgraFboStatus;
|
||||
EXPECT_EQ(r.bgraPixel, 0xFF102030u) << "a BGRA texel did not read back in RGBA order: " << std::hex << r.bgraPixel;
|
||||
EXPECT_EQ(std::string(r.desktopVersion).find("OpenGL ES"), std::string::npos)
|
||||
<< "a desktop context took the ES identity: " << r.desktopVersion;
|
||||
EXPECT_NE(std::string(r.desktopVersion), "") << diagnostics;
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
// Chrome's GPU process's own shape: ANGLE's native ES context, under the ES identity, told of the
|
||||
// loss and recovering like any other.
|
||||
TEST_F(ClientRecovery, AnglesNativeContextUnderTheEsIdentityRecovers) {
|
||||
ServerProcess server;
|
||||
ASSERT_TRUE(Launch(&server, "angle-es")) << server.Log();
|
||||
ClientLog client("angle-es");
|
||||
const RecoveryReport r = RunInClient<RecoveryReport>(server, client.base, [](RecoveryReport& report) {
|
||||
::setenv("MOBILEGL_ES_CONTEXT_IDENTITY", "1", 1);
|
||||
RunRecoveringClient(Route::NewContext, report, EGL_OPENGL_ES_API, kAngleNativeAttribs);
|
||||
});
|
||||
const std::string diagnostics = "server:\n" + server.Log() + "\nclient:\n" + client.Text();
|
||||
ExpectLossReportedAndRecovered(r, diagnostics);
|
||||
ExpectOneLossAndACleanSecondSession(server);
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
@@ -153,6 +153,9 @@ namespace MobileGL {
|
||||
return TextureInternalFormat::RGBA4;
|
||||
case GL_RGB5_A1:
|
||||
return TextureInternalFormat::RGB5A1;
|
||||
// GL_BGRA8_EXT (EXT_texture_storage with EXT_texture_format_BGRA8888): stored as RGBA8;
|
||||
// the BGRA byte order is the pixel transfer format's business, as on desktop GL.
|
||||
case 0x93A1:
|
||||
case GL_RGBA8:
|
||||
return TextureInternalFormat::RGBA8;
|
||||
case GL_RGBA8_SNORM:
|
||||
@@ -330,6 +333,9 @@ namespace MobileGL {
|
||||
return TextureInternalFormat::RG;
|
||||
case GL_RGB:
|
||||
return TextureInternalFormat::RGB;
|
||||
// GL_BGRA_EXT as an unsized internal format (EXT_texture_format_BGRA8888, an ES
|
||||
// application's spelling): the same storage as GL_RGBA.
|
||||
case GL_BGRA:
|
||||
case GL_RGBA:
|
||||
return TextureInternalFormat::RGBA;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user