From 3c69377f10a8fda7d75eabeba3b74d101eaa0f46 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Sun, 22 Nov 2020 11:24:29 +0000 Subject: [PATCH] 2d Legacy - close vulnerabilities and more debug checks While adding more debug checks to legacy renderer, I closed 2 types of vulnerabilities: * TYPE_PRIMITIVE would previously read from uninitialized data if only specifying a single color * Other legacy draw operations would fail in debug AFTER accessing out of bounds memory rather than before Many calls to glBufferSubData are wrapped in a safe version which checks for out of bounds and exits the draw function if this is detected. --- .../gles2/rasterizer_canvas_base_gles2.cpp | 74 +++++++++++---- drivers/gles2/rasterizer_canvas_gles2.cpp | 16 +++- drivers/gles2/rasterizer_storage_gles2.h | 32 ++++++- .../gles3/rasterizer_canvas_base_gles3.cpp | 94 +++++++++---------- drivers/gles3/rasterizer_canvas_gles3.cpp | 23 ++++- drivers/gles3/rasterizer_storage_gles3.h | 32 ++++++- drivers/gles_common/rasterizer_asserts.h | 9 ++ 7 files changed, 203 insertions(+), 77 deletions(-) diff --git a/drivers/gles2/rasterizer_canvas_base_gles2.cpp b/drivers/gles2/rasterizer_canvas_base_gles2.cpp index 5a90ffc84f6..edd2571f7cf 100644 --- a/drivers/gles2/rasterizer_canvas_base_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_base_gles2.cpp @@ -32,6 +32,7 @@ #include "core/os/os.h" #include "core/project_settings.h" +#include "drivers/gles_common/rasterizer_asserts.h" #include "rasterizer_scene_gles2.h" #include "servers/visual/visual_server_raster.h" @@ -447,11 +448,16 @@ void RasterizerCanvasBaseGLES2::_draw_polygon(const int *p_indices, int p_index_ glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); uint32_t buffer_ofs = 0; + uint32_t buffer_ofs_after = buffer_ofs + (sizeof(Vector2) * p_vertex_count); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs_after > data.polygon_buffer_size); +#endif + storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices, GL_ARRAY_BUFFER, _buffer_upload_usage_flag, true); glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL); - buffer_ofs += sizeof(Vector2) * p_vertex_count; + buffer_ofs = buffer_ofs_after; if (p_singlecolor) { glDisableVertexAttribArray(VS::ARRAY_COLOR); @@ -461,31 +467,31 @@ void RasterizerCanvasBaseGLES2::_draw_polygon(const int *p_indices, int p_index_ glDisableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } else { - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Color) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } if (p_uvs) { - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_TEX_UV); glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Vector2) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } else { glDisableVertexAttribArray(VS::ARRAY_TEX_UV); } if (p_weights && p_bones) { - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_WEIGHTS); glVertexAttribPointer(VS::ARRAY_WEIGHTS, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(float) * 4 * p_vertex_count; + buffer_ofs = buffer_ofs_after; - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_BONES); glVertexAttribPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, GL_FALSE, sizeof(int) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(int) * 4 * p_vertex_count; + buffer_ofs = buffer_ofs_after; } else { glDisableVertexAttribArray(VS::ARRAY_WEIGHTS); @@ -495,10 +501,16 @@ void RasterizerCanvasBaseGLES2::_draw_polygon(const int *p_indices, int p_index_ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); if (storage->config.support_32_bits_indices) { //should check for +#ifdef DEBUG_ENABLED + ERR_FAIL_COND((sizeof(int) * p_index_count) > data.polygon_index_buffer_size); +#endif storage->buffer_orphan_and_upload(data.polygon_index_buffer_size, 0, sizeof(int) * p_index_count, p_indices, GL_ELEMENT_ARRAY_BUFFER, _buffer_upload_usage_flag, true); glDrawElements(GL_TRIANGLES, p_index_count, GL_UNSIGNED_INT, 0); storage->info.render._2d_draw_call_count++; } else { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND((sizeof(uint16_t) * p_index_count) > data.polygon_index_buffer_size); +#endif uint16_t *index16 = (uint16_t *)alloca(sizeof(uint16_t) * p_index_count); for (int i = 0; i < p_index_count; i++) { index16[i] = uint16_t(p_indices[i]); @@ -517,11 +529,15 @@ void RasterizerCanvasBaseGLES2::_draw_generic(GLuint p_primitive, int p_vertex_c glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); uint32_t buffer_ofs = 0; + uint32_t buffer_ofs_after = buffer_ofs + (sizeof(Vector2) * p_vertex_count); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs_after > data.polygon_buffer_size); +#endif storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices, GL_ARRAY_BUFFER, _buffer_upload_usage_flag, true); glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL); - buffer_ofs += sizeof(Vector2) * p_vertex_count; + buffer_ofs = buffer_ofs_after; if (p_singlecolor) { glDisableVertexAttribArray(VS::ARRAY_COLOR); @@ -531,16 +547,17 @@ void RasterizerCanvasBaseGLES2::_draw_generic(GLuint p_primitive, int p_vertex_c glDisableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } else { - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Color) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } if (p_uvs) { - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_TEX_UV); glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); + buffer_ofs = buffer_ofs_after; } else { glDisableVertexAttribArray(VS::ARRAY_TEX_UV); } @@ -556,11 +573,15 @@ void RasterizerCanvasBaseGLES2::_draw_generic_indices(GLuint p_primitive, const glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); uint32_t buffer_ofs = 0; + uint32_t buffer_ofs_after = buffer_ofs + (sizeof(Vector2) * p_vertex_count); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs_after > data.polygon_buffer_size); +#endif storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices, GL_ARRAY_BUFFER, _buffer_upload_usage_flag, true); glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL); - buffer_ofs += sizeof(Vector2) * p_vertex_count; + buffer_ofs = buffer_ofs_after; if (p_singlecolor) { glDisableVertexAttribArray(VS::ARRAY_COLOR); @@ -570,28 +591,41 @@ void RasterizerCanvasBaseGLES2::_draw_generic_indices(GLuint p_primitive, const glDisableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } else { - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Color) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } if (p_uvs) { - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_TEX_UV); glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Vector2) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } else { glDisableVertexAttribArray(VS::ARRAY_TEX_UV); } +#ifdef RASTERIZER_EXTRA_CHECKS + // very slow, do not enable in normal use + for (int n = 0; n < p_index_count; n++) { + RAST_DEV_DEBUG_ASSERT(p_indices[n] < p_vertex_count); + } +#endif + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); if (storage->config.support_32_bits_indices) { //should check for +#ifdef DEBUG_ENABLED + ERR_FAIL_COND((sizeof(int) * p_index_count) > data.polygon_index_buffer_size); +#endif storage->buffer_orphan_and_upload(data.polygon_index_buffer_size, 0, sizeof(int) * p_index_count, p_indices, GL_ELEMENT_ARRAY_BUFFER, _buffer_upload_usage_flag, true); glDrawElements(p_primitive, p_index_count, GL_UNSIGNED_INT, 0); storage->info.render._2d_draw_call_count++; } else { +#ifdef DEBUG_ENABLED + ERR_FAIL_COND((sizeof(uint16_t) * p_index_count) > data.polygon_index_buffer_size); +#endif uint16_t *index16 = (uint16_t *)alloca(sizeof(uint16_t) * p_index_count); for (int i = 0; i < p_index_count; i++) { index16[i] = uint16_t(p_indices[i]); @@ -629,6 +663,7 @@ void RasterizerCanvasBaseGLES2::_draw_gui_primitive(int p_points, const Vector2 stride += 1; } + RAST_DEV_DEBUG_ASSERT(p_points <= 4); float buffer_data[(2 + 2 + 4 + 1) * 4]; for (int i = 0; i < p_points; i++) { @@ -939,8 +974,8 @@ void RasterizerCanvasBaseGLES2::initialize() { { uint32_t poly_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128); ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); + poly_size = MAX(poly_size, 2); // minimum 2k, may still see anomalies in editor poly_size *= 1024; - poly_size = MAX(poly_size, (2 + 2 + 4) * 4 * sizeof(float)); glGenBuffers(1, &data.polygon_buffer); glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); glBufferData(GL_ARRAY_BUFFER, poly_size, NULL, GL_DYNAMIC_DRAW); @@ -951,6 +986,7 @@ void RasterizerCanvasBaseGLES2::initialize() { uint32_t index_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128); ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); + index_size = MAX(index_size, 2); index_size *= 1024; // kb glGenBuffers(1, &data.polygon_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 22ca8f85682..2e237b08797 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -32,6 +32,7 @@ #include "core/os/os.h" #include "core/project_settings.h" +#include "drivers/gles_common/rasterizer_asserts.h" #include "rasterizer_scene_gles2.h" #include "servers/visual/visual_server_raster.h" @@ -1122,14 +1123,27 @@ void RasterizerCanvasGLES2::render_batches(Item::Command *const *p_commands, Ite state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); } + // we need a temporary because this must be nulled out + // if only a single color specified + const Color *colors = primitive->colors.ptr(); if (primitive->colors.size() == 1 && primitive->points.size() > 1) { Color c = primitive->colors[0]; glVertexAttrib4f(VS::ARRAY_COLOR, c.r, c.g, c.b, c.a); + colors = nullptr; } else if (primitive->colors.empty()) { glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } +#ifdef RASTERIZER_EXTRA_CHECKS + else { + RAST_DEV_DEBUG_ASSERT(primitive->colors.size() == primitive->points.size()); + } - _draw_gui_primitive(primitive->points.size(), primitive->points.ptr(), primitive->colors.ptr(), primitive->uvs.ptr()); + if (primitive->uvs.ptr()) { + RAST_DEV_DEBUG_ASSERT(primitive->uvs.size() == primitive->points.size()); + } +#endif + + _draw_gui_primitive(primitive->points.size(), primitive->points.ptr(), colors, primitive->uvs.ptr()); } break; case Item::Command::TYPE_TRANSFORM: { diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h index 28be646dbbc..40bc65c7229 100644 --- a/drivers/gles2/rasterizer_storage_gles2.h +++ b/drivers/gles2/rasterizer_storage_gles2.h @@ -33,6 +33,7 @@ #include "core/pool_vector.h" #include "core/self_list.h" +#include "drivers/gles_common/rasterizer_asserts.h" #include "servers/visual/rasterizer.h" #include "servers/visual/shader_language.h" #include "shader_compiler_gles2.h" @@ -1343,19 +1344,46 @@ public: virtual String get_video_adapter_name() const; virtual String get_video_adapter_vendor() const; - void buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target = GL_ARRAY_BUFFER, GLenum p_usage = GL_DYNAMIC_DRAW, bool p_optional_orphan = false); + void buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, 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; RasterizerStorageGLES2(); }; +inline bool RasterizerStorageGLES2::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 { + r_offset_after = p_offset + p_data_size; +#ifdef DEBUG_ENABLED + // we are trying to write across the edge of the buffer + if (r_offset_after > p_total_buffer_size) + return false; +#endif + glBufferSubData(p_target, p_offset, p_data_size, p_data); + return true; +} + // standardize the orphan / upload in one place so it can be changed per platform as necessary, and avoid future // bugs causing pipeline stalls -inline void RasterizerStorageGLES2::buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target, GLenum p_usage, bool p_optional_orphan) { +inline void RasterizerStorageGLES2::buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target, GLenum p_usage, bool p_optional_orphan) const { // Orphan the buffer to avoid CPU/GPU sync points caused by glBufferSubData // Was previously #ifndef GLES_OVER_GL however this causes stalls on desktop mac also (and possibly other) if (!p_optional_orphan || (config.should_orphan)) { glBufferData(p_target, p_buffer_size, NULL, p_usage); +#ifdef RASTERIZER_EXTRA_CHECKS + // fill with garbage off the end of the array + if (p_buffer_size) { + unsigned int start = p_offset + p_data_size; + unsigned int end = start + 1024; + if (end < p_buffer_size) { + uint8_t *garbage = (uint8_t *)alloca(1024); + for (int n = 0; n < 1024; n++) { + garbage[n] = Math::random(0, 255); + } + glBufferSubData(p_target, start, 1024, garbage); + } + } +#endif } + RAST_DEV_DEBUG_ASSERT((p_offset + p_data_size) <= p_buffer_size); glBufferSubData(p_target, p_offset, p_data_size, p_data); } diff --git a/drivers/gles3/rasterizer_canvas_base_gles3.cpp b/drivers/gles3/rasterizer_canvas_base_gles3.cpp index af5b51d7431..6192c959943 100644 --- a/drivers/gles3/rasterizer_canvas_base_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_base_gles3.cpp @@ -350,16 +350,18 @@ void RasterizerCanvasBaseGLES3::_draw_polygon(const int *p_indices, int p_index_ glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); uint32_t buffer_ofs = 0; + uint32_t buffer_ofs_after = buffer_ofs + (sizeof(Vector2) * p_vertex_count); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs_after > data.polygon_buffer_size); +#endif + storage->buffer_orphan_and_upload(data.polygon_buffer_size, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_vertices, GL_ARRAY_BUFFER, _buffer_upload_usage_flag); glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, false, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Vector2) * p_vertex_count; - //color -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); -#endif + buffer_ofs = buffer_ofs_after; + //color if (p_singlecolor) { glDisableVertexAttribArray(VS::ARRAY_COLOR); Color m = *p_colors; @@ -368,44 +370,33 @@ void RasterizerCanvasBaseGLES3::_draw_polygon(const int *p_indices, int p_index_ glDisableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } else { - - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, false, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Color) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); -#endif - if (p_uvs) { - - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_TEX_UV); glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, false, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Vector2) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } else { glDisableVertexAttribArray(VS::ARRAY_TEX_UV); } -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); -#endif - if (p_bones && p_weights) { - - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_BONES); //glVertexAttribPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, false, sizeof(int) * 4, ((uint8_t *)0) + buffer_ofs); glVertexAttribIPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, sizeof(int) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(int) * 4 * p_vertex_count; + buffer_ofs = buffer_ofs_after; - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_WEIGHTS); glVertexAttribPointer(VS::ARRAY_WEIGHTS, 4, GL_FLOAT, false, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(float) * 4 * p_vertex_count; + buffer_ofs = buffer_ofs_after; } else if (state.using_skeleton) { glVertexAttribI4ui(VS::ARRAY_BONES, 0, 0, 0, 0); @@ -413,7 +404,7 @@ void RasterizerCanvasBaseGLES3::_draw_polygon(const int *p_indices, int p_index_ } #ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); + ERR_FAIL_COND((sizeof(int) * p_index_count) > data.polygon_index_buffer_size); #endif //bind the indices buffer. @@ -442,11 +433,15 @@ void RasterizerCanvasBaseGLES3::_draw_generic(GLuint p_primitive, int p_vertex_c //vertex uint32_t buffer_ofs = 0; + uint32_t buffer_ofs_after = buffer_ofs + (sizeof(Vector2) * p_vertex_count); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs_after > data.polygon_buffer_size); +#endif storage->buffer_orphan_and_upload(data.polygon_buffer_size, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_vertices, GL_ARRAY_BUFFER, _buffer_upload_usage_flag); glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, false, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Vector2) * p_vertex_count; + buffer_ofs = buffer_ofs_after; //color if (p_singlecolor) { @@ -457,19 +452,17 @@ void RasterizerCanvasBaseGLES3::_draw_generic(GLuint p_primitive, int p_vertex_c glDisableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } else { - - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, false, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Color) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } if (p_uvs) { - - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_TEX_UV); glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, false, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Vector2) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } else { glDisableVertexAttribArray(VS::ARRAY_TEX_UV); @@ -490,16 +483,17 @@ void RasterizerCanvasBaseGLES3::_draw_generic_indices(GLuint p_primitive, const //vertex uint32_t buffer_ofs = 0; + uint32_t buffer_ofs_after = buffer_ofs + (sizeof(Vector2) * p_vertex_count); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND(buffer_ofs_after > data.polygon_buffer_size); +#endif storage->buffer_orphan_and_upload(data.polygon_buffer_size, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_vertices, GL_ARRAY_BUFFER, _buffer_upload_usage_flag); glEnableVertexAttribArray(VS::ARRAY_VERTEX); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, false, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Vector2) * p_vertex_count; - //color -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); -#endif + buffer_ofs = buffer_ofs_after; + //color if (p_singlecolor) { glDisableVertexAttribArray(VS::ARRAY_COLOR); Color m = *p_colors; @@ -508,32 +502,22 @@ void RasterizerCanvasBaseGLES3::_draw_generic_indices(GLuint p_primitive, const glDisableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } else { - - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_COLOR); glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, false, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Color) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); -#endif - if (p_uvs) { - - glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs); + RAST_FAIL_COND(!storage->safe_buffer_sub_data(data.polygon_buffer_size, GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs, buffer_ofs_after)); glEnableVertexAttribArray(VS::ARRAY_TEX_UV); glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, false, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); - buffer_ofs += sizeof(Vector2) * p_vertex_count; + buffer_ofs = buffer_ofs_after; } else { glDisableVertexAttribArray(VS::ARRAY_TEX_UV); } -#ifdef DEBUG_ENABLED - ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size); -#endif - #ifdef RASTERIZER_EXTRA_CHECKS // very slow, do not enable in normal use for (int n = 0; n < p_index_count; n++) { @@ -541,6 +525,10 @@ void RasterizerCanvasBaseGLES3::_draw_generic_indices(GLuint p_primitive, const } #endif +#ifdef DEBUG_ENABLED + ERR_FAIL_COND((sizeof(int) * p_index_count) > data.polygon_index_buffer_size); +#endif + //bind the indices buffer. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); storage->buffer_orphan_and_upload(data.polygon_index_buffer_size, 0, sizeof(int) * p_index_count, p_indices, GL_ELEMENT_ARRAY_BUFFER, _buffer_upload_usage_flag); @@ -584,6 +572,7 @@ void RasterizerCanvasBaseGLES3::_draw_gui_primitive(int p_points, const Vector2 stride += 1; } + RAST_DEV_DEBUG_ASSERT(p_points <= 4); float b[(2 + 2 + 4 + 1) * 4]; for (int i = 0; i < p_points; i++) { @@ -1218,8 +1207,8 @@ void RasterizerCanvasBaseGLES3::initialize() { uint32_t poly_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128); ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); + poly_size = MAX(poly_size, 2); // minimum 2k, may still see anomalies in editor poly_size *= 1024; //kb - poly_size = MAX(poly_size, (2 + 2 + 4 + 1) * 4 * sizeof(float)); glGenBuffers(1, &data.polygon_buffer); glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); glBufferData(GL_ARRAY_BUFFER, poly_size, NULL, GL_DYNAMIC_DRAW); //allocate max size @@ -1278,6 +1267,7 @@ void RasterizerCanvasBaseGLES3::initialize() { uint32_t index_size = GLOBAL_DEF_RST("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", 128); ProjectSettings::get_singleton()->set_custom_property_info("rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PropertyInfo(Variant::INT, "rendering/limits/buffers/canvas_polygon_index_buffer_size_kb", PROPERTY_HINT_RANGE, "0,256,1,or_greater")); + index_size = MAX(index_size, 2); index_size *= 1024; //kb glGenBuffers(1, &data.polygon_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 42480a8b2f6..2df1d995027 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "rasterizer_canvas_gles3.h" + +#include "drivers/gles_common/rasterizer_asserts.h" #include "servers/visual/visual_server_raster.h" static const GLenum gl_primitive[] = { @@ -595,6 +597,11 @@ void RasterizerCanvasGLES3::render_batches(Item::Command *const *p_commands, Ite if (pline->triangles.size()) { +#ifdef RASTERIZER_EXTRA_CHECKS + if (pline->triangle_colors.ptr() && (pline->triangle_colors.size() != 1)) { + RAST_DEV_DEBUG_ASSERT(pline->triangle_colors.size() == pline->triangles.size()); + } +#endif _draw_generic(GL_TRIANGLE_STRIP, pline->triangles.size(), pline->triangles.ptr(), NULL, pline->triangle_colors.ptr(), pline->triangle_colors.size() == 1); #ifdef GLES_OVER_GL glEnable(GL_LINE_SMOOTH); @@ -774,16 +781,30 @@ void RasterizerCanvasGLES3::render_batches(Item::Command *const *p_commands, Ite Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size); } + + // we need a temporary because this must be nulled out + // if only a single color specified + const Color *colors = primitive->colors.ptr(); if (primitive->colors.size() == 1 && primitive->points.size() > 1) { Color col = primitive->colors[0]; glVertexAttrib4f(VS::ARRAY_COLOR, col.r, col.g, col.b, col.a); + colors = nullptr; } else if (primitive->colors.empty()) { glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); } +#ifdef RASTERIZER_EXTRA_CHECKS + else { + RAST_DEV_DEBUG_ASSERT(primitive->colors.size() == primitive->points.size()); + } - _draw_gui_primitive(primitive->points.size(), primitive->points.ptr(), primitive->colors.ptr(), primitive->uvs.ptr()); + if (primitive->uvs.ptr()) { + RAST_DEV_DEBUG_ASSERT(primitive->uvs.size() == primitive->points.size()); + } +#endif + + _draw_gui_primitive(primitive->points.size(), primitive->points.ptr(), colors, primitive->uvs.ptr()); } break; case Item::Command::TYPE_POLYGON: { diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h index f1fed7085e5..c3eccb7d239 100644 --- a/drivers/gles3/rasterizer_storage_gles3.h +++ b/drivers/gles3/rasterizer_storage_gles3.h @@ -32,6 +32,7 @@ #define RASTERIZERSTORAGEGLES3_H #include "core/self_list.h" +#include "drivers/gles_common/rasterizer_asserts.h" #include "servers/visual/rasterizer.h" #include "servers/visual/shader_language.h" #include "shader_compiler_gles3.h" @@ -1509,19 +1510,46 @@ public: virtual String get_video_adapter_name() const; virtual String get_video_adapter_vendor() const; - void buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target = GL_ARRAY_BUFFER, GLenum p_usage = GL_DYNAMIC_DRAW, bool p_optional_orphan = false); + void buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, 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; RasterizerStorageGLES3(); }; +inline bool RasterizerStorageGLES3::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 { + r_offset_after = p_offset + p_data_size; +#ifdef DEBUG_ENABLED + // we are trying to write across the edge of the buffer + if (r_offset_after > p_total_buffer_size) + return false; +#endif + glBufferSubData(p_target, p_offset, p_data_size, p_data); + return true; +} + // standardize the orphan / upload in one place so it can be changed per platform as necessary, and avoid future // bugs causing pipeline stalls -inline void RasterizerStorageGLES3::buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target, GLenum p_usage, bool p_optional_orphan) { +inline void RasterizerStorageGLES3::buffer_orphan_and_upload(unsigned int p_buffer_size, unsigned int p_offset, unsigned int p_data_size, const void *p_data, GLenum p_target, GLenum p_usage, bool p_optional_orphan) const { // Orphan the buffer to avoid CPU/GPU sync points caused by glBufferSubData // Was previously #ifndef GLES_OVER_GL however this causes stalls on desktop mac also (and possibly other) if (!p_optional_orphan || (config.should_orphan)) { glBufferData(p_target, p_buffer_size, NULL, p_usage); +#ifdef RASTERIZER_EXTRA_CHECKS + // fill with garbage off the end of the array + if (p_buffer_size) { + unsigned int start = p_offset + p_data_size; + unsigned int end = start + 1024; + if (end < p_buffer_size) { + uint8_t *garbage = (uint8_t *)alloca(1024); + for (int n = 0; n < 1024; n++) { + garbage[n] = Math::random(0, 255); + } + glBufferSubData(p_target, start, 1024, garbage); + } + } +#endif } + RAST_DEV_DEBUG_ASSERT((p_offset + p_data_size) <= p_buffer_size); glBufferSubData(p_target, p_offset, p_data_size, p_data); } diff --git a/drivers/gles_common/rasterizer_asserts.h b/drivers/gles_common/rasterizer_asserts.h index d2aae6beccf..d44e0408193 100644 --- a/drivers/gles_common/rasterizer_asserts.h +++ b/drivers/gles_common/rasterizer_asserts.h @@ -55,4 +55,13 @@ #define RAST_DEBUG_ASSERT(a) #endif +// Thin wrapper around ERR_FAIL_COND to allow us to make it debug only +#ifdef DEBUG_ENABLED +#define RAST_FAIL_COND(m_cond) ERR_FAIL_COND(m_cond) +#else +#define RAST_FAIL_COND(m_cond) \ + if (m_cond) { \ + } +#endif + #endif // RASTERIZER_ASSERTS_H