Validate varying count when compiling shaders

This avoids crashing on devices when a number of varyings greater than the device limit is used.

For now this accurately prints an error when compiling the shader, but the error text only pops up in the editor if the number of user varyings is above the limit.
This commit is contained in:
clayjohn
2025-02-12 16:12:46 -08:00
parent 296de7da83
commit 35100396e4
17 changed files with 92 additions and 27 deletions

View File

@@ -6234,6 +6234,8 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) {
case LIMIT_VRS_MAX_FRAGMENT_WIDTH:
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
return vrs_capabilities.ss_max_fragment_size;
case LIMIT_MAX_SHADER_VARYINGS:
return MIN(D3D12_VS_OUTPUT_REGISTER_COUNT, D3D12_PS_INPUT_REGISTER_COUNT);
default: {
#ifdef DEV_ENABLED
WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + ".");

View File

@@ -110,6 +110,11 @@ Config::Config() {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
GLint max_vertex_output;
glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &max_vertex_output);
GLint max_fragment_input;
glGetIntegerv(GL_MAX_FRAGMENT_INPUT_COMPONENTS, &max_fragment_input);
max_shader_varyings = (uint32_t)MIN(max_vertex_output, max_fragment_input) / 4;
// sanity clamp buffer size to 16K..1MB
max_uniform_buffer_size = CLAMP(max_uniform_buffer_size, 16384, 1048576);

View File

@@ -62,6 +62,7 @@ public:
GLint max_texture_size = 0;
GLint max_viewport_size[2] = { 0, 0 };
GLint64 max_uniform_buffer_size = 0;
uint32_t max_shader_varyings = 0;
int64_t max_renderable_elements = 0;
int64_t max_renderable_lights = 0;

View File

@@ -465,4 +465,16 @@ Size2i Utilities::get_maximum_viewport_size() const {
return Size2i(config->max_viewport_size[0], config->max_viewport_size[1]);
}
uint32_t Utilities::get_maximum_shader_varyings() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, 31);
return config->max_shader_varyings;
}
uint64_t Utilities::get_maximum_uniform_buffer_size() const {
Config *config = Config::get_singleton();
ERR_FAIL_NULL_V(config, 65536);
return uint64_t(config->max_uniform_buffer_size);
}
#endif // GLES3_ENABLED

View File

@@ -226,6 +226,8 @@ public:
virtual String get_video_adapter_api_version() const override;
virtual Size2i get_maximum_viewport_size() const override;
virtual uint32_t get_maximum_shader_varyings() const override;
virtual uint64_t get_maximum_uniform_buffer_size() const override;
};
} // namespace GLES3

View File

@@ -124,6 +124,7 @@ struct MetalLimits {
uint32_t maxVertexInputBindings;
uint32_t maxVertexInputBindingStride;
uint32_t maxDrawIndexedIndexValue;
uint32_t maxShaderVaryings;
double temporalScalerInputContentMinScale;
double temporalScalerInputContentMaxScale;

View File

@@ -303,6 +303,7 @@ void MetalDeviceProperties::init_limits(id<MTLDevice> p_device) {
limits.maxVertexInputAttributes = 31;
limits.maxVertexInputBindings = 31;
limits.maxVertexInputBindingStride = (2 * KIBI);
limits.maxShaderVaryings = 31; // Accurate on Apple4 and above. See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
limits.minUniformBufferOffsetAlignment = 64;

View File

@@ -3999,6 +3999,8 @@ uint64_t RenderingDeviceDriverMetal::limit_get(Limit p_limit) {
return (uint64_t)((1.0 / limits.temporalScalerInputContentMaxScale) * 1000'000);
case LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE:
return (uint64_t)((1.0 / limits.temporalScalerInputContentMinScale) * 1000'000);
case LIMIT_MAX_SHADER_VARYINGS:
return limits.maxShaderVaryings;
UNKNOWN(LIMIT_VRS_TEXEL_WIDTH);
UNKNOWN(LIMIT_VRS_TEXEL_HEIGHT);
UNKNOWN(LIMIT_VRS_MAX_FRAGMENT_WIDTH);

View File

@@ -5886,6 +5886,10 @@ uint64_t RenderingDeviceDriverVulkan::limit_get(Limit p_limit) {
return vrs_capabilities.max_fragment_size.x;
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
return vrs_capabilities.max_fragment_size.y;
case LIMIT_MAX_SHADER_VARYINGS:
// The Vulkan spec states that built in varyings like gl_FragCoord should count against this, but in
// practice, that doesn't seem to be the case. The validation layers don't even complain.
return MIN(limits.maxVertexOutputComponents / 4, limits.maxFragmentInputComponents / 4);
default:
ERR_FAIL_V(0);
}