Browse Source

Add more validation to UBO size and alignment in Compatibility renderer

clayjohn 1 year ago
parent
commit
ea4be9afa6

+ 2 - 0
doc/classes/ProjectSettings.xml

@@ -2634,6 +2634,8 @@
 			[b]Note:[/b] This setting is only effective when using the Forward+ rendering method, not Mobile and Compatibility.
 			[b]Note:[/b] This setting is only effective when using the Forward+ rendering method, not Mobile and Compatibility.
 		</member>
 		</member>
 		<member name="rendering/limits/global_shader_variables/buffer_size" type="int" setter="" getter="" default="65536">
 		<member name="rendering/limits/global_shader_variables/buffer_size" type="int" setter="" getter="" default="65536">
+			The maximum number of uniforms that can be used by the global shader uniform buffer. Each item takes up one slot. In other words, a single uniform float and a uniform vec4 will take the same amount of space in the buffer.
+			[b]Note:[/b] When using the Compatibility backend, most mobile devices (and all web exports) will be limited to a maximum size of 1024 due to hardware constraints.
 		</member>
 		</member>
 		<member name="rendering/limits/opengl/max_lights_per_object" type="int" setter="" getter="" default="8">
 		<member name="rendering/limits/opengl/max_lights_per_object" type="int" setter="" getter="" default="8">
 			Max number of omnilights and spotlights renderable per object. At the default value of 8, this means that each surface can be affected by up to 8 omnilights and 8 spotlights. This is further limited by hardware support and [member rendering/limits/opengl/max_renderable_lights]. Setting this low will slightly reduce memory usage, may decrease shader compile times, and may result in faster rendering on low-end, mobile, or web devices.
 			Max number of omnilights and spotlights renderable per object. At the default value of 8, this means that each surface can be affected by up to 8 omnilights and 8 spotlights. This is further limited by hardware support and [member rendering/limits/opengl/max_renderable_lights]. Setting this low will slightly reduce memory usage, may decrease shader compile times, and may result in faster rendering on low-end, mobile, or web devices.

+ 6 - 0
drivers/gles3/rasterizer_canvas_gles3.h

@@ -157,6 +157,8 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender {
 		float atlas_rect[4];
 		float atlas_rect[4];
 	};
 	};
 
 
+	static_assert(sizeof(LightUniform) % 16 == 0, "2D light UBO size must be a multiple of 16 bytes");
+
 public:
 public:
 	enum {
 	enum {
 		BASE_UNIFORM_LOCATION = 0,
 		BASE_UNIFORM_LOCATION = 0,
@@ -186,6 +188,8 @@ public:
 		uint32_t pad2;
 		uint32_t pad2;
 	};
 	};
 
 
+	static_assert(sizeof(StateBuffer) % 16 == 0, "2D state UBO size must be a multiple of 16 bytes");
+
 	struct PolygonBuffers {
 	struct PolygonBuffers {
 		GLuint vertex_buffer = 0;
 		GLuint vertex_buffer = 0;
 		GLuint vertex_array = 0;
 		GLuint vertex_array = 0;
@@ -230,6 +234,8 @@ public:
 		uint32_t lights[4];
 		uint32_t lights[4];
 	};
 	};
 
 
+	static_assert(sizeof(InstanceData) == 128, "2D instance data struct size must be 128 bytes");
+
 	struct Data {
 	struct Data {
 		GLuint canvas_quad_vertices;
 		GLuint canvas_quad_vertices;
 		GLuint canvas_quad_array;
 		GLuint canvas_quad_array;

+ 2 - 0
drivers/gles3/rasterizer_scene_gles3.h

@@ -428,6 +428,7 @@ private:
 			bool pancake_shadows;
 			bool pancake_shadows;
 		};
 		};
 		static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes");
 		static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes");
+		static_assert(sizeof(UBO) < 16384, "Scene UBO size must be 16384 bytes or smaller");
 
 
 		struct MultiviewUBO {
 		struct MultiviewUBO {
 			float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
 			float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16];
@@ -435,6 +436,7 @@ private:
 			float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4];
 			float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4];
 		};
 		};
 		static_assert(sizeof(MultiviewUBO) % 16 == 0, "Multiview UBO size must be a multiple of 16 bytes");
 		static_assert(sizeof(MultiviewUBO) % 16 == 0, "Multiview UBO size must be a multiple of 16 bytes");
+		static_assert(sizeof(MultiviewUBO) < 16384, "MultiviewUBO size must be 16384 bytes or smaller");
 
 
 		struct TonemapUBO {
 		struct TonemapUBO {
 			float exposure = 1.0;
 			float exposure = 1.0;

+ 5 - 4
drivers/gles3/storage/material_storage.cpp

@@ -1055,6 +1055,7 @@ void MaterialData::update_parameters_internal(const HashMap<StringName, Variant>
 
 
 		ubo_data.resize(p_ubo_size);
 		ubo_data.resize(p_ubo_size);
 		if (ubo_data.size()) {
 		if (ubo_data.size()) {
+			ERR_FAIL_COND(p_ubo_size > uint32_t(Config::get_singleton()->max_uniform_buffer_size));
 			memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear
 			memset(ubo_data.ptrw(), 0, ubo_data.size()); //clear
 		}
 		}
 	}
 	}
@@ -1108,10 +1109,10 @@ MaterialStorage::MaterialStorage() {
 
 
 	static_assert(sizeof(GlobalShaderUniforms::Value) == 16);
 	static_assert(sizeof(GlobalShaderUniforms::Value) == 16);
 
 
-	global_shader_uniforms.buffer_size = MAX(4096, (int)GLOBAL_GET("rendering/limits/global_shader_variables/buffer_size"));
-	if (global_shader_uniforms.buffer_size > uint32_t(Config::get_singleton()->max_uniform_buffer_size)) {
-		global_shader_uniforms.buffer_size = uint32_t(Config::get_singleton()->max_uniform_buffer_size);
-		WARN_PRINT("Project setting \"rendering/limits/global_shader_variables/buffer_size\" exceeds maximum uniform buffer size of: " + itos(Config::get_singleton()->max_uniform_buffer_size));
+	global_shader_uniforms.buffer_size = MAX(16, (int)GLOBAL_GET("rendering/limits/global_shader_variables/buffer_size"));
+	if (global_shader_uniforms.buffer_size * sizeof(GlobalShaderUniforms::Value) > uint32_t(Config::get_singleton()->max_uniform_buffer_size)) {
+		global_shader_uniforms.buffer_size = uint32_t(Config::get_singleton()->max_uniform_buffer_size) / sizeof(GlobalShaderUniforms::Value);
+		WARN_PRINT("Project setting \"rendering/limits/global_shader_variables/buffer_size\" exceeds maximum uniform buffer size of: " + itos(Config::get_singleton()->max_uniform_buffer_size / sizeof(GlobalShaderUniforms::Value)) + ". Falling back on maximum buffer size.");
 	}
 	}
 
 
 	global_shader_uniforms.buffer_values = memnew_arr(GlobalShaderUniforms::Value, global_shader_uniforms.buffer_size);
 	global_shader_uniforms.buffer_values = memnew_arr(GlobalShaderUniforms::Value, global_shader_uniforms.buffer_size);

+ 3 - 0
drivers/gles3/storage/particles_storage.h

@@ -145,6 +145,9 @@ private:
 		Collider colliders[MAX_COLLIDERS];
 		Collider colliders[MAX_COLLIDERS];
 	};
 	};
 
 
+	static_assert(sizeof(ParticlesFrameParams) % 16 == 0, "ParticlesFrameParams size must be a multiple of 16 bytes");
+	static_assert(sizeof(ParticlesFrameParams) < 16384, "ParticlesFrameParams must be 16384 bytes or smaller");
+
 	struct Particles {
 	struct Particles {
 		RS::ParticlesMode mode = RS::PARTICLES_MODE_3D;
 		RS::ParticlesMode mode = RS::PARTICLES_MODE_3D;
 		bool inactive = true;
 		bool inactive = true;

+ 1 - 1
servers/rendering_server.cpp

@@ -3610,7 +3610,7 @@ void RenderingServer::init() {
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/environment/subsurface_scattering/subsurface_scattering_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001"), 0.05);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/environment/subsurface_scattering/subsurface_scattering_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001"), 0.05);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001"), 0.01);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/environment/subsurface_scattering/subsurface_scattering_depth_scale", PROPERTY_HINT_RANGE, "0.001,1,0.001"), 0.01);
 
 
-	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/limits/global_shader_variables/buffer_size", PROPERTY_HINT_RANGE, "1,1048576,1"), 65536);
+	GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/limits/global_shader_variables/buffer_size", PROPERTY_HINT_RANGE, "16,1048576,1"), 65536);
 
 
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/probe_capture/update_speed", PROPERTY_HINT_RANGE, "0.001,256,0.001"), 15);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/probe_capture/update_speed", PROPERTY_HINT_RANGE, "0.001,256,0.001"), 15);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/primitive_meshes/texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2);
 	GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/primitive_meshes/texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2);