mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 13:48:30 +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:
@@ -511,6 +511,13 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
INIT_GLES_FUNC(glGetSamplerParameterIuiv)
|
||||
INIT_GLES_FUNC(glTexBuffer)
|
||||
INIT_GLES_FUNC(glTexBufferRange)
|
||||
// Optional: absent on an ES 3.2 core driver, and absent on ES 3.1 without the
|
||||
// matching extension. The tier resolution below picks whichever spelling the
|
||||
// driver's own support actually comes from.
|
||||
INIT_GLES_FUNC_OPTIONAL(glTexBufferEXT)
|
||||
INIT_GLES_FUNC_OPTIONAL(glTexBufferOES)
|
||||
INIT_GLES_FUNC_OPTIONAL(glTexBufferRangeEXT)
|
||||
INIT_GLES_FUNC_OPTIONAL(glTexBufferRangeOES)
|
||||
INIT_GLES_FUNC(glTexStorage3DMultisample)
|
||||
INIT_GLES_FUNC(glMapBufferRange)
|
||||
INIT_GLES_FUNC(glBufferStorageEXT)
|
||||
@@ -1120,21 +1127,27 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
// with both needs no directive retargeting. The entry point has to have resolved either
|
||||
// way - the extension string alone is not support (see the multi-draw note above).
|
||||
{
|
||||
const Bool textureBufferIsCore =
|
||||
caps.GLESVersion.Major > 3 || (caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2);
|
||||
using Tier = MG_External::GLESCapabilities::TextureBufferTier;
|
||||
if (glesFuncs.glTexBuffer == nullptr) {
|
||||
caps.TextureBufferSupport = Tier::None;
|
||||
} else if (textureBufferIsCore) {
|
||||
// Each tier needs the entry point that tier's support actually ships. Gating all
|
||||
// three on the unsuffixed name - the ES 3.2 CORE spelling - would make every
|
||||
// EXT/OES driver look unsupported on a strict loader, and would make MobileGL call
|
||||
// a core entry point the driver never exported on a permissive one. The suffixed
|
||||
// name is preferred where the support is an extension, with the core name accepted
|
||||
// as a fallback because drivers that expose both alias them.
|
||||
const Bool hasCoreEntryPoint = glesFuncs.glTexBuffer != nullptr;
|
||||
if (esAtLeast32 && hasCoreEntryPoint) {
|
||||
caps.TextureBufferSupport = Tier::CoreEs32;
|
||||
} else if (hasExtTextureBuffer) {
|
||||
} else if (hasExtTextureBuffer && (glesFuncs.glTexBufferEXT != nullptr || hasCoreEntryPoint)) {
|
||||
caps.TextureBufferSupport = Tier::ExtensionEXT;
|
||||
} else if (hasOesTextureBuffer) {
|
||||
} else if (hasOesTextureBuffer && (glesFuncs.glTexBufferOES != nullptr || hasCoreEntryPoint)) {
|
||||
caps.TextureBufferSupport = Tier::ExtensionOES;
|
||||
} else {
|
||||
caps.TextureBufferSupport = Tier::None;
|
||||
}
|
||||
|
||||
// Assigned unconditionally, like every other capability in this function, so a
|
||||
// second fill on a reused struct cannot keep a stale true.
|
||||
caps.MaxTextureBufferSizeIsDriverReported = false;
|
||||
if (caps.TextureBufferSupport != Tier::None) {
|
||||
// Drain first: an error left by any earlier probe would otherwise read as this
|
||||
// query having failed, and the value would be discarded as a non-answer.
|
||||
@@ -1285,8 +1298,16 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
MGLOG_I(" GL_MAX_COMPUTE_UNIFORM_BLOCKS: %d", caps.MaxComputeUniformBlocks);
|
||||
MGLOG_I(" GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: %d", caps.MaxComputeWorkGroupInvocations);
|
||||
MGLOG_I(" GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: %d", caps.MaxShaderStorageBufferBindings);
|
||||
// Three distinct states, and the suffix must not conflate them: a driver answer, a floor
|
||||
// kept because there are no buffer textures to ask about, and a floor kept because the
|
||||
// driver claimed buffer textures but then refused the query (which is a driver bug worth
|
||||
// seeing spelled out rather than hidden behind the same wording as the honest case).
|
||||
MGLOG_I(" GL_MAX_TEXTURE_BUFFER_SIZE: %d%s", caps.MaxTextureBufferSize,
|
||||
caps.MaxTextureBufferSizeIsDriverReported ? "" : " (MobileGL floor - the driver has no buffer textures to ask)");
|
||||
caps.MaxTextureBufferSizeIsDriverReported
|
||||
? ""
|
||||
: (caps.TextureBufferSupport == MG_External::GLESCapabilities::TextureBufferTier::None
|
||||
? " (MobileGL floor - the driver has no buffer textures to ask)"
|
||||
: " (MobileGL floor - the driver claims buffer textures but rejected the query)"));
|
||||
MGLOG_I(" GL_MAX_UNIFORM_BUFFER_BINDINGS: %d", caps.MaxUniformBufferBindings);
|
||||
MGLOG_I(" GL_MAX_UNIFORM_BLOCK_SIZE: %d", caps.MaxUniformBlockSize);
|
||||
MGLOG_I(" GL_MAX_IMAGE_UNITS: %d", caps.MaxImageUnits);
|
||||
|
||||
@@ -600,6 +600,16 @@ namespace MobileGL {
|
||||
GL_FUNC_TYPEDEF(void, glSamplerParameterIuiv, GLuint sampler, GLenum pname, const GLuint* param)
|
||||
GL_FUNC_TYPEDEF(void, glGetSamplerParameterIiv, GLuint sampler, GLenum pname, GLint* params)
|
||||
GL_FUNC_TYPEDEF(void, glGetSamplerParameterIuiv, GLuint sampler, GLenum pname, GLuint* params)
|
||||
// The unsuffixed names are the ES 3.2 CORE entry points. A driver whose buffer-texture
|
||||
// support comes from GL_EXT_texture_buffer or GL_OES_texture_buffer exports the
|
||||
// suffixed spellings instead, and a strict eglGetProcAddress returns NULL for the core
|
||||
// one there - so resolving only the core name makes both extension tiers look absent.
|
||||
GL_FUNC_TYPEDEF(void, glTexBufferEXT, GLenum target, GLenum internalformat, GLuint buffer)
|
||||
GL_FUNC_TYPEDEF(void, glTexBufferOES, GLenum target, GLenum internalformat, GLuint buffer)
|
||||
GL_FUNC_TYPEDEF(void, glTexBufferRangeEXT, GLenum target, GLenum internalformat, GLuint buffer,
|
||||
GLintptr offset, GLsizeiptr size)
|
||||
GL_FUNC_TYPEDEF(void, glTexBufferRangeOES, GLenum target, GLenum internalformat, GLuint buffer,
|
||||
GLintptr offset, GLsizeiptr size)
|
||||
GL_FUNC_TYPEDEF(void, glTexBuffer, GLenum target, GLenum internalformat, GLuint buffer)
|
||||
GL_FUNC_TYPEDEF(void, glTexBufferRange, GLenum target, GLenum internalformat, GLuint buffer,
|
||||
GLintptr offset, GLsizeiptr size)
|
||||
@@ -1002,6 +1012,10 @@ namespace MobileGL {
|
||||
GL_FUNC_DECL(glGetSamplerParameterIuiv)
|
||||
GL_FUNC_DECL(glTexBuffer)
|
||||
GL_FUNC_DECL(glTexBufferRange)
|
||||
GL_FUNC_DECL(glTexBufferEXT)
|
||||
GL_FUNC_DECL(glTexBufferOES)
|
||||
GL_FUNC_DECL(glTexBufferRangeEXT)
|
||||
GL_FUNC_DECL(glTexBufferRangeOES)
|
||||
GL_FUNC_DECL(glTexStorage3DMultisample)
|
||||
GL_FUNC_DECL(glMapBufferRange)
|
||||
GL_FUNC_DECL(glBufferStorageEXT)
|
||||
|
||||
@@ -422,45 +422,56 @@ namespace MobileGL::MG_Util::SelfTest {
|
||||
"map array texture gets no driver storage at all, so sampling one reads nothing "
|
||||
"and rendering to one does not reach the screen");
|
||||
}
|
||||
// FAIL, not WARN: buffer textures are CORE in OpenGL 3.1 and MobileGL advertises a 4.x
|
||||
// context, so an application may use one without asking - and nothing degrades
|
||||
// gracefully when they are absent. The texture gets no driver storage (glTexBuffer does
|
||||
// not exist), and, worse, every shader declaring a samplerBuffer fails to compile
|
||||
// outright, because SPIRV-Cross emits `#extension GL_EXT_texture_buffer : require` for
|
||||
// it below ESSL 320. The program then never links and every draw using it silently
|
||||
// draws nothing - which is how Minecraft 26.3, whose cloud layer is built entirely from
|
||||
// gl_VertexID plus texelFetch on a GL_R8I buffer texture, loses its clouds.
|
||||
// WARN, not FAIL, and the choice is deliberate. The consequence is severe - buffer
|
||||
// textures are CORE in OpenGL 3.1 and MobileGL advertises a 4.x context, so an
|
||||
// application may use one without asking, and nothing degrades gracefully: the
|
||||
// texture gets no driver storage, and every shader declaring a samplerBuffer fails
|
||||
// to compile outright, because SPIRV-Cross emits `#extension GL_EXT_texture_buffer :
|
||||
// require` for it below ESSL 320, so the program never links and every draw using it
|
||||
// silently draws nothing. That is how Minecraft 26.3, whose cloud layer is built
|
||||
// entirely from gl_VertexID plus texelFetch on a GL_R8I buffer texture, loses its
|
||||
// clouds. But FAIL means "this backend cannot run on this driver", and that is not
|
||||
// true: such a device runs everything that does not touch a buffer texture. It is
|
||||
// also exactly the shape of the "Texture cube map array" row above, which loses its
|
||||
// shaders to the same SPIRV-Cross `: require` mechanism and is a WARN - two adjacent
|
||||
// rows with one consequence must not carry two severities.
|
||||
// The limit is stated on every tier because it is the one number an application can
|
||||
// read, and on the None tier it is knowingly a fiction (see below).
|
||||
{
|
||||
using Tier = MG_External::GLESCapabilities::TextureBufferTier;
|
||||
const Int advertisedLimit = caps.MaxTextureBufferSize;
|
||||
// A supported tier that then refused GL_MAX_TEXTURE_BUFFER_SIZE is a driver bug;
|
||||
// the row must not call MobileGL's floor "the driver's own answer" there.
|
||||
const char* limitProvenance =
|
||||
caps.MaxTextureBufferSizeIsDriverReported
|
||||
? "the driver's own answer"
|
||||
: "MobileGL's floor - this driver claims buffer textures but rejected the query";
|
||||
switch (caps.TextureBufferSupport) {
|
||||
case Tier::CoreEs32:
|
||||
builder.Pass("Buffer textures",
|
||||
format("core in ES 3.2; GL_MAX_TEXTURE_BUFFER_SIZE = {} is the "
|
||||
"driver's own answer, and ESSL 320 needs no #extension "
|
||||
"directive to declare a samplerBuffer",
|
||||
advertisedLimit));
|
||||
format("core in ES 3.2; GL_MAX_TEXTURE_BUFFER_SIZE = {} is {}, and "
|
||||
"ESSL 320 needs no #extension directive to declare a "
|
||||
"samplerBuffer",
|
||||
advertisedLimit, limitProvenance));
|
||||
break;
|
||||
case Tier::ExtensionEXT:
|
||||
builder.Pass("Buffer textures",
|
||||
format("GL_EXT_texture_buffer; GL_MAX_TEXTURE_BUFFER_SIZE = {} is "
|
||||
"the driver's own answer, and the directive SPIRV-Cross "
|
||||
"emits (GL_EXT_texture_buffer) is the one this driver wants",
|
||||
advertisedLimit));
|
||||
format("GL_EXT_texture_buffer; GL_MAX_TEXTURE_BUFFER_SIZE = {} is {}, "
|
||||
"and the directive SPIRV-Cross emits "
|
||||
"(GL_EXT_texture_buffer) is the one this driver wants",
|
||||
advertisedLimit, limitProvenance));
|
||||
break;
|
||||
case Tier::ExtensionOES:
|
||||
builder.Pass("Buffer textures",
|
||||
format("GL_OES_texture_buffer; GL_MAX_TEXTURE_BUFFER_SIZE = {} is "
|
||||
"the driver's own answer. SPIRV-Cross hardcodes the EXT "
|
||||
"spelling, so MobileGL retargets the emitted #extension "
|
||||
"directive to the OES one this driver advertises",
|
||||
advertisedLimit));
|
||||
format("GL_OES_texture_buffer; GL_MAX_TEXTURE_BUFFER_SIZE = {} is {}. "
|
||||
"SPIRV-Cross hardcodes the EXT spelling, so MobileGL "
|
||||
"retargets the emitted #extension directive to the OES one "
|
||||
"this driver advertises",
|
||||
advertisedLimit, limitProvenance));
|
||||
break;
|
||||
case Tier::None:
|
||||
default:
|
||||
builder.Fail("Buffer textures",
|
||||
builder.Warn("Buffer textures",
|
||||
format("not supported (pre-ES 3.2 without GL_EXT/OES_texture_buffer); "
|
||||
"glTexBuffer does not exist, so a buffer texture gets no storage, "
|
||||
"and any shader declaring a samplerBuffer fails to compile and "
|
||||
|
||||
@@ -541,6 +541,12 @@ namespace MobileGL {
|
||||
}
|
||||
|
||||
Bool ShaderCompiler::ModuleDeclaresBufferTextureSampler(const Vector<Uint32>& spirv) {
|
||||
if (spirv.empty()) {
|
||||
// Early out rather than letting BuildModule reject it: an empty module is a
|
||||
// stage that produced no SPIR-V, which is not a capability verdict, and the
|
||||
// parse would push a spurious diagnostic through the message consumer first.
|
||||
return false;
|
||||
}
|
||||
// Callers gate this on the driver LACKING buffer textures, so the module build
|
||||
// here only ever happens on a degraded driver that is about to fail the compile
|
||||
// anyway - it is not on the healthy path.
|
||||
@@ -556,10 +562,14 @@ namespace MobileGL {
|
||||
if (type.opcode() != spv::Op::OpTypeImage) {
|
||||
continue;
|
||||
}
|
||||
// OpTypeImage operands: Sampled Type, Dim, Depth, Arrayed, MS, Sampled, Format.
|
||||
// Dim is operand 1; SpvDimBuffer is what samplerBuffer/isamplerBuffer/
|
||||
// usamplerBuffer all lower to, whatever their sampled type.
|
||||
if (static_cast<spv::Dim>(type.GetSingleWordInOperand(1)) == spv::Dim::Buffer) {
|
||||
// OpTypeImage in-operands: Sampled Type, Dim, Depth, Arrayed, MS, Sampled,
|
||||
// Format. Dim is operand 1; Dim::Buffer is what samplerBuffer/isamplerBuffer/
|
||||
// usamplerBuffer all lower to, whatever their sampled type - and equally what
|
||||
// the imageBuffer family lowers to, which is correct here because SPIRV-Cross
|
||||
// requires the same extension for those. The operand-count guard mirrors
|
||||
// NormalizeRectCoordinatesPass, which reads the same operand.
|
||||
if (type.NumInOperands() >= 2 &&
|
||||
static_cast<spv::Dim>(type.GetSingleWordInOperand(1)) == spv::Dim::Buffer) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,8 +134,11 @@ namespace MobileGL {
|
||||
static Uint64 SpirvValidationFailureCount();
|
||||
static Uint64 NoteSpirvValidationFailure();
|
||||
|
||||
// True when the module declares any buffer-texture sampler - a samplerBuffer,
|
||||
// isamplerBuffer or usamplerBuffer, i.e. an OpTypeImage with Dim = Buffer.
|
||||
// True when the module declares any buffer-backed image type - an OpTypeImage with
|
||||
// Dim = Buffer. That is the samplerBuffer / isamplerBuffer / usamplerBuffer
|
||||
// family and equally the imageBuffer / iimageBuffer / uimageBuffer one: SPIRV-Cross
|
||||
// requires GL_EXT_texture_buffer for both, from the same branch, so both are
|
||||
// uncompilable on a driver without buffer textures and both belong here.
|
||||
// DirectGLES asks before handing the transpiled ESSL to the driver: buffer
|
||||
// textures are core in the OpenGL 3.1+ context MobileGL advertises but need
|
||||
// ES 3.2 or EXT/OES_texture_buffer on the host, and on a driver without them
|
||||
|
||||
Reference in New Issue
Block a user