mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 14:18:31 +09:00
[Fix] (BackendLoader, DirectGLES, DirectVulkan): derive the per-stage shader storage block limits from the backend
This commit is contained in:
@@ -340,6 +340,22 @@ namespace MobileGL {
|
|||||||
Int MaxVertexAttribs = 16;
|
Int MaxVertexAttribs = 16;
|
||||||
Int MaxComputeShaderStorageBlocks = 8;
|
Int MaxComputeShaderStorageBlocks = 8;
|
||||||
Int MaxCombinedShaderStorageBlocks = 32;
|
Int MaxCombinedShaderStorageBlocks = 32;
|
||||||
|
// Per-stage GL_MAX_*_SHADER_STORAGE_BLOCKS. Zero is a legal answer for the four
|
||||||
|
// non-compute, non-fragment stages and these defaults are the spec minimums, not
|
||||||
|
// placeholders: GL 4.6 table 23.64 and ES 3.2 table 21.44 both set the minimum for
|
||||||
|
// vertex, tessellation control, tessellation evaluation and geometry at 0, and only
|
||||||
|
// fragment (8 in GL, 4 in ES) and compute are guaranteed to have any. Every real ARM
|
||||||
|
// GLES driver takes that allowance - a Mali-G925 reports 0 for all four - so a
|
||||||
|
// backend that cannot honour a graphics-stage storage block MUST report 0 here
|
||||||
|
// rather than a hopeful number. Advertising a non-zero count the driver will refuse
|
||||||
|
// does not make the block work; it only moves the failure from an honest
|
||||||
|
// "unsupported" at query time to a backend link error the frontend never surfaces,
|
||||||
|
// after which every draw with that program silently renders nothing.
|
||||||
|
Int MaxVertexShaderStorageBlocks = 0;
|
||||||
|
Int MaxTessControlShaderStorageBlocks = 0;
|
||||||
|
Int MaxTessEvaluationShaderStorageBlocks = 0;
|
||||||
|
Int MaxGeometryShaderStorageBlocks = 0;
|
||||||
|
Int MaxFragmentShaderStorageBlocks = 8;
|
||||||
Int MaxComputeUniformBlocks = 12;
|
Int MaxComputeUniformBlocks = 12;
|
||||||
Int MaxComputeWorkGroupInvocations = 128;
|
Int MaxComputeWorkGroupInvocations = 128;
|
||||||
Int MaxShaderStorageBufferBindings = 8;
|
Int MaxShaderStorageBufferBindings = 8;
|
||||||
|
|||||||
@@ -1251,9 +1251,31 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
static_cast<Int>(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS));
|
static_cast<Int>(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS));
|
||||||
m_dynamicParameters.MaxComputeShaderStorageBlocks = m_GLESCapabilities.MaxComputeShaderStorageBlocks;
|
m_dynamicParameters.MaxComputeShaderStorageBlocks = m_GLESCapabilities.MaxComputeShaderStorageBlocks;
|
||||||
m_dynamicParameters.MaxCombinedShaderStorageBlocks = m_GLESCapabilities.MaxCombinedShaderStorageBlocks;
|
m_dynamicParameters.MaxCombinedShaderStorageBlocks = m_GLESCapabilities.MaxCombinedShaderStorageBlocks;
|
||||||
|
// Per-stage storage-block counts, forwarded from the host driver rather than invented.
|
||||||
|
// A stage the driver cannot serve reports 0, which is a legal answer everywhere these
|
||||||
|
// limits appear (GL 4.6 table 23.64, ES 3.2 table 21.44 - the minimum is 0 for every
|
||||||
|
// graphics stage except fragment) and is the only answer that lets an application take
|
||||||
|
// its own fallback instead of building a program the driver will refuse to link. The
|
||||||
|
// stage limit cannot exceed the combined limit or the number of binding points there
|
||||||
|
// are to bind buffers to, so clamp to both.
|
||||||
|
const auto clampStageStorageBlocks = [this](Int stageLimit) {
|
||||||
|
return std::min({std::max(stageLimit, 0), std::max(m_dynamicParameters.MaxCombinedShaderStorageBlocks, 0),
|
||||||
|
std::max(m_dynamicParameters.MaxShaderStorageBufferBindings, 0)});
|
||||||
|
};
|
||||||
|
m_dynamicParameters.MaxShaderStorageBufferBindings = m_GLESCapabilities.MaxShaderStorageBufferBindings;
|
||||||
|
m_dynamicParameters.MaxVertexShaderStorageBlocks =
|
||||||
|
clampStageStorageBlocks(m_GLESCapabilities.MaxVertexShaderStorageBlocks);
|
||||||
|
m_dynamicParameters.MaxTessControlShaderStorageBlocks =
|
||||||
|
clampStageStorageBlocks(m_GLESCapabilities.MaxTessControlShaderStorageBlocks);
|
||||||
|
m_dynamicParameters.MaxTessEvaluationShaderStorageBlocks =
|
||||||
|
clampStageStorageBlocks(m_GLESCapabilities.MaxTessEvaluationShaderStorageBlocks);
|
||||||
|
m_dynamicParameters.MaxGeometryShaderStorageBlocks =
|
||||||
|
clampStageStorageBlocks(m_GLESCapabilities.MaxGeometryShaderStorageBlocks);
|
||||||
|
m_dynamicParameters.MaxFragmentShaderStorageBlocks =
|
||||||
|
clampStageStorageBlocks(m_GLESCapabilities.MaxFragmentShaderStorageBlocks);
|
||||||
m_dynamicParameters.MaxComputeUniformBlocks = m_GLESCapabilities.MaxComputeUniformBlocks;
|
m_dynamicParameters.MaxComputeUniformBlocks = m_GLESCapabilities.MaxComputeUniformBlocks;
|
||||||
m_dynamicParameters.MaxComputeWorkGroupInvocations = m_GLESCapabilities.MaxComputeWorkGroupInvocations;
|
m_dynamicParameters.MaxComputeWorkGroupInvocations = m_GLESCapabilities.MaxComputeWorkGroupInvocations;
|
||||||
m_dynamicParameters.MaxShaderStorageBufferBindings = m_GLESCapabilities.MaxShaderStorageBufferBindings;
|
// (MaxShaderStorageBufferBindings is assigned above, before the per-stage clamp reads it.)
|
||||||
// This is the number glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE) hands the application, and
|
// This is the number glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE) hands the application, and
|
||||||
// on a host without buffer textures it is knowingly a floor MobileGL cannot honour rather
|
// on a host without buffer textures it is knowingly a floor MobileGL cannot honour rather
|
||||||
// than a driver answer (m_GLESCapabilities.MaxTextureBufferSizeIsDriverReported says
|
// than a driver answer (m_GLESCapabilities.MaxTextureBufferSizeIsDriverReported says
|
||||||
|
|||||||
@@ -847,6 +847,38 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_dynamicParameters.MaxShaderStorageBufferBindings =
|
m_dynamicParameters.MaxShaderStorageBufferBindings =
|
||||||
clampLimit("GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS", m_vulkanCaps.MaxShaderStorageBufferBindings,
|
clampLimit("GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS", m_vulkanCaps.MaxShaderStorageBufferBindings,
|
||||||
kMaxAdvertisedBufferBlocks);
|
kMaxAdvertisedBufferBlocks);
|
||||||
|
// Per-stage GL_MAX_*_SHADER_STORAGE_BLOCKS. Vulkan has one descriptor limit for every
|
||||||
|
// stage (maxPerStageDescriptorStorageBuffers, which is what MaxComputeShaderStorageBlocks
|
||||||
|
// carries), so the stage limits differ only by whether the stage can have blocks at all.
|
||||||
|
//
|
||||||
|
// Deliberately NOT gated on vertexPipelineStoresAndAtomics, unlike the per-stage image
|
||||||
|
// uniforms below. That gate reads as the obvious one and is wrong here in practice: a
|
||||||
|
// Mali-G925-Immortalis reports vertexPipelineStoresAndAtomics=false (supported AND
|
||||||
|
// enabled) and yet runs all 433 KHR-GL43.constant_expressions.*_tess_* cases correctly
|
||||||
|
// through this backend - those write their result through a storage block declared in a
|
||||||
|
// tessellation stage. Gating would report 0 and turn 433 passing cases into
|
||||||
|
// "unsupported", removing function that demonstrably works.
|
||||||
|
//
|
||||||
|
// The asymmetry with DirectGLES is real and is the point. There, 0 prevents a program
|
||||||
|
// the driver refuses outright at link time; the honest limit converts a silent
|
||||||
|
// wrong-render into a capability an application can route around. Here there is no such
|
||||||
|
// failure to prevent, so the limit stays at what the device can address. If a Vulkan
|
||||||
|
// device is ever found that genuinely rejects such a pipeline, the gate belongs at
|
||||||
|
// pipeline creation where the rejection is observable, not on a feature bit this driver
|
||||||
|
// reports inaccurately.
|
||||||
|
{
|
||||||
|
const Int maxPerStageStorageBlocks =
|
||||||
|
std::min(std::max(m_dynamicParameters.MaxComputeShaderStorageBlocks, 0),
|
||||||
|
std::min(std::max(m_dynamicParameters.MaxCombinedShaderStorageBlocks, 0),
|
||||||
|
std::max(m_dynamicParameters.MaxShaderStorageBufferBindings, 0)));
|
||||||
|
m_dynamicParameters.MaxVertexShaderStorageBlocks = maxPerStageStorageBlocks;
|
||||||
|
m_dynamicParameters.MaxTessControlShaderStorageBlocks = maxPerStageStorageBlocks;
|
||||||
|
m_dynamicParameters.MaxTessEvaluationShaderStorageBlocks = maxPerStageStorageBlocks;
|
||||||
|
// The one hard capability in the set: no geometry stage means no blocks in it.
|
||||||
|
m_dynamicParameters.MaxGeometryShaderStorageBlocks =
|
||||||
|
m_vulkanCaps.SupportsGeometryShader ? maxPerStageStorageBlocks : 0;
|
||||||
|
m_dynamicParameters.MaxFragmentShaderStorageBlocks = maxPerStageStorageBlocks;
|
||||||
|
}
|
||||||
m_dynamicParameters.MaxTextureBufferSize = clampLimit(
|
m_dynamicParameters.MaxTextureBufferSize = clampLimit(
|
||||||
"GL_MAX_TEXTURE_BUFFER_SIZE", m_vulkanCaps.MaxTextureBufferSize, kMaxAdvertisedTextureBufferSize);
|
"GL_MAX_TEXTURE_BUFFER_SIZE", m_vulkanCaps.MaxTextureBufferSize, kMaxAdvertisedTextureBufferSize);
|
||||||
m_dynamicParameters.TextureBufferOffsetAlignment = m_vulkanCaps.TextureBufferOffsetAlignment;
|
m_dynamicParameters.TextureBufferOffsetAlignment = m_vulkanCaps.TextureBufferOffsetAlignment;
|
||||||
|
|||||||
@@ -692,8 +692,12 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
!f.glUnmapBuffer || !f.glMemoryBarrier || !f.glCreateShader || !f.glCreateProgram) {
|
!f.glUnmapBuffer || !f.glMemoryBarrier || !f.glCreateShader || !f.glCreateProgram) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
GLint maxVertexSsboBlocks = 0;
|
// Read from caps, not re-queried: the per-stage limits are resolved (and their query
|
||||||
f.glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexSsboBlocks);
|
// errors drained) before this probe runs, so asking the driver again would be a second
|
||||||
|
// round trip that can disagree with the number MobileGL actually advertises - and, on
|
||||||
|
// the early return below, would leave its own GL_INVALID_ENUM in the queue for the
|
||||||
|
// application's first glGetError to find.
|
||||||
|
const GLint maxVertexSsboBlocks = caps.MaxVertexShaderStorageBlocks;
|
||||||
if (maxVertexSsboBlocks < 1) {
|
if (maxVertexSsboBlocks < 1) {
|
||||||
// The native indirect machinery cannot read the command buffer from the vertex
|
// The native indirect machinery cannot read the command buffer from the vertex
|
||||||
// stage on this driver anyway; assume conforming zero-based gl_InstanceID.
|
// stage on this driver anyway; assume conforming zero-based gl_InstanceID.
|
||||||
@@ -1036,6 +1040,15 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
GLint maxVertexAttribs = 16;
|
GLint maxVertexAttribs = 16;
|
||||||
GLint maxComputeShaderStorageBlocks = 8;
|
GLint maxComputeShaderStorageBlocks = 8;
|
||||||
GLint maxCombinedShaderStorageBlocks = 32;
|
GLint maxCombinedShaderStorageBlocks = 32;
|
||||||
|
// ES 3.2 table 21.44 minimums. Zero for the four graphics stages below fragment is not a
|
||||||
|
// placeholder - it is what the spec permits and what ARM's GLES driver actually reports,
|
||||||
|
// so a probe that never runs (pre-ES 3.2, unsupported pname) leaves behind the truthful
|
||||||
|
// answer rather than an optimistic one.
|
||||||
|
GLint maxVertexShaderStorageBlocks = 0;
|
||||||
|
GLint maxTessControlShaderStorageBlocks = 0;
|
||||||
|
GLint maxTessEvaluationShaderStorageBlocks = 0;
|
||||||
|
GLint maxGeometryShaderStorageBlocks = 0;
|
||||||
|
GLint maxFragmentShaderStorageBlocks = 4;
|
||||||
GLint maxComputeUniformBlocks = 12;
|
GLint maxComputeUniformBlocks = 12;
|
||||||
GLint maxComputeWorkGroupInvocations = 128;
|
GLint maxComputeWorkGroupInvocations = 128;
|
||||||
GLint maxShaderStorageBufferBindings = 8;
|
GLint maxShaderStorageBufferBindings = 8;
|
||||||
@@ -1127,6 +1140,59 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
(caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2)) {
|
(caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2)) {
|
||||||
glesFuncs.glGetIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &maxGeometryImageUniforms);
|
glesFuncs.glGetIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &maxGeometryImageUniforms);
|
||||||
}
|
}
|
||||||
|
// Per-stage storage-block counts. Deliberately NOT batched with the unconditional probes
|
||||||
|
// above, for the reason GL_MAX_TEXTURE_BUFFER_SIZE is not: the vertex and fragment pnames
|
||||||
|
// are ES 3.1, but the tessellation and geometry ones only exist from ES 3.2 on (or under
|
||||||
|
// EXT_tessellation_shader / EXT_geometry_shader), so on an older context they raise
|
||||||
|
// GL_INVALID_ENUM, leave the local untouched, and - with nothing draining the queue until
|
||||||
|
// some later probe - let that error be misattributed to an unrelated query in between, or
|
||||||
|
// leak into the application's first glGetError.
|
||||||
|
//
|
||||||
|
// A stage whose probe does not run keeps the spec minimum, which for all four graphics
|
||||||
|
// stages is 0. That is the honest answer: DirectGLES emits ESSL 3.10 on an ES 3.1 context,
|
||||||
|
// where those stages do not exist at all.
|
||||||
|
{
|
||||||
|
const auto drainErrors = [&glesFuncs]() {
|
||||||
|
Bool hadError = false;
|
||||||
|
if (glesFuncs.glGetError) {
|
||||||
|
while (glesFuncs.glGetError() != GL_NO_ERROR) hadError = true;
|
||||||
|
}
|
||||||
|
return hadError;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Isolate from errors raised by the preceding probes so the drain below reports on
|
||||||
|
// these queries only.
|
||||||
|
drainErrors();
|
||||||
|
glesFuncs.glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexShaderStorageBlocks);
|
||||||
|
glesFuncs.glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
|
||||||
|
if (drainErrors()) {
|
||||||
|
MGLOG_W("Per-stage shader storage block query failed for the vertex/fragment "
|
||||||
|
"stages; assuming the ES minimums (vertex 0, fragment 4)");
|
||||||
|
maxVertexShaderStorageBlocks = 0;
|
||||||
|
maxFragmentShaderStorageBlocks = 4;
|
||||||
|
}
|
||||||
|
if (esAtLeast32) {
|
||||||
|
glesFuncs.glGetIntegerv(GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS,
|
||||||
|
&maxTessControlShaderStorageBlocks);
|
||||||
|
glesFuncs.glGetIntegerv(GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS,
|
||||||
|
&maxTessEvaluationShaderStorageBlocks);
|
||||||
|
glesFuncs.glGetIntegerv(GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS, &maxGeometryShaderStorageBlocks);
|
||||||
|
if (drainErrors()) {
|
||||||
|
MGLOG_W("Per-stage shader storage block query failed for the tessellation/"
|
||||||
|
"geometry stages; assuming the ES minimum of 0");
|
||||||
|
maxTessControlShaderStorageBlocks = 0;
|
||||||
|
maxTessEvaluationShaderStorageBlocks = 0;
|
||||||
|
maxGeometryShaderStorageBlocks = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// A driver is free to report a negative or nonsensical count into an untouched
|
||||||
|
// out-param; clamp before anything downstream treats it as a capacity.
|
||||||
|
maxVertexShaderStorageBlocks = std::max(maxVertexShaderStorageBlocks, 0);
|
||||||
|
maxTessControlShaderStorageBlocks = std::max(maxTessControlShaderStorageBlocks, 0);
|
||||||
|
maxTessEvaluationShaderStorageBlocks = std::max(maxTessEvaluationShaderStorageBlocks, 0);
|
||||||
|
maxGeometryShaderStorageBlocks = std::max(maxGeometryShaderStorageBlocks, 0);
|
||||||
|
maxFragmentShaderStorageBlocks = std::max(maxFragmentShaderStorageBlocks, 0);
|
||||||
|
}
|
||||||
glesFuncs.glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
|
glesFuncs.glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
|
||||||
glesFuncs.glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments);
|
glesFuncs.glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments);
|
||||||
glesFuncs.glGetIntegerv(GL_MAX_CLIP_DISTANCES, &maxClipDistances);
|
glesFuncs.glGetIntegerv(GL_MAX_CLIP_DISTANCES, &maxClipDistances);
|
||||||
@@ -1271,6 +1337,11 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
caps.MaxVertexAttribs = maxVertexAttribs;
|
caps.MaxVertexAttribs = maxVertexAttribs;
|
||||||
caps.MaxComputeShaderStorageBlocks = maxComputeShaderStorageBlocks;
|
caps.MaxComputeShaderStorageBlocks = maxComputeShaderStorageBlocks;
|
||||||
caps.MaxCombinedShaderStorageBlocks = maxCombinedShaderStorageBlocks;
|
caps.MaxCombinedShaderStorageBlocks = maxCombinedShaderStorageBlocks;
|
||||||
|
caps.MaxVertexShaderStorageBlocks = maxVertexShaderStorageBlocks;
|
||||||
|
caps.MaxTessControlShaderStorageBlocks = maxTessControlShaderStorageBlocks;
|
||||||
|
caps.MaxTessEvaluationShaderStorageBlocks = maxTessEvaluationShaderStorageBlocks;
|
||||||
|
caps.MaxGeometryShaderStorageBlocks = maxGeometryShaderStorageBlocks;
|
||||||
|
caps.MaxFragmentShaderStorageBlocks = maxFragmentShaderStorageBlocks;
|
||||||
caps.MaxComputeUniformBlocks = maxComputeUniformBlocks;
|
caps.MaxComputeUniformBlocks = maxComputeUniformBlocks;
|
||||||
caps.MaxComputeWorkGroupInvocations = maxComputeWorkGroupInvocations;
|
caps.MaxComputeWorkGroupInvocations = maxComputeWorkGroupInvocations;
|
||||||
caps.MaxShaderStorageBufferBindings = maxShaderStorageBufferBindings;
|
caps.MaxShaderStorageBufferBindings = maxShaderStorageBufferBindings;
|
||||||
@@ -1348,6 +1419,14 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
MGLOG_I(" GL_MAX_VERTEX_ATTRIBS: %d", caps.MaxVertexAttribs);
|
MGLOG_I(" GL_MAX_VERTEX_ATTRIBS: %d", caps.MaxVertexAttribs);
|
||||||
MGLOG_I(" GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: %d", caps.MaxComputeShaderStorageBlocks);
|
MGLOG_I(" GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: %d", caps.MaxComputeShaderStorageBlocks);
|
||||||
MGLOG_I(" GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS: %d", caps.MaxCombinedShaderStorageBlocks);
|
MGLOG_I(" GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS: %d", caps.MaxCombinedShaderStorageBlocks);
|
||||||
|
// Worth a line each: a zero here is what stops an application's storage block from ever
|
||||||
|
// working in that stage, and reading it back from an artifact is the difference between
|
||||||
|
// "MobileGL dropped my draw" and "this driver has no SSBOs outside compute".
|
||||||
|
MGLOG_I(" GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: %d", caps.MaxVertexShaderStorageBlocks);
|
||||||
|
MGLOG_I(" GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS: %d", caps.MaxTessControlShaderStorageBlocks);
|
||||||
|
MGLOG_I(" GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS: %d", caps.MaxTessEvaluationShaderStorageBlocks);
|
||||||
|
MGLOG_I(" GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS: %d", caps.MaxGeometryShaderStorageBlocks);
|
||||||
|
MGLOG_I(" GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS: %d", caps.MaxFragmentShaderStorageBlocks);
|
||||||
MGLOG_I(" GL_MAX_COMPUTE_UNIFORM_BLOCKS: %d", caps.MaxComputeUniformBlocks);
|
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_COMPUTE_WORK_GROUP_INVOCATIONS: %d", caps.MaxComputeWorkGroupInvocations);
|
||||||
MGLOG_I(" GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: %d", caps.MaxShaderStorageBufferBindings);
|
MGLOG_I(" GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: %d", caps.MaxShaderStorageBufferBindings);
|
||||||
|
|||||||
@@ -1248,6 +1248,17 @@ namespace MobileGL {
|
|||||||
Int MaxVertexAttribs = 16;
|
Int MaxVertexAttribs = 16;
|
||||||
Int MaxComputeShaderStorageBlocks = 8;
|
Int MaxComputeShaderStorageBlocks = 8;
|
||||||
Int MaxCombinedShaderStorageBlocks = 32;
|
Int MaxCombinedShaderStorageBlocks = 32;
|
||||||
|
// Per-stage GL_MAX_*_SHADER_STORAGE_BLOCKS as the host GLES driver reports them.
|
||||||
|
// The defaults are the ES 3.2 minimums (table 21.44): 0 for every graphics stage
|
||||||
|
// except fragment, which is 4. ES only gained the tessellation and geometry pnames
|
||||||
|
// in 3.2 (or with EXT_tessellation_shader / EXT_geometry_shader), so those two are
|
||||||
|
// queried behind a support check and left at the default otherwise - see
|
||||||
|
// FillInGLESCapabilities.
|
||||||
|
Int MaxVertexShaderStorageBlocks = 0;
|
||||||
|
Int MaxTessControlShaderStorageBlocks = 0;
|
||||||
|
Int MaxTessEvaluationShaderStorageBlocks = 0;
|
||||||
|
Int MaxGeometryShaderStorageBlocks = 0;
|
||||||
|
Int MaxFragmentShaderStorageBlocks = 4;
|
||||||
Int MaxComputeUniformBlocks = 12;
|
Int MaxComputeUniformBlocks = 12;
|
||||||
Int MaxComputeWorkGroupInvocations = 128;
|
Int MaxComputeWorkGroupInvocations = 128;
|
||||||
Int MaxShaderStorageBufferBindings = 8;
|
Int MaxShaderStorageBufferBindings = 8;
|
||||||
|
|||||||
Reference in New Issue
Block a user