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.
This commit is contained in:
lawnjelly 2020-11-22 11:24:29 +00:00
parent b900ec03f0
commit 3c69377f10
7 changed files with 203 additions and 77 deletions

View file

@ -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);

View file

@ -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: {

View file

@ -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);
}

View file

@ -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);

View file

@ -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: {

View file

@ -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);
}

View file

@ -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