Skip to content

Commit 679d63b

Browse files
committed
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.
1 parent 296de7d commit 679d63b

17 files changed

+94
-29
lines changed

drivers/d3d12/rendering_device_driver_d3d12.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -3236,7 +3236,7 @@ Vector<uint8_t> RenderingDeviceDriverD3D12::shader_compile_binary_from_spirv(Vec
32363236
DEV_ASSERT(binding_info.res_class == (uint32_t)RES_CLASS_INVALID || binding_info.res_class == (uint32_t)res_class);
32373237
binding_info.res_class = res_class;
32383238
} else if (p_dxil_type == DXIL_RES_SAMPLER) {
3239-
binding_info.has_sampler = (uint32_t)true;
3239+
binding_info.has_sampler = (uint32_t) true;
32403240
} else {
32413241
CRASH_NOW();
32423242
}
@@ -6234,6 +6234,8 @@ uint64_t RenderingDeviceDriverD3D12::limit_get(Limit p_limit) {
62346234
case LIMIT_VRS_MAX_FRAGMENT_WIDTH:
62356235
case LIMIT_VRS_MAX_FRAGMENT_HEIGHT:
62366236
return vrs_capabilities.ss_max_fragment_size;
6237+
case LIMIT_MAX_SHADER_VARYINGS:
6238+
return MIN(D3D12_VS_OUTPUT_REGISTER_COUNT, D3D12_PS_INPUT_REGISTER_COUNT);
62376239
default: {
62386240
#ifdef DEV_ENABLED
62396241
WARN_PRINT("Returning maximum value for unknown limit " + itos(p_limit) + ".");

drivers/gles3/storage/config.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ Config::Config() {
110110
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
111111
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport_size);
112112
glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
113+
GLint max_vertex_output;
114+
glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &max_vertex_output);
115+
GLint max_fragment_input;
116+
glGetIntegerv(GL_MAX_FRAGMENT_INPUT_COMPONENTS, &max_fragment_input);
117+
max_shader_varyings = (uint32_t)MIN(max_vertex_output, max_fragment_input) / 4;
113118

114119
// sanity clamp buffer size to 16K..1MB
115120
max_uniform_buffer_size = CLAMP(max_uniform_buffer_size, 16384, 1048576);

drivers/gles3/storage/config.h

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class Config {
6262
GLint max_texture_size = 0;
6363
GLint max_viewport_size[2] = { 0, 0 };
6464
GLint64 max_uniform_buffer_size = 0;
65+
uint32_t max_shader_varyings = 0;
6566

6667
int64_t max_renderable_elements = 0;
6768
int64_t max_renderable_lights = 0;

drivers/gles3/storage/utilities.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -465,4 +465,16 @@ Size2i Utilities::get_maximum_viewport_size() const {
465465
return Size2i(config->max_viewport_size[0], config->max_viewport_size[1]);
466466
}
467467

468+
uint32_t Utilities::get_maximum_shader_varyings() const {
469+
Config *config = Config::get_singleton();
470+
ERR_FAIL_NULL_V(config, 31);
471+
return config->max_shader_varyings;
472+
}
473+
474+
uint64_t Utilities::get_maximum_uniform_buffer_size() const {
475+
Config *config = Config::get_singleton();
476+
ERR_FAIL_NULL_V(config, 65536);
477+
return uint64_t(config->max_uniform_buffer_size);
478+
}
479+
468480
#endif // GLES3_ENABLED

drivers/gles3/storage/utilities.h

+2
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ class Utilities : public RendererUtilities {
226226
virtual String get_video_adapter_api_version() const override;
227227

228228
virtual Size2i get_maximum_viewport_size() const override;
229+
virtual uint32_t get_maximum_shader_varyings() const override;
230+
virtual uint64_t get_maximum_uniform_buffer_size() const override;
229231
};
230232

231233
} // namespace GLES3

drivers/metal/metal_device_properties.h

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ struct MetalLimits {
124124
uint32_t maxVertexInputBindings;
125125
uint32_t maxVertexInputBindingStride;
126126
uint32_t maxDrawIndexedIndexValue;
127+
uint32_t maxShaderVaryings;
127128

128129
double temporalScalerInputContentMinScale;
129130
double temporalScalerInputContentMaxScale;

drivers/metal/metal_device_properties.mm

+1
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@
303303
limits.maxVertexInputAttributes = 31;
304304
limits.maxVertexInputBindings = 31;
305305
limits.maxVertexInputBindingStride = (2 * KIBI);
306+
limits.maxShaderVaryings = 31; // Accurate on Apple4 and above. See: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
306307

307308
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
308309
limits.minUniformBufferOffsetAlignment = 64;

drivers/metal/rendering_device_driver_metal.mm

+2
Original file line numberDiff line numberDiff line change
@@ -3999,6 +3999,8 @@ bool isArrayTexture(MTLTextureType p_type) {
39993999
return (uint64_t)((1.0 / limits.temporalScalerInputContentMaxScale) * 1000'000);
40004000
case LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE:
40014001
return (uint64_t)((1.0 / limits.temporalScalerInputContentMinScale) * 1000'000);
4002+
case LIMIT_MAX_SHADER_VARYINGS:
4003+
return limits.maxShaderVaryings;
40024004
UNKNOWN(LIMIT_VRS_TEXEL_WIDTH);
40034005
UNKNOWN(LIMIT_VRS_TEXEL_HEIGHT);
40044006
UNKNOWN(LIMIT_VRS_MAX_FRAGMENT_WIDTH);

drivers/vulkan/rendering_device_driver_vulkan.cpp

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

servers/rendering/dummy/storage/utilities.h

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class Utilities : public RendererUtilities {
9494
virtual String get_video_adapter_api_version() const override { return String(); }
9595

9696
virtual Size2i get_maximum_viewport_size() const override { return Size2i(); }
97+
virtual uint32_t get_maximum_shader_varyings() const override { return 31; } // Fair assumption for everything except old OpenGL-only phones.
98+
virtual uint64_t get_maximum_uniform_buffer_size() const override { return 65536; } // Fair assumption for all devices.
9799
};
98100

99101
} // namespace RendererDummy

servers/rendering/renderer_rd/storage_rd/utilities.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,11 @@ Size2i Utilities::get_maximum_viewport_size() const {
329329
int max_y = device->limit_get(RenderingDevice::LIMIT_MAX_VIEWPORT_DIMENSIONS_Y);
330330
return Size2i(max_x, max_y);
331331
}
332+
333+
uint32_t Utilities::get_maximum_shader_varyings() const {
334+
return RenderingDevice::get_singleton()->limit_get(RenderingDevice::LIMIT_MAX_SHADER_VARYINGS);
335+
}
336+
337+
uint64_t Utilities::get_maximum_uniform_buffer_size() const {
338+
return RenderingDevice::get_singleton()->limit_get(RenderingDevice::LIMIT_MAX_UNIFORM_BUFFER_SIZE);
339+
}

servers/rendering/renderer_rd/storage_rd/utilities.h

+2
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ class Utilities : public RendererUtilities {
117117
virtual String get_video_adapter_api_version() const override;
118118

119119
virtual Size2i get_maximum_viewport_size() const override;
120+
virtual uint32_t get_maximum_shader_varyings() const override;
121+
virtual uint64_t get_maximum_uniform_buffer_size() const override;
120122
};
121123

122124
} // namespace RendererRD

servers/rendering/rendering_device_commons.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
#include "core/object/object.h"
3535
#include "core/variant/type_info.h"
3636

37-
#define STEPIFY(m_number, m_alignment) ((((m_number) + ((m_alignment) - 1)) / (m_alignment)) * (m_alignment))
37+
#define STEPIFY(m_number, m_alignment) ((((m_number) + ((m_alignment)-1)) / (m_alignment)) * (m_alignment))
3838

3939
class RenderingDeviceCommons : public Object {
4040
////////////////////////////////////////////
@@ -875,6 +875,7 @@ class RenderingDeviceCommons : public Object {
875875
LIMIT_VRS_MAX_FRAGMENT_HEIGHT,
876876
LIMIT_METALFX_TEMPORAL_SCALER_MIN_SCALE,
877877
LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE,
878+
LIMIT_MAX_SHADER_VARYINGS,
878879
};
879880

880881
enum Features {

servers/rendering/shader_compiler.cpp

+2-17
Original file line numberDiff line numberDiff line change
@@ -686,30 +686,14 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
686686
vcode += _prestr(varying.precision, ShaderLanguage::is_float_type(varying.type));
687687
vcode += _typestr(varying.type);
688688
vcode += " " + _mkid(varying_name);
689-
uint32_t inc = 1U;
689+
uint32_t inc = varying.get_size();
690690

691691
if (varying.array_size > 0) {
692-
inc = (uint32_t)varying.array_size;
693-
694692
vcode += "[";
695693
vcode += itos(varying.array_size);
696694
vcode += "]";
697695
}
698696

699-
switch (varying.type) {
700-
case SL::TYPE_MAT2:
701-
inc *= 2U;
702-
break;
703-
case SL::TYPE_MAT3:
704-
inc *= 3U;
705-
break;
706-
case SL::TYPE_MAT4:
707-
inc *= 4U;
708-
break;
709-
default:
710-
break;
711-
}
712-
713697
vcode += ";\n";
714698
// GLSL ES 3.0 does not allow layout qualifiers for varyings
715699
if (!RS::get_singleton()->is_low_end()) {
@@ -1481,6 +1465,7 @@ Error ShaderCompiler::compile(RS::ShaderMode p_mode, const String &p_code, Ident
14811465
info.render_modes = ShaderTypes::get_singleton()->get_modes(p_mode);
14821466
info.shader_types = ShaderTypes::get_singleton()->get_types();
14831467
info.global_shader_uniform_type_func = _get_global_shader_uniform_type;
1468+
info.base_varying_index = actions.base_varying_index;
14841469

14851470
Error err = parser.compile(p_code, info);
14861471

servers/rendering/shader_language.cpp

+21-10
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "core/os/os.h"
3434
#include "core/templates/local_vector.h"
3535
#include "servers/rendering/renderer_compositor.h"
36+
#include "servers/rendering/rendering_server_globals.h"
3637
#include "servers/rendering_server.h"
3738
#include "shader_types.h"
3839

@@ -9111,17 +9112,12 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
91119112
int prop_index = 0;
91129113
#ifdef DEBUG_ENABLED
91139114
uint64_t uniform_buffer_size = 0;
9114-
uint64_t max_uniform_buffer_size = 0;
9115+
uint64_t max_uniform_buffer_size = 65536;
91159116
int uniform_buffer_exceeded_line = -1;
9116-
9117-
bool check_device_limit_warnings = false;
9118-
{
9119-
RenderingDevice *device = RenderingDevice::get_singleton();
9120-
if (device != nullptr) {
9121-
check_device_limit_warnings = check_warnings && HAS_WARNING(ShaderWarning::DEVICE_LIMIT_EXCEEDED_FLAG);
9122-
9123-
max_uniform_buffer_size = device->limit_get(RenderingDevice::LIMIT_MAX_UNIFORM_BUFFER_SIZE);
9124-
}
9117+
bool check_device_limit_warnings = check_warnings && HAS_WARNING(ShaderWarning::DEVICE_LIMIT_EXCEEDED_FLAG);
9118+
// Can be false for internal shaders created in the process of initializing the engine.
9119+
if (RSG::utilities) {
9120+
max_uniform_buffer_size = RSG::utilities->get_maximum_uniform_buffer_size();
91259121
}
91269122
#endif // DEBUG_ENABLED
91279123
ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL;
@@ -10959,13 +10955,27 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
1095910955

1096010956
tk = _get_token();
1096110957
}
10958+
uint32_t varying_index = base_varying_index;
10959+
uint32_t max_varyings = 31;
10960+
// Can be false for internal shaders created in the process of initializing the engine.
10961+
if (RSG::utilities) {
10962+
max_varyings = RSG::utilities->get_maximum_shader_varyings();
10963+
}
1096210964

1096310965
for (const KeyValue<StringName, ShaderNode::Varying> &kv : shader->varyings) {
1096410966
if (kv.value.stage != ShaderNode::Varying::STAGE_FRAGMENT && (kv.value.type > TYPE_BVEC4 && kv.value.type < TYPE_FLOAT) && kv.value.interpolation != INTERPOLATION_FLAT) {
1096510967
_set_tkpos(kv.value.tkpos);
1096610968
_set_error(vformat(RTR("Varying with integer data type must be declared with `%s` interpolation qualifier."), "flat"));
1096710969
return ERR_PARSE_ERROR;
1096810970
}
10971+
10972+
if (varying_index + kv.value.get_size() > max_varyings) {
10973+
_set_tkpos(kv.value.tkpos);
10974+
_set_error(vformat(RTR("Too many varyings used in shader. %d used. Maximum supported is %d"), varying_index + kv.value.get_size(), max_varyings));
10975+
return ERR_PARSE_ERROR;
10976+
}
10977+
10978+
varying_index += kv.value.get_size();
1096910979
}
1097010980

1097110981
#ifdef DEBUG_ENABLED
@@ -11183,6 +11193,7 @@ Error ShaderLanguage::compile(const String &p_code, const ShaderCompileInfo &p_i
1118311193
global_shader_uniform_get_type_func = p_info.global_shader_uniform_type_func;
1118411194

1118511195
varying_function_names = p_info.varying_function_names;
11196+
base_varying_index = p_info.base_varying_index;
1118611197

1118711198
nodes = nullptr;
1118811199

servers/rendering/shader_language.h

+24
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,28 @@ class ShaderLanguage {
631631
int array_size = 0;
632632
TkPos tkpos;
633633

634+
uint32_t get_size() const {
635+
uint32_t size = 1;
636+
if (array_size > 0) {
637+
size = (uint32_t)array_size;
638+
}
639+
640+
switch (type) {
641+
case TYPE_MAT2:
642+
size *= 2;
643+
break;
644+
case TYPE_MAT3:
645+
size *= 3;
646+
break;
647+
case TYPE_MAT4:
648+
size *= 4;
649+
break;
650+
default:
651+
break;
652+
}
653+
return size;
654+
}
655+
634656
Varying() {}
635657
};
636658

@@ -1022,6 +1044,7 @@ class ShaderLanguage {
10221044
String current_uniform_subgroup_name;
10231045

10241046
VaryingFunctionNames varying_function_names;
1047+
uint32_t base_varying_index = 0;
10251048

10261049
TkPos _get_tkpos() {
10271050
TkPos tkp;
@@ -1217,6 +1240,7 @@ class ShaderLanguage {
12171240
HashSet<String> shader_types;
12181241
GlobalShaderUniformGetTypeFunc global_shader_uniform_type_func = nullptr;
12191242
bool is_include = false;
1243+
uint32_t base_varying_index = 0;
12201244
};
12211245

12221246
Error compile(const String &p_code, const ShaderCompileInfo &p_info);

servers/rendering/storage/utilities.h

+2
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ class RendererUtilities {
184184
virtual String get_video_adapter_api_version() const = 0;
185185

186186
virtual Size2i get_maximum_viewport_size() const = 0;
187+
virtual uint32_t get_maximum_shader_varyings() const = 0;
188+
virtual uint64_t get_maximum_uniform_buffer_size() const = 0;
187189
};
188190

189191
#endif // RENDERER_UTILITIES_H

0 commit comments

Comments
 (0)