mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Fix, Test] (BackendLoader, DirectVulkan, ShaderTranspiler): report GL_MAX_CLIP_DISTANCES from the backend's real clip-distance capability
This commit is contained in:
@@ -372,6 +372,16 @@ namespace MobileGL {
|
||||
Int MaxComputeImageUniforms = 8;
|
||||
Int MaxDrawBuffers = 8;
|
||||
Int MaxColorAttachments = 8;
|
||||
// GL_MAX_CLIP_DISTANCES. Zero is a legal answer here, not a placeholder, and a
|
||||
// backend that cannot host a clip distance MUST report it: advertising eight the
|
||||
// backend will refuse does not make gl_ClipDistance work, it only moves the failure
|
||||
// from an honest "unsupported" at query time to a backend shader-compile error the
|
||||
// frontend never surfaces, after which every draw with that program silently renders
|
||||
// nothing. DirectGLES fills it from GL_EXT_clip_cull_distance, DirectVulkan from the
|
||||
// shaderClipDistance device feature. The DEFAULT stays at the GL 4.3 core minimum
|
||||
// because it describes the no-backend case (standalone shader compiles, unit tests),
|
||||
// where there is no device to be honest about and BuildTBuiltInResource still has to
|
||||
// hand glslang a workable gl_MaxClipDistances.
|
||||
Int MaxClipDistances = 8;
|
||||
Int MaxViewports = 16;
|
||||
Int MaxViewportWidth = 16384;
|
||||
|
||||
@@ -905,7 +905,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const Int maxSupportedDrawBuffers = static_cast<Int>(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS);
|
||||
m_dynamicParameters.MaxDrawBuffers = std::min(m_vulkanCaps.MaxDrawBuffers, maxSupportedDrawBuffers);
|
||||
m_dynamicParameters.MaxColorAttachments = std::min(m_vulkanCaps.MaxColorAttachments, maxSupportedDrawBuffers);
|
||||
m_dynamicParameters.MaxClipDistances = m_vulkanCaps.MaxClipDistances;
|
||||
// Same shape as the image-uniform limits three lines above: maxClipDistances is reported
|
||||
// by every device, but declaring ClipDistance in a module needs the shaderClipDistance
|
||||
// FEATURE, which VulkanRenderer enables exactly where the physical device has it. Without
|
||||
// it the limit describes a capacity no shader may use, so report none.
|
||||
m_dynamicParameters.MaxClipDistances =
|
||||
m_vulkanCaps.SupportsShaderClipDistance ? std::max(m_vulkanCaps.MaxClipDistances, 0) : 0;
|
||||
m_dynamicParameters.MaxViewports = m_vulkanCaps.MaxViewports;
|
||||
m_dynamicParameters.MaxViewportWidth = m_vulkanCaps.MaxViewportWidth;
|
||||
m_dynamicParameters.MaxViewportHeight = m_vulkanCaps.MaxViewportHeight;
|
||||
|
||||
@@ -151,6 +151,18 @@ void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }
|
||||
glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, out);
|
||||
}
|
||||
|
||||
// GL_MAX_CLIP_DISTANCES is a real backend answer, not a constant: DirectGLES reports
|
||||
// 0 on a driver without GL_EXT_clip_cull_distance, and DirectVulkan reports 0 without
|
||||
// the shaderClipDistance device feature. On such a stack the shader above cannot
|
||||
// compile - and MUST not, because declaring a clip distance the backend cannot host
|
||||
// is exactly what used to link cleanly and then render nothing. Skip rather than
|
||||
// fail: there is no clipping to assert about.
|
||||
static bool BackendHostsTwoClipDistances() {
|
||||
GLint maxClipDistances = 0;
|
||||
glGetIntegerv(GL_MAX_CLIP_DISTANCES, &maxClipDistances);
|
||||
return maxClipDistances >= 2;
|
||||
}
|
||||
|
||||
// Never assume the eight start disabled - see the header note about
|
||||
// XfbAfterClipDistanceScenario leaving one on for the rest of the process.
|
||||
static void DisableEveryClipDistance() {
|
||||
@@ -229,6 +241,9 @@ void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }
|
||||
// The claim: an enabled clip distance removes the fragments where it is negative.
|
||||
TEST_F(ClipDistanceScenario, AnEnabledClipDistanceRemovesTheNegativeHalf) {
|
||||
if (!Ready()) return;
|
||||
if (!BackendHostsTwoClipDistances()) {
|
||||
GTEST_SKIP() << "this backend advertises no clip distances, so there is nothing to clip with";
|
||||
}
|
||||
HeadlessGL& gl = Gl();
|
||||
const int width = gl.Width();
|
||||
const int height = gl.Height();
|
||||
@@ -280,6 +295,9 @@ void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }
|
||||
// draw simply failed - would pass the case above.
|
||||
TEST_F(ClipDistanceScenario, ADisabledClipDistanceRemovesNothing) {
|
||||
if (!Ready()) return;
|
||||
if (!BackendHostsTwoClipDistances()) {
|
||||
GTEST_SKIP() << "this backend advertises no clip distances, so there is nothing to clip with";
|
||||
}
|
||||
HeadlessGL& gl = Gl();
|
||||
const int width = gl.Width();
|
||||
const int height = gl.Height();
|
||||
@@ -329,6 +347,9 @@ void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }
|
||||
// passes both cases above and fails this one.
|
||||
TEST_F(ClipDistanceScenario, TheEnablesAreIndependentPerDistance) {
|
||||
if (!Ready()) return;
|
||||
if (!BackendHostsTwoClipDistances()) {
|
||||
GTEST_SKIP() << "this backend advertises no clip distances, so there is nothing to clip with";
|
||||
}
|
||||
HeadlessGL& gl = Gl();
|
||||
const int width = gl.Width();
|
||||
const int height = gl.Height();
|
||||
|
||||
@@ -45,6 +45,13 @@ namespace {
|
||||
GLint maxFragmentSsboBlocks = 9;
|
||||
bool tessAndGeometrySsboBlocksQueried = false;
|
||||
bool perStageSsboBlockQueryRaisesError = false;
|
||||
// GL_MAX_CLIP_DISTANCES. Not ES core in any version - it exists only as
|
||||
// GL_MAX_CLIP_DISTANCES_EXT under GL_EXT_clip_cull_distance - so asking a driver without
|
||||
// the extension raises GL_INVALID_ENUM and leaves the out-param untouched. The "queried"
|
||||
// flag is what pins the gating; the "raises error" knob is what pins the drain.
|
||||
GLint maxClipDistances = 8;
|
||||
bool maxClipDistancesQueried = false;
|
||||
bool clipDistanceQueryRaisesError = false;
|
||||
GLfloat minFragmentInterpolationOffset = -0.75f;
|
||||
GLfloat maxFragmentInterpolationOffset = 0.625f;
|
||||
GLint fragmentInterpolationOffsetBits = 6;
|
||||
@@ -160,6 +167,14 @@ namespace {
|
||||
case GL_MAX_COMPUTE_IMAGE_UNIFORMS:
|
||||
*data = g_fake.maxComputeImageUniforms;
|
||||
break;
|
||||
case GL_MAX_CLIP_DISTANCES:
|
||||
g_fake.maxClipDistancesQueried = true;
|
||||
if (g_fake.clipDistanceQueryRaisesError) {
|
||||
g_fake.pendingError = GL_INVALID_ENUM;
|
||||
} else {
|
||||
*data = g_fake.maxClipDistances;
|
||||
}
|
||||
break;
|
||||
case GL_FRAGMENT_INTERPOLATION_OFFSET_BITS:
|
||||
g_fake.fragmentInterpolationLimitsQueried = true;
|
||||
if (g_fake.fragmentInterpolationQueryRaisesError) {
|
||||
@@ -642,6 +657,64 @@ TEST(PerStageStorageBlockCapabilities, ARejectedQueryIsDrainedAndFallsBackToTheS
|
||||
EXPECT_EQ(g_fake.pendingError, static_cast<GLenum>(GL_NO_ERROR));
|
||||
}
|
||||
|
||||
// GL_MAX_CLIP_DISTANCES is the same defect as the per-stage storage blocks above, one pname
|
||||
// over: the query does not exist without GL_EXT_clip_cull_distance, so an unguarded probe left
|
||||
// an optimistic 8 behind on every ARM driver. Advertising eight clip planes a driver cannot host
|
||||
// does not make gl_ClipDistance work - SPIRV-Cross emits it behind an `#extension ... : require`
|
||||
// the ESSL compiler rejects, DirectGLES has nowhere to put the per-distance enables, and the
|
||||
// draw renders nothing while LINK_STATUS says everything is fine.
|
||||
TEST(ClipDistanceCapabilities, NoExtensionMeansNoClipDistancesAndNoQuery) {
|
||||
const auto funcs = MakeFakeGLESFunctions();
|
||||
|
||||
ResetFakeDriver();
|
||||
g_fake.maxVertexSsboBlocks = 0;
|
||||
|
||||
MobileGL::MG_External::GLESCapabilities caps;
|
||||
ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(caps, funcs));
|
||||
|
||||
EXPECT_FALSE(caps.SupportsClipDistance);
|
||||
EXPECT_EQ(caps.MaxClipDistances, 0);
|
||||
EXPECT_FALSE(g_fake.maxClipDistancesQueried)
|
||||
<< "GL_MAX_CLIP_DISTANCES is not ES core; asking for it without the extension only leaks "
|
||||
"a GL_INVALID_ENUM";
|
||||
}
|
||||
|
||||
// The other half of the same claim, and the one that keeps this from being a blanket zero: a
|
||||
// driver that HAS the extension must have its real limit come through untouched. Adreno does,
|
||||
// and it passes the clip-distance conformance cases on the strength of it.
|
||||
TEST(ClipDistanceCapabilities, TheExtensionIsQueriedAndItsLimitIsReportedVerbatim) {
|
||||
ResetFakeDriver();
|
||||
g_fake.maxVertexSsboBlocks = 0;
|
||||
g_fake.extensions.emplace_back("GL_EXT_clip_cull_distance");
|
||||
g_fake.maxClipDistances = 6;
|
||||
const auto funcs = MakeFakeGLESFunctions();
|
||||
|
||||
MobileGL::MG_External::GLESCapabilities caps;
|
||||
ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(caps, funcs));
|
||||
|
||||
EXPECT_TRUE(caps.SupportsClipDistance);
|
||||
EXPECT_TRUE(g_fake.maxClipDistancesQueried);
|
||||
EXPECT_EQ(caps.MaxClipDistances, 6);
|
||||
}
|
||||
|
||||
// A driver that advertises the extension and then refuses the query is a driver fault, not a
|
||||
// missing feature - but the answer has to be the honest zero either way, and the error must not
|
||||
// be left for the application's first glGetError to find.
|
||||
TEST(ClipDistanceCapabilities, ARejectedQueryIsDrainedAndReportsZero) {
|
||||
ResetFakeDriver();
|
||||
g_fake.maxVertexSsboBlocks = 0;
|
||||
g_fake.extensions.emplace_back("GL_EXT_clip_cull_distance");
|
||||
g_fake.clipDistanceQueryRaisesError = true;
|
||||
const auto funcs = MakeFakeGLESFunctions();
|
||||
|
||||
MobileGL::MG_External::GLESCapabilities caps;
|
||||
ASSERT_TRUE(MobileGL::MG_Util::BackendLoader::FillInGLESCapabilities(caps, funcs));
|
||||
|
||||
EXPECT_TRUE(g_fake.maxClipDistancesQueried);
|
||||
EXPECT_EQ(caps.MaxClipDistances, 0);
|
||||
EXPECT_EQ(funcs.glGetError(), GL_NO_ERROR) << "the failed query must not leave an error behind";
|
||||
}
|
||||
|
||||
TEST(FragmentInterpolationCapabilities, QueriesOnlyWhenSupportedAndPreservesDriverLimits) {
|
||||
const auto funcs = MakeFakeGLESFunctions();
|
||||
|
||||
|
||||
@@ -643,6 +643,28 @@ TEST(DirectGLESSanity, PreservesHostPerStageImageUniformLimits) {
|
||||
EXPECT_EQ(params.MaxComputeImageUniforms, 5);
|
||||
}
|
||||
|
||||
// maxClipDistances is a LIMIT every Vulkan device reports; declaring ClipDistance in a module
|
||||
// needs the shaderClipDistance FEATURE, which is separate and which VulkanRenderer enables only
|
||||
// where the physical device has it. Forwarding the limit without the feature advertises eight
|
||||
// clip planes no shader may use - the same shape as the image-uniform limits above, and the same
|
||||
// shape as the GL_EXT_clip_cull_distance lie on DirectGLES. Not a blanket zero: a device WITH the
|
||||
// feature keeps its real number.
|
||||
TEST(DirectVulkanSanity, GatesClipDistancesOnTheShaderClipDistanceFeature) {
|
||||
using namespace MobileGL;
|
||||
|
||||
MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend;
|
||||
MG_External::VulkanCapabilities caps;
|
||||
caps.MaxClipDistances = 8;
|
||||
|
||||
caps.SupportsShaderClipDistance = false;
|
||||
backend.ApplyVulkanCapabilitiesForTesting(caps);
|
||||
EXPECT_EQ(backend.GetDynamicParameters().MaxClipDistances, 0);
|
||||
|
||||
caps.SupportsShaderClipDistance = true;
|
||||
backend.ApplyVulkanCapabilitiesForTesting(caps);
|
||||
EXPECT_EQ(backend.GetDynamicParameters().MaxClipDistances, 8);
|
||||
}
|
||||
|
||||
TEST(FragmentInterpolationCapabilities, PlumbsGLESAndBothVulkanPropertyPaths) {
|
||||
using namespace MobileGL;
|
||||
|
||||
|
||||
@@ -1063,7 +1063,15 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
GLint maxComputeImageUniforms = 8;
|
||||
GLint maxDrawBuffers = 8;
|
||||
GLint maxColorAttachments = 8;
|
||||
GLint maxClipDistances = 8;
|
||||
// Zero is a legal answer, not a placeholder. GL_MAX_CLIP_DISTANCES exists in ES only as
|
||||
// GL_MAX_CLIP_DISTANCES_EXT under GL_EXT_clip_cull_distance, so on a driver without that
|
||||
// extension there is nowhere to put a clip distance at all: SPIRV-Cross emits
|
||||
// gl_ClipDistance behind an `#extension ... : require` the ESSL compiler rejects, and
|
||||
// DirectGLES has no state to forward the per-distance enables into (see the gate in
|
||||
// DirectGLES::SyncRenderState). Starting at 8 meant a probe that could never run left an
|
||||
// optimistic 8 behind, so the frontend promised eight clip planes and every draw with a
|
||||
// clipping program silently rendered nothing. The guarded probe below only ever widens it.
|
||||
GLint maxClipDistances = 0;
|
||||
GLint maxViewports = 16;
|
||||
GLfloat minFragmentInterpolationOffset = -0.5f;
|
||||
GLfloat maxFragmentInterpolationOffset = 0.4375f;
|
||||
@@ -1195,7 +1203,30 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
}
|
||||
glesFuncs.glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
|
||||
glesFuncs.glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments);
|
||||
glesFuncs.glGetIntegerv(GL_MAX_CLIP_DISTANCES, &maxClipDistances);
|
||||
// GL_MAX_CLIP_DISTANCES is 0x0D32, which ES only ever spells GL_MAX_CLIP_DISTANCES_EXT and
|
||||
// only ever has under GL_EXT_clip_cull_distance. The extension was already resolved into
|
||||
// caps.SupportsClipDistance a few hundred lines above and is the same flag DirectGLES
|
||||
// gates the CLIP_DISTANCEi enable forwarding on, so ask the driver only where the pname
|
||||
// exists; everywhere else the honest 0 stands and no GL_INVALID_ENUM is left behind for an
|
||||
// unrelated query - or the application's first glGetError - to trip over.
|
||||
if (caps.SupportsClipDistance) {
|
||||
if (glesFuncs.glGetError) {
|
||||
while (glesFuncs.glGetError() != GL_NO_ERROR) {
|
||||
}
|
||||
}
|
||||
glesFuncs.glGetIntegerv(GL_MAX_CLIP_DISTANCES, &maxClipDistances);
|
||||
Bool queryFailed = false;
|
||||
if (glesFuncs.glGetError) {
|
||||
while (glesFuncs.glGetError() != GL_NO_ERROR) {
|
||||
queryFailed = true;
|
||||
}
|
||||
}
|
||||
if (queryFailed) {
|
||||
MGLOG_W("GL_EXT_clip_cull_distance is advertised but GL_MAX_CLIP_DISTANCES was "
|
||||
"rejected; reporting no clip distances");
|
||||
maxClipDistances = 0;
|
||||
}
|
||||
}
|
||||
glesFuncs.glGetIntegerv(GL_MAX_VIEWPORTS, &maxViewports);
|
||||
glesFuncs.glGetIntegerv(GL_MAX_VIEWPORT_DIMS, maxViewportDims);
|
||||
glesFuncs.glGetIntegerv(GL_VIEWPORT_SUBPIXEL_BITS, &viewportSubpixelBits);
|
||||
@@ -1368,7 +1399,9 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
caps.MaxComputeImageUniforms = maxComputeImageUniforms;
|
||||
caps.MaxDrawBuffers = maxDrawBuffers;
|
||||
caps.MaxColorAttachments = maxColorAttachments;
|
||||
caps.MaxClipDistances = maxClipDistances;
|
||||
// A driver is free to write nonsense into an out-param it then rejects, and without the
|
||||
// extension the probe above never ran at all - so the flag, not the local, decides.
|
||||
caps.MaxClipDistances = caps.SupportsClipDistance ? std::max(maxClipDistances, 0) : 0;
|
||||
caps.MaxViewports = maxViewports;
|
||||
caps.MaxViewportWidth = maxViewportDims[0];
|
||||
caps.MaxViewportHeight = maxViewportDims[1];
|
||||
@@ -1450,7 +1483,12 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
MGLOG_I(" GL_MAX_COMPUTE_IMAGE_UNIFORMS: %d", caps.MaxComputeImageUniforms);
|
||||
MGLOG_I(" GL_MAX_DRAW_BUFFERS: %d", caps.MaxDrawBuffers);
|
||||
MGLOG_I(" GL_MAX_COLOR_ATTACHMENTS: %d", caps.MaxColorAttachments);
|
||||
MGLOG_I(" GL_MAX_CLIP_DISTANCES: %d", caps.MaxClipDistances);
|
||||
// Worth spelling the reason out for the same reason the per-stage storage block counts
|
||||
// are: a zero here is what stops an application's gl_ClipDistance from ever clipping, and
|
||||
// reading it back from an artifact is the difference between "MobileGL dropped my draw"
|
||||
// and "this driver has no clip distances".
|
||||
MGLOG_I(" GL_MAX_CLIP_DISTANCES: %d%s", caps.MaxClipDistances,
|
||||
caps.SupportsClipDistance ? "" : " (no GL_EXT_clip_cull_distance on this driver)");
|
||||
MGLOG_I(" GL_MAX_VIEWPORTS: %d", caps.MaxViewports);
|
||||
MGLOG_I(" GL_MAX_VIEWPORT_DIMS: [%d, %d]", caps.MaxViewportWidth, caps.MaxViewportHeight);
|
||||
MGLOG_I(" GL_VIEWPORT_BOUNDS_RANGE: [%.3f, %.3f]", caps.ViewportBoundsRangeMin,
|
||||
|
||||
@@ -1275,7 +1275,10 @@ namespace MobileGL {
|
||||
Int MaxComputeImageUniforms = 8;
|
||||
Int MaxDrawBuffers = 8;
|
||||
Int MaxColorAttachments = 8;
|
||||
Int MaxClipDistances = 8;
|
||||
// Zero is a legal answer, not a placeholder: ES reaches clip distances only through
|
||||
// GL_EXT_clip_cull_distance, so a driver without it has none. See the guarded probe
|
||||
// in FillInGLESCapabilities.
|
||||
Int MaxClipDistances = 0;
|
||||
Int MaxViewports = 16;
|
||||
Int MaxViewportWidth = 16384;
|
||||
Int MaxViewportHeight = 16384;
|
||||
|
||||
@@ -237,6 +237,7 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
supportedFeatures.vertexPipelineStoresAndAtomics == VK_TRUE;
|
||||
caps.SupportsFragmentStoresAndAtomics = supportedFeatures.fragmentStoresAndAtomics == VK_TRUE;
|
||||
caps.SupportsGeometryShader = supportedFeatures.geometryShader == VK_TRUE;
|
||||
caps.SupportsShaderClipDistance = supportedFeatures.shaderClipDistance == VK_TRUE;
|
||||
caps.MaxShaderStorageBlockSize = static_cast<SizeT>(p.limits.maxStorageBufferRange);
|
||||
const Bool supportsShaderSubgroup = vk.vkGetPhysicalDeviceProperties2 &&
|
||||
HasUsableShaderSubgroupSupport(subgroupProps);
|
||||
@@ -331,6 +332,7 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
caps.SupportsVertexPipelineStoresAndAtomics = false;
|
||||
caps.SupportsFragmentStoresAndAtomics = false;
|
||||
caps.SupportsGeometryShader = false;
|
||||
caps.SupportsShaderClipDistance = false;
|
||||
caps.MaxShaderStorageBlockSize = static_cast<SizeT>(properties.limits.maxStorageBufferRange);
|
||||
caps.SupportsShaderSubgroup = false;
|
||||
caps.SubgroupSize = 0;
|
||||
|
||||
@@ -96,6 +96,12 @@ namespace MobileGL {
|
||||
Bool SupportsVertexPipelineStoresAndAtomics = false;
|
||||
Bool SupportsFragmentStoresAndAtomics = false;
|
||||
Bool SupportsGeometryShader = false;
|
||||
// VkPhysicalDeviceFeatures::shaderClipDistance. maxClipDistances is a LIMIT and is
|
||||
// reported whatever the feature says, so the limit alone does not mean a module may
|
||||
// declare ClipDistance - VulkanRenderer enables the feature only where the physical
|
||||
// device has it, and without it a shader writing gl_ClipDistance is invalid. Very
|
||||
// widely supported, hence read from the device features and never assumed false.
|
||||
Bool SupportsShaderClipDistance = false;
|
||||
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
||||
Bool SupportsShaderSubgroup = false;
|
||||
Uint32 SubgroupSize = 0;
|
||||
|
||||
@@ -82,7 +82,6 @@ namespace MobileGL {
|
||||
Resources.maxFragmentInputVectors = 15;
|
||||
Resources.minProgramTexelOffset = -8;
|
||||
Resources.maxProgramTexelOffset = 7;
|
||||
Resources.maxClipDistances = 8;
|
||||
Resources.maxComputeUniformComponents = MAX_COMPUTE_UNIFORM_COMPONENTS;
|
||||
Resources.maxComputeTextureImageUnits = 16;
|
||||
Resources.maxComputeImageUniforms = 8;
|
||||
@@ -173,6 +172,14 @@ namespace MobileGL {
|
||||
Resources.maxComputeImageUniforms = dynamicParameters.MaxComputeImageUniforms;
|
||||
Resources.maxCombinedImageUniforms = dynamicParameters.MaxCombinedImageUniforms;
|
||||
Resources.maxComputeTextureImageUnits = dynamicParameters.MaxComputeTextureImageUnits;
|
||||
// Load-bearing, not cosmetic. glslang rejects gl_ClipDistance[i] for
|
||||
// i >= maxClipDistances (ParseHelper.cpp) and expands gl_MaxClipDistances from the
|
||||
// same number, so tracking the backend limit is what turns "the program links,
|
||||
// the backend's shader compile fails somewhere the frontend never surfaces, and
|
||||
// the draw renders nothing" into an honest glCompileShader error with a log. It is
|
||||
// also what makes glGetIntegerv(GL_MAX_CLIP_DISTANCES) and gl_MaxClipDistances
|
||||
// agree, which KHR-GLxx.clip_distance.coverage compares directly.
|
||||
Resources.maxClipDistances = dynamicParameters.MaxClipDistances;
|
||||
|
||||
// The compute work-group limits are the env's, not the backend parameters': they
|
||||
// are the only ones that come from a REAL indexed driver query, which
|
||||
|
||||
Reference in New Issue
Block a user