Fix glGet overflows by using 64 bit versions

Certain glGet operations require 64 bit versions according to the GLES spec.
The previous code was susceptible to overflow bugs,  especially running under ANGLE.
This commit is contained in:
lawnjelly 2023-10-09 08:12:04 +01:00
parent fe7ed984b5
commit 6ff370030a
7 changed files with 52 additions and 10 deletions

View file

@ -134,6 +134,15 @@ void RasterizerStorageGLES2::GLWrapper::reset() {
texture_unit_table.blank(); texture_unit_table.blank();
} }
int32_t RasterizerStorageGLES2::safe_gl_get_integer(unsigned int p_gl_param_name, int32_t p_max_accepted) {
// There is no glGetInteger64v in the base GLES2 spec as far as I can see.
// So we will just have a capped 32 bit version for GLES2.
int32_t temp;
glGetIntegerv(p_gl_param_name, &temp);
temp = MIN(temp, p_max_accepted);
return temp;
}
void RasterizerStorageGLES2::bind_quad_array() const { void RasterizerStorageGLES2::bind_quad_array() const {
glBindBuffer(GL_ARRAY_BUFFER, resources.quadie); glBindBuffer(GL_ARRAY_BUFFER, resources.quadie);
glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr);
@ -6316,7 +6325,7 @@ void RasterizerStorageGLES2::initialize() {
config.depth_type = GL_UNSIGNED_INT; config.depth_type = GL_UNSIGNED_INT;
// Initialize GLWrapper early on, as required for any calls to glActiveTexture. // Initialize GLWrapper early on, as required for any calls to glActiveTexture.
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units); config.max_texture_image_units = safe_gl_get_integer(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, Config::max_desired_texture_image_units);
gl_wrapper.initialize(config.max_texture_image_units); gl_wrapper.initialize(config.max_texture_image_units);
#ifdef GLES_OVER_GL #ifdef GLES_OVER_GL

View file

@ -65,6 +65,7 @@ public:
int max_vertex_texture_image_units; int max_vertex_texture_image_units;
int max_texture_image_units; int max_texture_image_units;
static const int32_t max_desired_texture_image_units = 64;
int max_texture_size; int max_texture_size;
int max_cubemap_texture_size; int max_cubemap_texture_size;
int max_viewport_dimensions[2]; int max_viewport_dimensions[2];
@ -1390,6 +1391,8 @@ public:
virtual String get_video_adapter_name() const; virtual String get_video_adapter_name() const;
virtual String get_video_adapter_vendor() const; virtual String get_video_adapter_vendor() const;
static int32_t safe_gl_get_integer(unsigned int p_gl_param_name, int32_t p_max_accepted = INT32_MAX);
// NOTE : THESE SIZES ARE IN BYTES. BUFFER SIZES MAY NOT BE SPECIFIED IN BYTES SO REMEMBER TO CONVERT THEM WHEN CALLING. // NOTE : THESE SIZES ARE IN BYTES. BUFFER SIZES MAY NOT BE SPECIFIED IN BYTES SO REMEMBER TO CONVERT THEM WHEN CALLING.
void buffer_orphan_and_upload(unsigned int p_buffer_size_bytes, unsigned int p_offset_bytes, unsigned int p_data_size_bytes, const void *p_data, GLenum p_target = GL_ARRAY_BUFFER, GLenum p_usage = GL_DYNAMIC_DRAW, bool p_optional_orphan = false) const; void buffer_orphan_and_upload(unsigned int p_buffer_size_bytes, unsigned int p_offset_bytes, unsigned int p_data_size_bytes, const void *p_data, GLenum p_target = GL_ARRAY_BUFFER, GLenum p_usage = GL_DYNAMIC_DRAW, bool p_optional_orphan = false) const;
bool safe_buffer_sub_data(unsigned int p_total_buffer_size, GLenum p_target, unsigned int p_offset, unsigned int p_data_size, const void *p_data, unsigned int &r_offset_after) const; bool safe_buffer_sub_data(unsigned int p_total_buffer_size, GLenum p_target, unsigned int p_offset, unsigned int p_data_size, const void *p_data, unsigned int &r_offset_after) const;

View file

@ -558,7 +558,8 @@ void ShaderGLES2::setup(
} }
} }
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_image_units); // The upper limit must match the version used in storage.
max_image_units = RasterizerStorageGLES2::safe_gl_get_integer(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, RasterizerStorageGLES2::Config::max_desired_texture_image_units);
} }
void ShaderGLES2::finish() { void ShaderGLES2::finish() {

View file

@ -5152,8 +5152,22 @@ void RasterizerSceneGLES3::initialize() {
{ {
//spot and omni ubos //spot and omni ubos
int max_ubo_size; // SPECIAL CASE for GL_MAX_UNIFORM_BLOCK_SIZE.
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_ubo_size); // Under ANGLE, in some situations this will return INT32_MAX + 1
// (or very high values).
// This seems to be because ANGLE supports system memory backing,
// but the true hardware GPU max may be lower.
// Afaik we have no way of querying this hardware supported value.
// We also ideally want to take advantage of GPUs that *do* support large uniform
// blocks in hardware (although this is probably not taken advantage of in Godot 3.x currently).
// This logic is thus a compromise.
int max_ubo_size = RasterizerStorageGLES3::safe_gl_get_integer(GL_MAX_UNIFORM_BLOCK_SIZE);
// Some maximum we are likely to currently need, currently 1 meg.
// Not really necessary but provides a small guard against excessive sizes.
max_ubo_size = MIN(max_ubo_size, 1024 * 1024);
const int ubo_light_size = 160; const int ubo_light_size = 160;
state.ubo_light_size = ubo_light_size; state.ubo_light_size = ubo_light_size;
state.max_ubo_lights = MIN(render_list.max_lights, max_ubo_size / ubo_light_size); state.max_ubo_lights = MIN(render_list.max_lights, max_ubo_size / ubo_light_size);

View file

@ -152,6 +152,13 @@ void RasterizerStorageGLES3::GLWrapper::reset() {
texture_unit_table.blank(); texture_unit_table.blank();
} }
int32_t RasterizerStorageGLES3::safe_gl_get_integer(unsigned int p_gl_param_name, int32_t p_max_accepted) {
int64_t temp;
glGetInteger64v(p_gl_param_name, &temp);
temp = MIN(temp, (int64_t)p_max_accepted);
return temp;
}
Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool &r_srgb, bool p_force_decompress) const { Ref<Image> RasterizerStorageGLES3::_get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, uint32_t p_flags, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool &r_srgb, bool p_force_decompress) const {
r_compressed = false; r_compressed = false;
r_gl_format = 0; r_gl_format = 0;
@ -8143,8 +8150,7 @@ void RasterizerStorageGLES3::initialize() {
/// ///
{ {
int max_extensions = 0; int max_extensions = safe_gl_get_integer(GL_NUM_EXTENSIONS);
glGetIntegerv(GL_NUM_EXTENSIONS, &max_extensions);
for (int i = 0; i < max_extensions; i++) { for (int i = 0; i < max_extensions; i++) {
const GLubyte *s = glGetStringi(GL_EXTENSIONS, i); const GLubyte *s = glGetStringi(GL_EXTENSIONS, i);
if (!s) { if (!s) {
@ -8157,8 +8163,12 @@ void RasterizerStorageGLES3::initialize() {
config.shrink_textures_x2 = false; config.shrink_textures_x2 = false;
config.use_fast_texture_filter = int(ProjectSettings::get_singleton()->get("rendering/quality/filters/use_nearest_mipmap_filter")); config.use_fast_texture_filter = int(ProjectSettings::get_singleton()->get("rendering/quality/filters/use_nearest_mipmap_filter"));
// Cap max_texture_image_units as we don't need large numbers of units,
// just in case an implementation provides a large number, as we want to keep
// the table in gl_wrapper small.
config.max_texture_image_units = safe_gl_get_integer(GL_MAX_TEXTURE_IMAGE_UNITS, Config::max_desired_texture_image_units);
// Initialize GLWrapper early on, as required for any calls to glActiveTexture. // Initialize GLWrapper early on, as required for any calls to glActiveTexture.
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &config.max_texture_image_units);
gl_wrapper.initialize(config.max_texture_image_units); gl_wrapper.initialize(config.max_texture_image_units);
config.etc_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture"); config.etc_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture");
@ -8392,8 +8402,8 @@ void RasterizerStorageGLES3::initialize() {
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &config.max_texture_size); config.max_texture_size = safe_gl_get_integer(GL_MAX_TEXTURE_SIZE);
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &config.max_cubemap_texture_size); config.max_cubemap_texture_size = safe_gl_get_integer(GL_MAX_CUBE_MAP_TEXTURE_SIZE);
config.use_rgba_2d_shadows = !config.framebuffer_float_supported; config.use_rgba_2d_shadows = !config.framebuffer_float_supported;

View file

@ -97,6 +97,7 @@ public:
float anisotropic_level; float anisotropic_level;
int max_texture_image_units; int max_texture_image_units;
static const int32_t max_desired_texture_image_units = 64;
int max_texture_size; int max_texture_size;
int max_cubemap_texture_size; int max_cubemap_texture_size;
@ -1530,6 +1531,8 @@ public:
void initialize(); void initialize();
void finalize(); void finalize();
static int32_t safe_gl_get_integer(unsigned int p_gl_param_name, int32_t p_max_accepted = INT32_MAX);
virtual bool has_os_feature(const String &p_feature) const; virtual bool has_os_feature(const String &p_feature) const;
virtual void update_dirty_resources(); virtual void update_dirty_resources();

View file

@ -34,6 +34,7 @@
#include "core/os/os.h" #include "core/os/os.h"
#include "core/print_string.h" #include "core/print_string.h"
#include "core/threaded_callable_queue.h" #include "core/threaded_callable_queue.h"
#include "drivers/gles3/rasterizer_storage_gles3.h"
#include "drivers/gles3/shader_cache_gles3.h" #include "drivers/gles3/shader_cache_gles3.h"
#include "servers/visual_server.h" #include "servers/visual_server.h"
@ -1216,7 +1217,8 @@ void ShaderGLES3::setup(const char **p_conditional_defines, int p_conditional_co
} }
} }
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_image_units); // The upper limit must match the version used in storage.
max_image_units = RasterizerStorageGLES3::safe_gl_get_integer(GL_MAX_TEXTURE_IMAGE_UNITS, RasterizerStorageGLES3::Config::max_desired_texture_image_units);
} }
void ShaderGLES3::init_async_compilation() { void ShaderGLES3::init_async_compilation() {