mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-13 06:38:31 +09:00
[Fix, Test] (MG_Util, MG_Backend/DirectGLES, MG_Test): resolve buffer textures through the entry point the tier ships, not the ES 3.2 core name; bound the OES retarget to an exact extension name
This commit is contained in:
@@ -7233,13 +7233,66 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glGetQueryObjectui64vEXT;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// The entry point the resolved tier's support ships, or null when there is none.
|
||||
MG_External::GLES::glTexBuffer_PTR ResolveTexBufferEntryPoint() {
|
||||
using Tier = MG_External::GLESCapabilities::TextureBufferTier;
|
||||
switch (g_GLESCapabilities.TextureBufferSupport) {
|
||||
case Tier::ExtensionEXT:
|
||||
return g_GLESFuncs.glTexBufferEXT ? g_GLESFuncs.glTexBufferEXT : g_GLESFuncs.glTexBuffer;
|
||||
case Tier::ExtensionOES:
|
||||
return g_GLESFuncs.glTexBufferOES ? g_GLESFuncs.glTexBufferOES : g_GLESFuncs.glTexBuffer;
|
||||
case Tier::CoreEs32:
|
||||
return g_GLESFuncs.glTexBuffer;
|
||||
case Tier::None:
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
MG_External::GLES::glTexBufferRange_PTR ResolveTexBufferRangeEntryPoint() {
|
||||
using Tier = MG_External::GLESCapabilities::TextureBufferTier;
|
||||
switch (g_GLESCapabilities.TextureBufferSupport) {
|
||||
case Tier::ExtensionEXT:
|
||||
return g_GLESFuncs.glTexBufferRangeEXT ? g_GLESFuncs.glTexBufferRangeEXT
|
||||
: g_GLESFuncs.glTexBufferRange;
|
||||
case Tier::ExtensionOES:
|
||||
return g_GLESFuncs.glTexBufferRangeOES ? g_GLESFuncs.glTexBufferRangeOES
|
||||
: g_GLESFuncs.glTexBufferRange;
|
||||
case Tier::CoreEs32:
|
||||
return g_GLESFuncs.glTexBufferRange;
|
||||
case Tier::None:
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Bool AreBufferTexturesSupported() {
|
||||
// The tier already folds in the resolved-pointer requirement (see FillInGLESCapabilities),
|
||||
// but the pointer is re-checked here because the tier is only meaningful once the
|
||||
// capabilities have been filled in, and callers may run before that.
|
||||
// Both halves matter. The tier is what the driver ADVERTISES, and it is only meaningful
|
||||
// once the capabilities have been filled in; the resolved pointer is what MobileGL can
|
||||
// actually call, through the spelling that tier's support ships. Gating on the
|
||||
// unsuffixed name alone would call an entry point an EXT/OES driver never exported.
|
||||
return g_GLESCapabilities.TextureBufferSupport !=
|
||||
MG_External::GLESCapabilities::TextureBufferTier::None &&
|
||||
g_GLESFuncs.glTexBuffer != nullptr;
|
||||
ResolveTexBufferEntryPoint() != nullptr;
|
||||
}
|
||||
|
||||
void CallTexBuffer(GLenum target, GLenum internalFormat, GLuint buffer) {
|
||||
MG_External::GLES::glTexBuffer_PTR entryPoint = ResolveTexBufferEntryPoint();
|
||||
if (entryPoint == nullptr) {
|
||||
return;
|
||||
}
|
||||
entryPoint(target, internalFormat, buffer);
|
||||
}
|
||||
|
||||
Bool CallTexBufferRange(GLenum target, GLenum internalFormat, GLuint buffer, GLintptr offset, GLsizeiptr size) {
|
||||
MG_External::GLES::glTexBufferRange_PTR entryPoint = ResolveTexBufferRangeEntryPoint();
|
||||
if (entryPoint == nullptr) {
|
||||
return false;
|
||||
}
|
||||
entryPoint(target, internalFormat, buffer, offset, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* GetBufferTextureTierName() {
|
||||
|
||||
@@ -127,6 +127,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// Human-readable name of the buffer-texture tier for diagnostics and the driver POST:
|
||||
// "core (ES 3.2)", "GL_EXT_texture_buffer", "GL_OES_texture_buffer" or "unsupported".
|
||||
const char* GetBufferTextureTierName();
|
||||
// glTexBuffer / glTexBufferRange through whichever spelling this driver's buffer-texture
|
||||
// support actually ships: the unsuffixed names are ES 3.2 core, while an EXT/OES driver
|
||||
// exports glTexBuffer{,Range}EXT / OES. Callers must have checked
|
||||
// AreBufferTexturesSupported() first. CallTexBufferRange reports whether it could honour
|
||||
// the range - no tier is required to expose the range form, and the whole-buffer form is
|
||||
// the documented fallback.
|
||||
void CallTexBuffer(GLenum target, GLenum internalFormat, GLuint buffer);
|
||||
Bool CallTexBufferRange(GLenum target, GLenum internalFormat, GLuint buffer, GLintptr offset, GLsizeiptr size);
|
||||
// GL timer-query objects, backed by GL_EXT_disjoint_timer_query. The
|
||||
// creators return null (the frontend then falls back to an immediately
|
||||
// available zero result) when the calling thread does not own the ES
|
||||
|
||||
@@ -2812,17 +2812,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// is absent).
|
||||
const SizeT rangeOffset = textureBufferObject->GetBufferRangeOffset();
|
||||
const SizeT rangeSize = textureBufferObject->GetBufferRangeSizeInBytes();
|
||||
// Through CallTexBuffer/CallTexBufferRange rather than g_GLESFuncs directly:
|
||||
// the unsuffixed entry points are the ES 3.2 core spelling, and a driver
|
||||
// whose buffer textures come from EXT/OES_texture_buffer exports the
|
||||
// suffixed ones instead. The dispatchers pick whichever this tier ships.
|
||||
if (rangeOffset == 0 && rangeSize == buffer->GetSize()) {
|
||||
g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
|
||||
} else if (g_GLESFuncs.glTexBufferRange != nullptr) {
|
||||
g_GLESFuncs.glTexBufferRange(GL_TEXTURE_BUFFER, glInternalFormat, backendId,
|
||||
static_cast<GLintptr>(rangeOffset),
|
||||
static_cast<GLsizeiptr>(rangeSize));
|
||||
} else {
|
||||
MGLOG_E("Texture buffer %u names a sub-range but the driver has no "
|
||||
CallTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
|
||||
} else if (!CallTexBufferRange(GL_TEXTURE_BUFFER, glInternalFormat, backendId,
|
||||
static_cast<GLintptr>(rangeOffset),
|
||||
static_cast<GLsizeiptr>(rangeSize))) {
|
||||
MGLOG_I("Texture buffer %u names a sub-range but the driver has no "
|
||||
"glTexBufferRange; binding the whole buffer instead",
|
||||
stateTextureObject->GetExternalIndex());
|
||||
g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
|
||||
CallTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
|
||||
}
|
||||
DebugImpl::ErrorLopper::Loop(
|
||||
[file = __FILE__, line = __LINE__, func = __func__, glInternalFormat, backendId](GLenum err) {
|
||||
@@ -4442,11 +4444,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
source = result;
|
||||
|
||||
// First in the chain because it is the only header-level rewrite: it edits
|
||||
// #extension directives and never the body, so it is independent of every pass
|
||||
// below and running it early keeps the directive block correct for
|
||||
// ForceSupporterOutput, which scans for the last #extension line to decide where
|
||||
// its precision statements go.
|
||||
// Position in the chain is arbitrary: this is the only header-level rewrite, it
|
||||
// edits #extension directives and never the body, and the replacement is the
|
||||
// same length and stays an #extension line - so it commutes with every pass
|
||||
// below, including ForceSupporterOutput's scan for the last directive. First,
|
||||
// because a header concern reads better before the body ones.
|
||||
source = RetargetTextureBufferExtension(std::move(source),
|
||||
g_GLESCapabilities.TextureBufferSupport);
|
||||
|
||||
|
||||
@@ -448,6 +448,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// advertise is a hard compile error - so on an OES-only driver the emitted shader
|
||||
// fails to compile for the sake of one token.
|
||||
//
|
||||
// Line comments are excluded by the directive check below; a `#extension` line inside
|
||||
// a /* */ block is not, and would be rewritten. That is harmless (it stays a comment)
|
||||
// and is not worth a preprocessor-aware scan here.
|
||||
//
|
||||
// Deliberately a directive rewrite and nothing more. The alternative - teaching the
|
||||
// SPIR-V to stop asking for the extension - is not available: the requirement is
|
||||
// synthesized by SPIRV-Cross from the image type itself, not carried in the module,
|
||||
@@ -464,9 +468,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static_assert(sizeof("GL_OES_texture_buffer") - 1 == kExtNameLength,
|
||||
"the two spellings must be the same length for the in-place replace");
|
||||
|
||||
// Only rewrite the name where it is the subject of an #extension directive. The same
|
||||
// token can legitimately appear in a comment SPIRV-Cross carried through, and a
|
||||
// shader that merely mentions the string must not be edited.
|
||||
// Only rewrite the name where it is the whole subject of an #extension directive.
|
||||
// Two separate guards, both load-bearing:
|
||||
// * the directive check, so a line-comment mentioning the name is left alone;
|
||||
// * the identifier-boundary check, because GL_EXT_texture_buffer is a PREFIX of
|
||||
// GL_EXT_texture_buffer_object - a different, real extension that SPIRV-Cross
|
||||
// emits from the same `case DimBuffer:` on its legacy-desktop branch. Without
|
||||
// the boundary this pass would silently rewrite a request for that extension
|
||||
// into a request for a GL_OES_texture_buffer_object that does not exist.
|
||||
const auto isIdentifierChar = [](char c) {
|
||||
return std::isalnum(static_cast<unsigned char>(c)) != 0 || c == '_';
|
||||
};
|
||||
SizeT searchFrom = 0;
|
||||
while (true) {
|
||||
const SizeT hit = glslCode.find(kExtName, searchFrom);
|
||||
@@ -475,6 +487,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
searchFrom = hit + kExtNameLength;
|
||||
|
||||
// Identifier boundary on both sides, so the name is not a fragment of a longer one.
|
||||
if (hit > 0 && isIdentifierChar(glslCode[hit - 1])) {
|
||||
continue;
|
||||
}
|
||||
if (hit + kExtNameLength < glslCode.size() && isIdentifierChar(glslCode[hit + kExtNameLength])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Walk back to the start of the line and require that it is an #extension
|
||||
// directive, allowing whitespace between '#' and the keyword.
|
||||
SizeT lineStart = glslCode.rfind('\n', hit);
|
||||
|
||||
Reference in New Issue
Block a user