summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gles2/rasterizer_canvas_gles2.cpp947
-rw-r--r--drivers/gles2/rasterizer_canvas_gles2.h46
-rw-r--r--drivers/gles2/rasterizer_gles2.cpp5
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.h4
-rw-r--r--drivers/gles2/shader_gles2.cpp14
-rw-r--r--drivers/gles2/shader_gles2.h1
-rw-r--r--drivers/gles2/shaders/canvas.glsl33
7 files changed, 596 insertions, 454 deletions
diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp
index 3d388c031a..c35d9bac98 100644
--- a/drivers/gles2/rasterizer_canvas_gles2.cpp
+++ b/drivers/gles2/rasterizer_canvas_gles2.cpp
@@ -64,9 +64,13 @@ void RasterizerCanvasGLES2::_set_uniforms() {
state.canvas_shader.set_uniform(CanvasShaderGLES2::SCREEN_PIXEL_SIZE, screen_pixel_size);
}
+
+ state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, state.uniforms.texpixel_size);
}
void RasterizerCanvasGLES2::canvas_begin() {
+ data.primitive = GL_TRIANGLES;
+ data.texture = GL_NONE;
state.canvas_shader.bind();
if (storage->frame.current_rt) {
@@ -94,6 +98,7 @@ void RasterizerCanvasGLES2::canvas_begin() {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
+ data.texture = storage->resources.white_tex;
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
glDisableVertexAttribArray(VS::ARRAY_COLOR);
@@ -124,7 +129,7 @@ void RasterizerCanvasGLES2::canvas_begin() {
state.uniforms.extra_matrix = Transform2D();
_set_uniforms();
- _bind_quad_buffer();
+ state.prev_uniforms = state.uniforms;
}
void RasterizerCanvasGLES2::canvas_end() {
@@ -142,6 +147,7 @@ void RasterizerCanvasGLES2::canvas_end() {
RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map) {
RasterizerStorageGLES2::Texture *tex_return = NULL;
+ GLuint newtexid;
if (p_texture.is_valid()) {
@@ -151,8 +157,7 @@ RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(con
state.current_tex = RID();
state.current_tex_ptr = NULL;
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
+ newtexid = storage->resources.white_tex;
} else {
@@ -166,8 +171,7 @@ RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(con
texture->render_target->used_in_frame = true;
}
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, texture->tex_id);
+ newtexid = texture->tex_id;
state.current_tex = p_texture;
state.current_tex_ptr = texture;
@@ -178,8 +182,15 @@ RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(con
state.current_tex = RID();
state.current_tex_ptr = NULL;
+ newtexid = storage->resources.white_tex;
+ }
+
+ if (data.texture != newtexid) {
+ _flush();
+
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
+ glBindTexture(GL_TEXTURE_2D, newtexid);
+ data.texture = newtexid;
}
return tex_return;
@@ -189,217 +200,154 @@ void RasterizerCanvasGLES2::_set_texture_rect_mode(bool p_enable, bool p_ninepat
}
void RasterizerCanvasGLES2::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) {
+ _begin(GL_TRIANGLES);
+ _prepare(p_vertex_count, p_index_count);
- glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
+ Vertex *v = data.mem_vertex_buffer + data.mem_vertex_buffer_offset;
- uint32_t buffer_ofs = 0;
-
- glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vector2) * p_vertex_count, p_vertices);
- glEnableVertexAttribArray(VS::ARRAY_VERTEX);
- glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL);
- buffer_ofs += sizeof(Vector2) * p_vertex_count;
+ bool single;
+ Color color;
if (p_singlecolor) {
- glDisableVertexAttribArray(VS::ARRAY_COLOR);
- Color m = *p_colors;
- glVertexAttrib4f(VS::ARRAY_COLOR, m.r, m.g, m.b, m.a);
+ single = true;
+ color = *p_colors;
} else if (!p_colors) {
- 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);
- glEnableVertexAttribArray(VS::ARRAY_COLOR);
- glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), ((uint8_t *)0) + buffer_ofs);
- buffer_ofs += sizeof(Color) * p_vertex_count;
- }
-
- if (p_uvs) {
- glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs);
- glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
- glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)0) + buffer_ofs);
- buffer_ofs += sizeof(Vector2) * p_vertex_count;
+ single = true;
+ color = Color(1, 1, 1, 1);
} else {
- glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
+ single = false;
}
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer);
- glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(int) * p_index_count, p_indices);
-
- glDrawElements(GL_TRIANGLES, p_index_count, GL_UNSIGNED_INT, 0);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-}
+ const bool use_single_color = single;
+ const Color single_color = color;
-void RasterizerCanvasGLES2::_draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) {
+ for (int i = 0; i < p_vertex_count; ++i) {
+ v->v = p_vertices[i];
- glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
+ if (use_single_color)
+ v->c = single_color;
+ else
+ v->c = p_colors[i];
- uint32_t buffer_ofs = 0;
+ if (p_uvs)
+ v->uv = p_uvs[i];
+ else
+ v->uv = Vector2();
- glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vector2) * p_vertex_count, p_vertices);
- glEnableVertexAttribArray(VS::ARRAY_VERTEX);
- glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), (uint8_t *)0);
- buffer_ofs += sizeof(Vector2) * p_vertex_count;
-
- if (p_singlecolor) {
- glDisableVertexAttribArray(VS::ARRAY_COLOR);
- Color m = *p_colors;
- glVertexAttrib4f(VS::ARRAY_COLOR, m.r, m.g, m.b, m.a);
- } else if (!p_colors) {
- 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);
- glEnableVertexAttribArray(VS::ARRAY_COLOR);
- glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), ((uint8_t *)0) + buffer_ofs);
- buffer_ofs += sizeof(Color) * p_vertex_count;
+ ++v;
}
- if (p_uvs) {
- glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs);
- glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
- glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)0) + buffer_ofs);
- } else {
- glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
- }
+ memcpy(data.mem_index_buffer + data.mem_index_buffer_offset, p_indices, p_index_count * sizeof(int));
- glDrawArrays(p_primitive, 0, p_vertex_count);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
+ _commit(p_vertex_count, p_index_count);
}
-void RasterizerCanvasGLES2::_draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs) {
-
- static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN };
-
- int color_offset = 0;
- int uv_offset = 0;
- int stride = 2;
-
- if (p_colors) {
- color_offset = stride;
- stride += 4;
- }
+void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip, RasterizerStorageGLES2::Material *p_material) {
- if (p_uvs) {
- uv_offset = stride;
- stride += 2;
- }
+ int command_count = p_item->commands.size();
+ Item::Command **commands = p_item->commands.ptrw();
- float buffer_data[(2 + 2 + 4) * 4];
+ for (int i = 0; i < command_count; i++) {
- for (int i = 0; i < p_points; i++) {
- buffer_data[stride * i + 0] = p_vertices[i].x;
- buffer_data[stride * i + 1] = p_vertices[i].y;
- }
+ Item::Command *command = commands[i];
- if (p_colors) {
- for (int i = 0; i < p_points; i++) {
- buffer_data[stride * i + color_offset + 0] = p_colors[i].r;
- buffer_data[stride * i + color_offset + 1] = p_colors[i].g;
- buffer_data[stride * i + color_offset + 2] = p_colors[i].b;
- buffer_data[stride * i + color_offset + 3] = p_colors[i].a;
+ if (command->type != Item::Command::TYPE_RECT && state.tiled) {
+ _flush();
+ _untile();
}
- }
- if (p_uvs) {
- for (int i = 0; i < p_points; i++) {
- buffer_data[stride * i + uv_offset + 0] = p_uvs[i].x;
- buffer_data[stride * i + uv_offset + 1] = p_uvs[i].y;
- }
- }
+ switch (command->type) {
- glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
- glBufferSubData(GL_ARRAY_BUFFER, 0, p_points * stride * 4 * sizeof(float), buffer_data);
+ case Item::Command::TYPE_LINE: {
+ const Item::CommandLine *line = static_cast<Item::CommandLine *>(command);
- glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), NULL);
+ if (line->width <= 1) {
+ const int p_vertex_count = 2;
+ const int p_index_count = 2;
- if (p_colors) {
- glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, stride * sizeof(float), (uint8_t *)0 + color_offset * sizeof(float));
- glEnableVertexAttribArray(VS::ARRAY_COLOR);
- }
+ _begin(GL_LINES);
+ _prepare(p_vertex_count, p_index_count);
- if (p_uvs) {
- glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), (uint8_t *)0 + uv_offset * sizeof(float));
- glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
- }
+ _bind_shader(p_material);
+ _bind_canvas_texture(RID(), RID());
- glDrawArrays(prim[p_points], 0, p_points);
+ Vertex vertices[p_vertex_count];
- glBindBuffer(GL_ARRAY_BUFFER, 0);
-}
+ vertices[0].v = Vector2(line->from.x, line->from.y);
+ vertices[0].c = line->color;
+ vertices[0].uv = Vector2();
-void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip, RasterizerStorageGLES2::Material *p_material) {
+ vertices[1].v = Vector2(line->to.x, line->to.y);
+ vertices[1].c = line->color;
+ vertices[1].uv = Vector2();
- int command_count = p_item->commands.size();
- Item::Command **commands = p_item->commands.ptrw();
+ memcpy(data.mem_vertex_buffer + data.mem_vertex_buffer_offset, vertices, sizeof(vertices));
- for (int i = 0; i < command_count; i++) {
+ const int indices[p_index_count] = { 0, 1 };
- Item::Command *command = commands[i];
+ memcpy(data.mem_index_buffer + data.mem_index_buffer_offset, indices, sizeof(indices));
- switch (command->type) {
+ _commit(p_vertex_count, p_index_count);
+ } else {
+ const int p_vertex_count = 4;
+ const int p_index_count = 6;
- case Item::Command::TYPE_LINE: {
+ _begin(GL_TRIANGLES);
+ _prepare(p_vertex_count, p_index_count);
- Item::CommandLine *line = static_cast<Item::CommandLine *>(command);
+ _bind_shader(p_material);
+ _bind_canvas_texture(RID(), RID());
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
- if (state.canvas_shader.bind()) {
- _set_uniforms();
- state.canvas_shader.use_material((void *)p_material);
- }
+ Vertex *v = data.mem_vertex_buffer + data.mem_vertex_buffer_offset;
- _bind_canvas_texture(RID(), RID());
+ Vector2 t = (line->from - line->to).normalized().tangent() * line->width * 0.5;
- glDisableVertexAttribArray(VS::ARRAY_COLOR);
- glVertexAttrib4fv(VS::ARRAY_COLOR, line->color.components);
+ v[0].v = line->from - t;
+ v[0].c = line->color;
+ v[0].uv = Vector2();
- state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, state.uniforms.modelview_matrix);
+ v[1].v = line->from + t;
+ v[1].c = line->color;
+ v[1].uv = Vector2();
- if (line->width <= 1) {
- Vector2 verts[2] = {
- Vector2(line->from.x, line->from.y),
- Vector2(line->to.x, line->to.y)
- };
+ v[2].v = line->to + t;
+ v[2].c = line->color;
+ v[2].uv = Vector2();
- _draw_gui_primitive(2, verts, NULL, NULL);
- } else {
- Vector2 t = (line->from - line->to).normalized().tangent() * line->width * 0.5;
+ v[3].v = line->to - t;
+ v[3].c = line->color;
+ v[3].uv = Vector2();
- Vector2 verts[4] = {
- line->from - t,
- line->from + t,
- line->to + t,
- line->to - t
+ const int indices[p_index_count] = {
+ 0, 1, 2,
+ 2, 3, 0
};
- _draw_gui_primitive(4, verts, NULL, NULL);
+ memcpy(data.mem_index_buffer + data.mem_index_buffer_offset, indices, sizeof(indices));
+
+ _commit(p_vertex_count, p_index_count);
}
+
} break;
case Item::Command::TYPE_RECT: {
+ const int p_vertex_count = 4;
+ const int p_index_count = 6;
- Item::CommandRect *r = static_cast<Item::CommandRect *>(command);
+ _begin(GL_TRIANGLES);
+ _prepare(p_vertex_count, p_index_count);
- glDisableVertexAttribArray(VS::ARRAY_COLOR);
- glVertexAttrib4fv(VS::ARRAY_COLOR, r->modulate.components);
+ Item::CommandRect *r = static_cast<Item::CommandRect *>(command);
- _bind_quad_buffer();
+ _bind_shader(p_material);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
- if (state.canvas_shader.bind()) {
- _set_uniforms();
- state.canvas_shader.use_material((void *)p_material);
- }
+ Rect2 src_rect;
+ Rect2 dst_rect;
RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(r->texture, r->normal_map);
if (!tex) {
- Rect2 dst_rect = Rect2(r->rect.position, r->rect.size);
+ dst_rect = Rect2(r->rect.position, r->rect.size);
if (dst_rect.size.width < 0) {
dst_rect.position.x += dst_rect.size.width;
@@ -410,24 +358,28 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
dst_rect.size.height *= -1;
}
- state.canvas_shader.set_uniform(CanvasShaderGLES2::DST_RECT, Color(dst_rect.position.x, dst_rect.position.y, dst_rect.size.x, dst_rect.size.y));
- state.canvas_shader.set_uniform(CanvasShaderGLES2::SRC_RECT, Color(0, 0, 1, 1));
+ src_rect = Rect2(0, 0, 1, 1);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
} else {
- bool untile = false;
+ const bool tiled = r->flags & CANVAS_RECT_TILE && !(tex->flags & VS::TEXTURE_FLAG_REPEAT);
- if (r->flags & CANVAS_RECT_TILE && !(tex->flags & VS::TEXTURE_FLAG_REPEAT)) {
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- untile = true;
+ if (tiled != state.tiled) {
+ _flush();
+
+ if (tiled) {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ state.tiled = true;
+ } else {
+ _untile();
+ }
}
Size2 texpixel_size(1.0 / tex->width, 1.0 / tex->height);
- Rect2 src_rect = (r->flags & CANVAS_RECT_REGION) ? Rect2(r->source.position * texpixel_size, r->source.size * texpixel_size) : Rect2(0, 0, 1, 1);
- Rect2 dst_rect = Rect2(r->rect.position, r->rect.size);
+ src_rect = (r->flags & CANVAS_RECT_REGION) ? Rect2(r->source.position * texpixel_size, r->source.size * texpixel_size) : Rect2(0, 0, 1, 1);
+ dst_rect = Rect2(r->rect.position, r->rect.size);
if (dst_rect.size.width < 0) {
dst_rect.position.x += dst_rect.size.width;
@@ -440,48 +392,61 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
if (r->flags & CANVAS_RECT_FLIP_H) {
src_rect.size.x *= -1;
+ src_rect.position.x -= src_rect.size.width;
}
-
if (r->flags & CANVAS_RECT_FLIP_V) {
src_rect.size.y *= -1;
+ src_rect.position.y -= src_rect.size.height;
}
-
if (r->flags & CANVAS_RECT_TRANSPOSE) {
dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform
}
- state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
+ state.uniforms.texpixel_size = texpixel_size;
+ }
- state.canvas_shader.set_uniform(CanvasShaderGLES2::DST_RECT, Color(dst_rect.position.x, dst_rect.position.y, dst_rect.size.x, dst_rect.size.y));
- state.canvas_shader.set_uniform(CanvasShaderGLES2::SRC_RECT, Color(src_rect.position.x, src_rect.position.y, src_rect.size.x, src_rect.size.y));
+ Vertex *v = data.mem_vertex_buffer + data.mem_vertex_buffer_offset;
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ // 0,0
+ v[0].v = dst_rect.position;
+ v[0].c = r->modulate;
+ v[0].uv = src_rect.position;
- if (untile) {
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
- }
+ // 0,1
+ v[1].v = Vector2(dst_rect.position.x, dst_rect.position.y + dst_rect.size.y);
+ v[1].c = r->modulate;
+ v[1].uv = Vector2(src_rect.position.x, src_rect.position.y + src_rect.size.y);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ // 1,1
+ v[2].v = Vector2(dst_rect.position.x + dst_rect.size.x, dst_rect.position.y + dst_rect.size.y);
+ v[2].c = r->modulate;
+ v[2].uv = Vector2(src_rect.position.x + src_rect.size.x, src_rect.position.y + src_rect.size.y);
+ // 1,0
+ v[3].v = Vector2(dst_rect.position.x + dst_rect.size.x, dst_rect.position.y);
+ v[3].c = r->modulate;
+ v[3].uv = Vector2(src_rect.position.x + src_rect.size.x, src_rect.position.y);
+
+ const int indices[p_index_count] = {
+ 0, 1, 2,
+ 2, 3, 0
+ };
+
+ memcpy(data.mem_index_buffer + data.mem_index_buffer_offset, indices, sizeof(int) * p_index_count);
+
+ _commit(p_vertex_count, p_index_count);
} break;
case Item::Command::TYPE_NINEPATCH: {
+ const int p_vertex_count = 16;
+ const int p_index_count = 54;
- Item::CommandNinePatch *np = static_cast<Item::CommandNinePatch *>(command);
+ _begin(GL_TRIANGLES);
+ _prepare(p_vertex_count, p_index_count);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true);
- if (state.canvas_shader.bind()) {
- _set_uniforms();
- state.canvas_shader.use_material((void *)p_material);
- }
-
- glDisableVertexAttribArray(VS::ARRAY_COLOR);
- glVertexAttrib4fv(VS::ARRAY_COLOR, np->color.components);
+ Item::CommandNinePatch *np = static_cast<Item::CommandNinePatch *>(command);
+ _bind_shader(p_material);
RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(np->texture, np->normal_map);
if (!tex) {
@@ -491,8 +456,7 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
Size2 texpixel_size(1.0 / tex->width, 1.0 / tex->height);
- // state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, state.uniforms.modelview_matrix);
- state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
+ state.uniforms.texpixel_size = texpixel_size;
Rect2 source = np->source;
if (source.size.x == 0 && source.size.y == 0) {
@@ -504,255 +468,334 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur
// this buffer contains [ POS POS UV UV ] *
- float buffer[16 * 2 + 16 * 2];
+ Vertex *v = data.mem_vertex_buffer + data.mem_vertex_buffer_offset;
- {
+ v[0].v = np->rect.position;
+ v[0].c = np->color;
+ v[0].uv = source.position * texpixel_size;
- // first row
+ v[1].v = np->rect.position + Vector2(np->margin[MARGIN_LEFT], 0);
+ v[1].c = np->color;
+ v[1].uv = (source.position + Vector2(np->margin[MARGIN_LEFT], 0)) * texpixel_size;
- buffer[(0 * 4 * 4) + 0] = np->rect.position.x;
- buffer[(0 * 4 * 4) + 1] = np->rect.position.y;
+ v[2].v = np->rect.position + Vector2(np->rect.size.x - np->margin[MARGIN_RIGHT], 0);
+ v[2].c = np->color;
+ v[2].uv = (source.position + Vector2(source.size.x - np->margin[MARGIN_RIGHT], 0)) * texpixel_size;
- buffer[(0 * 4 * 4) + 2] = source.position.x * texpixel_size.x;
- buffer[(0 * 4 * 4) + 3] = source.position.y * texpixel_size.y;
+ v[3].v = np->rect.position + Vector2(np->rect.size.x, 0);
+ v[3].c = np->color;
+ v[3].uv = (source.position + Vector2(source.size.x, 0)) * texpixel_size;
- buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT];
- buffer[(0 * 4 * 4) + 5] = np->rect.position.y;
+ v[4].v = np->rect.position + Vector2(0, np->margin[MARGIN_TOP]);
+ v[4].c = np->color;
+ v[4].uv = (source.position + Vector2(0, np->margin[MARGIN_TOP])) * texpixel_size;
- buffer[(0 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
- buffer[(0 * 4 * 4) + 7] = source.position.y * texpixel_size.y;
+ v[5].v = np->rect.position + Vector2(np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP]);
+ v[5].c = np->color;
+ v[5].uv = (source.position + Vector2(np->margin[MARGIN_LEFT], np->margin[MARGIN_TOP])) * texpixel_size;
- buffer[(0 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT];
- buffer[(0 * 4 * 4) + 9] = np->rect.position.y;
+ v[6].v = np->rect.position + Vector2(np->rect.size.x - np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP]);
+ v[6].c = np->color;
+ v[6].uv = (source.position + Vector2(source.size.x - np->margin[MARGIN_RIGHT], np->margin[MARGIN_TOP])) * texpixel_size;
- buffer[(0 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
- buffer[(0 * 4 * 4) + 11] = source.position.y * texpixel_size.y;
+ v[7].v = np->rect.position + Vector2(np->rect.size.x, np->margin[MARGIN_TOP]);
+ v[7].c = np->color;
+ v[7].uv = (source.position + Vector2(source.size.x, np->margin[MARGIN_TOP])) * texpixel_size;
- buffer[(0 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x;
- buffer[(0 * 4 * 4) + 13] = np->rect.position.y;
+ v[8].v = np->rect.position + Vector2(0, np->rect.size.y - np->margin[MARGIN_BOTTOM]);
+ v[8].c = np->color;
+ v[8].uv = (source.position + Vector2(0, source.size.y - np->margin[MARGIN_BOTTOM])) * texpixel_size;
- buffer[(0 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;
- buffer[(0 * 4 * 4) + 15] = source.position.y * texpixel_size.y;
+ v[9].v = np->rect.position + Vector2(np->margin[MARGIN_LEFT], np->rect.size.y - np->margin[MARGIN_BOTTOM]);
+ v[9].c = np->color;
+ v[9].uv = (source.position + Vector2(np->margin[MARGIN_LEFT], source.size.y - np->margin[MARGIN_BOTTOM])) * texpixel_size;
- // second row
+ v[10].v = np->rect.position + np->rect.size - Vector2(np->margin[MARGIN_RIGHT], np->margin[MARGIN_BOTTOM]);
+ v[10].c = np->color;
+ v[10].uv = (source.position + source.size - Vector2(np->margin[MARGIN_RIGHT], np->margin[MARGIN_BOTTOM])) * texpixel_size;
- buffer[(1 * 4 * 4) + 0] = np->rect.position.x;
- buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP];
+ v[11].v = np->rect.position + np->rect.size - Vector2(0, np->margin[MARGIN_BOTTOM]);
+ v[11].c = np->color;
+ v[11].uv = (source.position + source.size - Vector2(0, np->margin[MARGIN_BOTTOM])) * texpixel_size;
- buffer[(1 * 4 * 4) + 2] = source.position.x * texpixel_size.x;
- buffer[(1 * 4 * 4) + 3] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
+ v[12].v = np->rect.position + Vector2(0, np->rect.size.y);
+ v[12].c = np->color;
+ v[12].uv = (source.position + Vector2(0, source.size.y)) * texpixel_size;
- buffer[(1 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT];
- buffer[(1 * 4 * 4) + 5] = np->rect.position.y + np->margin[MARGIN_TOP];
+ v[13].v = np->rect.position + Vector2(np->margin[MARGIN_LEFT], np->rect.size.y);
+ v[13].c = np->color;
+ v[13].uv = (source.position + Vector2(np->margin[MARGIN_LEFT], source.size.y)) * texpixel_size;
- buffer[(1 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
- buffer[(1 * 4 * 4) + 7] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
+ v[14].v = np->rect.position + np->rect.size - Vector2(np->margin[MARGIN_RIGHT], 0);
+ v[14].c = np->color;
+ v[14].uv = (source.position + source.size - Vector2(np->margin[MARGIN_RIGHT], 0)) * texpixel_size;
- buffer[(1 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT];
- buffer[(1 * 4 * 4) + 9] = np->rect.position.y + np->margin[MARGIN_TOP];
+ v[15].v = np->rect.position + np->rect.size;
+ v[15].c = np->color;
+ v[15].uv = (source.position + source.size) * texpixel_size;
- buffer[(1 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
- buffer[(1 * 4 * 4) + 11] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
+ memcpy(data.mem_index_buffer + data.mem_index_buffer_offset, data.ninepatch_elements, sizeof(data.ninepatch_elements));
- buffer[(1 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x;
- buffer[(1 * 4 * 4) + 13] = np->rect.position.y + np->margin[MARGIN_TOP];
+ _commit(p_vertex_count, p_index_count - (np->draw_center ? 0 : 6));
+ } break;
- buffer[(1 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;
- buffer[(1 * 4 * 4) + 15] = (source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y;
+ case Item::Command::TYPE_CIRCLE: {
+ Item::CommandCircle *circle = static_cast<Item::CommandCircle *>(command);
- // thrid row
+ _bind_shader(p_material);
- buffer[(2 * 4 * 4) + 0] = np->rect.position.x;
- buffer[(2 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM];
+ const int num_points = 32;
- buffer[(2 * 4 * 4) + 2] = source.position.x * texpixel_size.x;
- buffer[(2 * 4 * 4) + 3] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
+ Vector2 points[num_points + 1];
+ points[num_points] = circle->pos;
- buffer[(2 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT];
- buffer[(2 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM];
+ int indices[num_points * 3];
- buffer[(2 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
- buffer[(2 * 4 * 4) + 7] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
+ for (int i = 0; i < num_points; i++) {
+ points[i] = circle->pos + Vector2(Math::sin(i * Math_PI * 2.0 / num_points), Math::cos(i * Math_PI * 2.0 / num_points)) * circle->radius;
+ indices[i * 3 + 0] = i;
+ indices[i * 3 + 1] = (i + 1) % num_points;
+ indices[i * 3 + 2] = num_points;
+ }
- buffer[(2 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT];
- buffer[(2 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM];
+ _bind_canvas_texture(RID(), RID());
- buffer[(2 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
- buffer[(2 * 4 * 4) + 11] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
+ _draw_polygon(indices, num_points * 3, num_points + 1, points, NULL, &circle->color, true);
+ } break;
- buffer[(2 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x;
- buffer[(2 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y - np->margin[MARGIN_BOTTOM];
+ case Item::Command::TYPE_POLYGON: {
+ Item::CommandPolygon *polygon = static_cast<Item::CommandPolygon *>(command);
- buffer[(2 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;
- buffer[(2 * 4 * 4) + 15] = (source.position.y + source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y;
+ const int *indices = polygon->indices.ptr();
+ if (!indices) // self-intersecting polygon
+ break;
- // fourth row
+ _bind_shader(p_material);
+ RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(polygon->texture, polygon->normal_map);
- buffer[(3 * 4 * 4) + 0] = np->rect.position.x;
- buffer[(3 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y;
+ if (texture) {
+ Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
+ state.uniforms.texpixel_size = texpixel_size;
+ }
- buffer[(3 * 4 * 4) + 2] = source.position.x * texpixel_size.x;
- buffer[(3 * 4 * 4) + 3] = (source.position.y + source.size.y) * texpixel_size.y;
+ _draw_polygon(indices, polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1);
+ } break;
- buffer[(3 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT];
- buffer[(3 * 4 * 4) + 5] = np->rect.position.y + np->rect.size.y;
+ case Item::Command::TYPE_POLYLINE: {
+ Item::CommandPolyLine *pline = static_cast<Item::CommandPolyLine *>(command);
- buffer[(3 * 4 * 4) + 6] = (source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x;
- buffer[(3 * 4 * 4) + 7] = (source.position.y + source.size.y) * texpixel_size.y;
+ if (pline->triangles.size()) {
+ const int p_vertex_count = pline->triangles.size();
+ const int p_triangle_count = p_vertex_count - 2;
+ const int p_index_count = p_triangle_count * 3;
- buffer[(3 * 4 * 4) + 8] = np->rect.position.x + np->rect.size.x - np->margin[MARGIN_RIGHT];
- buffer[(3 * 4 * 4) + 9] = np->rect.position.y + np->rect.size.y;
+ _begin(GL_TRIANGLES);
+ _prepare(p_vertex_count, p_index_count);
- buffer[(3 * 4 * 4) + 10] = (source.position.x + source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x;
- buffer[(3 * 4 * 4) + 11] = (source.position.y + source.size.y) * texpixel_size.y;
+ _bind_shader(p_material);
+ _bind_canvas_texture(RID(), RID());
- buffer[(3 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x;
- buffer[(3 * 4 * 4) + 13] = np->rect.position.y + np->rect.size.y;
+ const Vector2 *t = pline->triangles.ptr();
+ Vertex *v = data.mem_vertex_buffer + data.mem_vertex_buffer_offset;
- buffer[(3 * 4 * 4) + 14] = (source.position.x + source.size.x) * texpixel_size.x;
- buffer[(3 * 4 * 4) + 15] = (source.position.y + source.size.y) * texpixel_size.y;
+ const bool p_singlecolor = pline->triangle_colors.size() == 1;
+ const Color *p_colors = pline->triangle_colors.ptr();
- // print_line(String::num((source.position.y + source.size.y) * texpixel_size.y));
- }
+ bool single;
+ Color color;
- glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices);
- glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * (16 + 16) * 2, buffer);
+ if (pline->triangle_colors.size() == 1) {
+ single = true;
+ color = *p_colors;
+ } else if (!p_colors) {
+ single = true;
+ color = Color(1, 1, 1, 1);
+ } else {
+ single = false;
+ }
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ninepatch_elements);
+ const bool use_single_color = single;
+ const Color single_color = color;
- glEnableVertexAttribArray(VS::ARRAY_VERTEX);
- glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
+ for (int i = 0; i < p_vertex_count; ++i) {
+ if (use_single_color)
+ v->c = single_color;
+ else
+ v->c = p_colors[i];
- glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), NULL);
- glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (uint8_t *)0 + (sizeof(float) * 2));
+ v->uv = Vector2();
+ v->v = t[i];
- glDrawElements(GL_TRIANGLES, 18 * 3 - (np->draw_center ? 0 : 6), GL_UNSIGNED_BYTE, NULL);
+ ++v;
+ }
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ for (int i = 0; i < p_triangle_count; ++i) {
+ const int indices[3] = {
+ i, i + 1, i + 2
+ };
- } break;
+ memcpy(data.mem_index_buffer + data.mem_index_buffer_offset + i * 3, indices, sizeof(indices));
+ }
- case Item::Command::TYPE_CIRCLE: {
+ _commit(p_vertex_count, p_index_count);
+ } else {
+ _begin(GL_LINES);
- Item::CommandCircle *circle = static_cast<Item::CommandCircle *>(command);
+ _bind_shader(p_material);
+ _bind_canvas_texture(RID(), RID());
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
+ const Color *p_colors = pline->line_colors.ptr();
- if (state.canvas_shader.bind()) {
- _set_uniforms();
- state.canvas_shader.use_material((void *)p_material);
- }
+ bool single;
+ Color color;
- static const int num_points = 32;
+ if (pline->line_colors.size() == 1) {
+ single = true;
+ color = *p_colors;
+ } else if (!p_colors) {
+ single = true;
+ color = Color(1, 1, 1, 1);
+ } else {
+ single = false;
+ }
- Vector2 points[num_points + 1];
- points[num_points] = circle->pos;
+ const bool use_single_color = single;
+ const Color single_color = color;
- int indices[num_points * 3];
+ const Vector2 *p_lines = pline->lines.ptr();
- for (int i = 0; i < num_points; i++) {
- points[i] = circle->pos + Vector2(Math::sin(i * Math_PI * 2.0 / num_points), Math::cos(i * Math_PI * 2.0 / num_points)) * circle->radius;
- indices[i * 3 + 0] = i;
- indices[i * 3 + 1] = (i + 1) % num_points;
- indices[i * 3 + 2] = num_points;
- }
+ if (pline->multiline) {
+ const int p_lines_count = pline->lines.size() / 2;
- _bind_canvas_texture(RID(), RID());
+ for (int i = 0; i < p_lines_count; ++i) {
+ const int p_vertex_count = 2;
+ const int p_index_count = 2;
- _draw_polygon(indices, num_points * 3, num_points + 1, points, NULL, &circle->color, true);
- } break;
+ _prepare(p_vertex_count, p_index_count);
- case Item::Command::TYPE_POLYGON: {
+ Vertex *v = data.mem_vertex_buffer + data.mem_vertex_buffer_offset;
- Item::CommandPolygon *polygon = static_cast<Item::CommandPolygon *>(command);
+ for (int j = 0; j < 2; ++j) {
+ if (use_single_color)
+ v->c = single_color;
+ else
+ v->c = p_colors[i];
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true);
+ v->uv = Vector2();
+ v->v = p_lines[i * 2 + j];
- if (state.canvas_shader.bind()) {
- _set_uniforms();
- state.canvas_shader.use_material((void *)p_material);
- }
+ ++v;
+ }
- RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(polygon->texture, polygon->normal_map);
+ const int indices[p_index_count] = { 0, 1 };
- if (texture) {
- Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
- state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
- }
+ memcpy(data.mem_index_buffer + data.mem_index_buffer_offset, indices, sizeof(indices));
- _draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1);
- } break;
+ _commit(p_vertex_count, p_index_count);
+ }
+ } else {
+ const int p_vertex_count = pline->lines.size();
+ const int p_lines_count = p_vertex_count - 1;
+ const int p_index_count = p_lines_count * 2;
- case Item::Command::TYPE_POLYLINE: {
- Item::CommandPolyLine *pline = static_cast<Item::CommandPolyLine *>(command);
+ _prepare(p_vertex_count, p_index_count);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
+ _bind_shader(p_material);
+ _bind_canvas_texture(RID(), RID());
- if (state.canvas_shader.bind()) {
- _set_uniforms();
- state.canvas_shader.use_material((void *)p_material);
- }
+ Vertex *v = data.mem_vertex_buffer + data.mem_vertex_buffer_offset;
- _bind_canvas_texture(RID(), RID());
+ for (int i = 0; i < p_vertex_count; ++i) {
+ if (use_single_color)
+ v->c = single_color;
+ else
+ v->c = p_colors[i];
- if (pline->triangles.size()) {
- _draw_generic(GL_TRIANGLE_STRIP, pline->triangles.size(), pline->triangles.ptr(), NULL, pline->triangle_colors.ptr(), pline->triangle_colors.size() == 1);
- } else {
- if (pline->multiline) {
- int todo = pline->lines.size() / 2;
- int max_per_call = data.polygon_buffer_size / (sizeof(real_t) * 4);
- int offset = 0;
-
- while (todo) {
- int to_draw = MIN(max_per_call, todo);
- _draw_generic(GL_LINES, to_draw * 2, &pline->lines.ptr()[offset], NULL, pline->line_colors.size() == 1 ? pline->line_colors.ptr() : &pline->line_colors.ptr()[offset], pline->line_colors.size() == 1);
- todo -= to_draw;
- offset += to_draw * 2;
+ v->uv = Vector2();
+ v->v = p_lines[i];
+
+ ++v;
}
- } else {
- _draw_generic(GL_LINES, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1);
+
+ for (int i = 0; i < p_lines_count; ++i) {
+ const int indices[2] = { i, i + 1 };
+
+ memcpy(data.mem_index_buffer + data.mem_index_buffer_offset + i * 2, indices, sizeof(indices));
+ }
+
+ _commit(p_vertex_count, p_index_count);
}
}
} break;
case Item::Command::TYPE_PRIMITIVE: {
-
Item::CommandPrimitive *primitive = static_cast<Item::CommandPrimitive *>(command);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false);
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true);
- if (state.canvas_shader.bind()) {
- _set_uniforms();
- state.canvas_shader.use_material((void *)p_material);
- }
+ const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN };
ERR_CONTINUE(primitive->points.size() < 1);
+ _bind_shader(p_material);
RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(primitive->texture, primitive->normal_map);
if (texture) {
Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
- state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size);
+ state.uniforms.texpixel_size = texpixel_size;
}
+ const int p_vertex_count = primitive->points.size();
+ const int p_index_count = p_vertex_count;
+
+ _begin(prim[p_vertex_count]);
+ _prepare(p_vertex_count, p_index_count);
+
+ Vertex *v = data.mem_vertex_buffer + data.mem_vertex_buffer_offset;
+ int *index = data.mem_index_buffer + data.mem_index_buffer_offset;
+
+ Color c;
+ bool p_single_color;
+
+ const Color *p_colors = primitive->colors.ptr();
+ const Vector2 *p_uvs = primitive->uvs.ptr();
+ const Vector2 *p_points = primitive->points.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);
+ p_single_color = true;
+ c = primitive->colors[0];
} else if (primitive->colors.empty()) {
- glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
+ p_single_color = true;
+ c = Color(1, 1, 1, 1);
+ } else {
+ p_single_color = false;
+ }
+
+ const bool use_single_color = p_single_color;
+ const Color single_color = c;
+
+ for (int i = 0; i < p_vertex_count; ++i) {
+ if (use_single_color)
+ v->c = single_color;
+ else
+ v->c = p_colors[i];
+
+ if (p_uvs)
+ v->uv = p_uvs[i];
+ else
+ v->uv = Vector2();
+
+ v->v = p_points[i];
+
+ index[i] = i;
+
+ ++v;
}
- _draw_gui_primitive(primitive->points.size(), primitive->points.ptr(), primitive->colors.ptr(), primitive->uvs.ptr());
+ _commit(p_vertex_count, p_index_count);
} break;
case Item::Command::TYPE_TRANSFORM: {
Item::CommandTransform *transform = static_cast<Item::CommandTransform *>(command);
state.uniforms.extra_matrix = transform->xform;
- state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX, state.uniforms.extra_matrix);
} break;
case Item::Command::TYPE_PARTICLES: {
@@ -815,6 +858,7 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex);
+ data.texture = storage->resources.white_tex;
int last_blend_mode = -1;
@@ -824,8 +868,9 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
Item *ci = p_item_list;
- if (current_clip != ci->final_clip_owner) {
+ Item *material_owner = ci->material_owner ? ci->material_owner : ci;
+ if (current_clip != ci->final_clip_owner) {
current_clip = ci->final_clip_owner;
if (current_clip) {
@@ -849,8 +894,6 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
}
}
- Item *material_owner = ci->material_owner ? ci->material_owner : ci;
-
RID material = material_owner->material;
RasterizerStorageGLES2::Material *material_ptr = storage->material_owner.getornull(material);
@@ -980,9 +1023,11 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
state.uniforms.extra_matrix = Transform2D();
_set_uniforms();
-
_canvas_item_render_commands(p_item_list, NULL, reclip, material_ptr);
+ // TODO: figure out when to _flush to get better batching results
+ _flush();
+
rebind_shader = true; // hacked in for now.
if (reclip) {
@@ -996,6 +1041,8 @@ void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, cons
p_item_list = p_item_list->next;
}
+ _flush();
+
if (current_clip) {
glDisable(GL_SCISSOR_TEST);
}
@@ -1034,17 +1081,40 @@ void RasterizerCanvasGLES2::reset_canvas() {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
-void RasterizerCanvasGLES2::_bind_quad_buffer() {
- glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices);
- glEnableVertexAttribArray(VS::ARRAY_VERTEX);
- glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, NULL);
-}
-void RasterizerCanvasGLES2::draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src) {
+void RasterizerCanvasGLES2::draw_generic_textured_rect(const Rect2 &dst_rect, const Rect2 &src_rect) {
+
+ const int p_index_count = 6;
+ const int p_vertex_count = 4;
+
+ Vertex v[p_vertex_count];
+ Color c(1, 1, 1, 1);
+
+ // 0,0
+ v[0].v = dst_rect.position;
+ v[0].c = c;
+ v[0].uv = src_rect.position;
+
+ // 0,1
+ v[1].v = Vector2(dst_rect.position.x, dst_rect.position.y + dst_rect.size.y);
+ v[1].c = c;
+ v[1].uv = Vector2(src_rect.position.x, src_rect.position.y + src_rect.size.y);
+
+ // 1,1
+ v[2].v = Vector2(dst_rect.position.x + dst_rect.size.x, dst_rect.position.y + dst_rect.size.y);
+ v[2].c = c;
+ v[2].uv = Vector2(src_rect.position.x + src_rect.size.x, src_rect.position.y + src_rect.size.y);
- state.canvas_shader.set_uniform(CanvasShaderGLES2::DST_RECT, Color(p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y));
- state.canvas_shader.set_uniform(CanvasShaderGLES2::SRC_RECT, Color(p_src.position.x, p_src.position.y, p_src.size.x, p_src.size.y));
+ // 1,0
+ v[3].v = Vector2(dst_rect.position.x + dst_rect.size.x, dst_rect.position.y);
+ v[3].c = c;
+ v[3].uv = Vector2(src_rect.position.x + src_rect.size.x, src_rect.position.y);
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ const int indices[p_index_count] = {
+ 0, 1, 2,
+ 2, 3, 0
+ };
+
+ _draw(GL_TRIANGLES, p_vertex_count, v, p_index_count, indices);
}
void RasterizerCanvasGLES2::draw_window_margins(int *black_margin, RID *black_image) {
@@ -1052,60 +1122,36 @@ void RasterizerCanvasGLES2::draw_window_margins(int *black_margin, RID *black_im
void RasterizerCanvasGLES2::initialize() {
- // quad buffer
- {
- glGenBuffers(1, &data.canvas_quad_vertices);
- glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices);
-
- const float qv[8] = {
- 0, 0,
- 0, 1,
- 1, 1,
- 1, 0
- };
-
- glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, qv, GL_STATIC_DRAW);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
// polygon buffer
{
uint32_t poly_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_buffer_size_kb", 128);
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);
+ glGenBuffers(1, &data.vertex_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, data.vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, poly_size, NULL, GL_DYNAMIC_DRAW);
- data.polygon_buffer_size = poly_size;
+ data.vertex_buffer_size = poly_size;
glBindBuffer(GL_ARRAY_BUFFER, 0);
uint32_t index_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_index_size_kb", 128);
index_size *= 1024; // kb
- glGenBuffers(1, &data.polygon_index_buffer);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer);
+ glGenBuffers(1, &data.index_buffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_size, NULL, GL_DYNAMIC_DRAW);
+
+ data.index_buffer_size = index_size;
+
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
// ninepatch buffers
{
// array buffer
- glGenBuffers(1, &data.ninepatch_vertices);
- glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices);
-
- glBufferData(GL_ARRAY_BUFFER, sizeof(float) * (16 + 16) * 2, NULL, GL_DYNAMIC_DRAW);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
-
- // element buffer
- glGenBuffers(1, &data.ninepatch_elements);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ninepatch_elements);
#define _EIDX(y, x) (y * 4 + x)
- uint8_t elems[3 * 2 * 9] = {
+ const int elems[3 * 2 * 9] = {
// first row
@@ -1149,14 +1195,24 @@ void RasterizerCanvasGLES2::initialize() {
;
#undef _EIDX
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elems), elems, GL_STATIC_DRAW);
+ memcpy(data.ninepatch_elements, elems, sizeof(elems));
+ }
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ {
+ const uint32_t size = data.vertex_buffer_size / sizeof(Vertex);
+ data.mem_vertex_buffer = (Vertex *)memalloc(sizeof(Vertex) * size);
+ data.mem_vertex_buffer_offset = 0;
+ data.mem_vertex_buffer_size = size;
}
- state.canvas_shader.init();
+ {
+ const uint32_t size = data.index_buffer_size / sizeof(int);
+ data.mem_index_buffer = (int *)memalloc(sizeof(int) * size);
+ data.mem_index_buffer_offset = 0;
+ data.mem_index_buffer_size = size;
+ }
- state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);
+ state.canvas_shader.init();
state.canvas_shader.bind();
}
@@ -1166,3 +1222,92 @@ void RasterizerCanvasGLES2::finalize() {
RasterizerCanvasGLES2::RasterizerCanvasGLES2() {
}
+
+void RasterizerCanvasGLES2::_begin(const GLuint p_primitive) {
+ if (data.primitive != p_primitive) {
+ _flush();
+ data.primitive = p_primitive;
+ }
+}
+
+void RasterizerCanvasGLES2::_prepare(const int p_vertex_count, const int p_index_count) {
+ if (data.mem_vertex_buffer_size - data.mem_vertex_buffer_offset < p_vertex_count ||
+ data.mem_index_buffer_size - data.mem_index_buffer_offset < p_index_count) {
+ _flush();
+ }
+}
+
+void RasterizerCanvasGLES2::_draw(const GLuint p_primitive, const int p_vertex_count, const Vertex *p_vertices, const int p_index_count, const int *p_indices) {
+ glBindBuffer(GL_ARRAY_BUFFER, data.vertex_buffer);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertex) * p_vertex_count, p_vertices);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.index_buffer);
+ glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(int) * p_index_count, p_indices);
+
+ glEnableVertexAttribArray(VS::ARRAY_VERTEX);
+ glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), NULL);
+
+ glEnableVertexAttribArray(VS::ARRAY_COLOR);
+ glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), ((uint8_t *)0) + sizeof(Vector2));
+
+ glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
+ glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), ((uint8_t *)0) + sizeof(Vector2) + sizeof(Color));
+
+ glDrawElements(p_primitive, p_index_count, GL_UNSIGNED_INT, 0);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+void RasterizerCanvasGLES2::_flush() {
+ if (data.mem_vertex_buffer_offset) {
+ _draw(data.primitive, data.mem_vertex_buffer_offset, data.mem_vertex_buffer, data.mem_index_buffer_offset, data.mem_index_buffer);
+ }
+
+ data.mem_vertex_buffer_offset = 0;
+ data.mem_index_buffer_offset = 0;
+}
+
+void RasterizerCanvasGLES2::_commit(const int p_vertex_count, const int p_index_count) {
+ ERR_FAIL_COND(!p_vertex_count);
+ ERR_FAIL_COND(!p_index_count);
+
+ if (state.uniforms.extra_matrix != state.prev_uniforms.extra_matrix ||
+ state.uniforms.final_modulate != state.prev_uniforms.final_modulate ||
+ state.uniforms.modelview_matrix != state.prev_uniforms.modelview_matrix ||
+ state.uniforms.projection_matrix != state.prev_uniforms.projection_matrix ||
+ state.uniforms.texpixel_size != state.prev_uniforms.texpixel_size ||
+ state.uniforms.time != state.prev_uniforms.time) {
+
+ _set_uniforms();
+ state.prev_uniforms = state.uniforms;
+ _flush();
+ }
+
+ const int new_index_offset = data.mem_index_buffer_offset + p_index_count;
+
+ for (int i = data.mem_index_buffer_offset; i < new_index_offset; ++i)
+ data.mem_index_buffer[i] += data.mem_vertex_buffer_offset;
+
+ data.mem_vertex_buffer_offset += p_vertex_count;
+ data.mem_index_buffer_offset = new_index_offset;
+}
+
+void RasterizerCanvasGLES2::_untile() {
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ state.tiled = false;
+}
+
+void RasterizerCanvasGLES2::_bind_shader(RasterizerStorageGLES2::Material *p_material) {
+ if (!state.canvas_shader.is_dirty()) {
+ return;
+ }
+
+ _flush();
+
+ if (state.canvas_shader.bind()) {
+ state.canvas_shader.use_material((void *)p_material);
+ }
+}
diff --git a/drivers/gles2/rasterizer_canvas_gles2.h b/drivers/gles2/rasterizer_canvas_gles2.h
index cda3ec79e7..d5a122e533 100644
--- a/drivers/gles2/rasterizer_canvas_gles2.h
+++ b/drivers/gles2/rasterizer_canvas_gles2.h
@@ -50,23 +50,44 @@ public:
Color final_modulate;
float time;
+
+ Size2 texpixel_size;
+ };
+
+ struct Vertex {
+ Vector2 v;
+ Color c;
+ Vector2 uv;
};
struct Data {
- GLuint canvas_quad_vertices;
- GLuint polygon_buffer;
- GLuint polygon_index_buffer;
+ GLuint vertex_buffer;
+ GLuint index_buffer;
+
+ uint32_t vertex_buffer_size;
+ uint32_t index_buffer_size;
+
+ int ninepatch_elements[3 * 2 * 9];
- uint32_t polygon_buffer_size;
+ int *mem_index_buffer;
+ uint32_t mem_index_buffer_offset;
+ uint32_t mem_index_buffer_size;
- GLuint ninepatch_vertices;
- GLuint ninepatch_elements;
+ Vertex *mem_vertex_buffer;
+ uint32_t mem_vertex_buffer_offset;
+ uint32_t mem_vertex_buffer_size;
+ GLuint primitive;
+ GLuint texture;
} data;
struct State {
Uniforms uniforms;
+ Uniforms prev_uniforms;
+
+ bool tiled;
+
bool canvas_texscreen_used;
CanvasShaderGLES2 canvas_shader;
// CanvasShadowShaderGLES3 canvas_shadow_shader;
@@ -99,9 +120,16 @@ public:
_FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false);
- _FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs);
_FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
- _FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
+
+ _FORCE_INLINE_ void _begin(const GLuint p_primitive);
+ _FORCE_INLINE_ void _prepare(const int p_vertex_count, const int p_index_count);
+ _FORCE_INLINE_ void _commit(const int p_vertex_count, const int p_index_count);
+
+ _FORCE_INLINE_ void _flush();
+ _FORCE_INLINE_ void _draw(const GLuint p_primitive, const int p_vertex_count, const Vertex *p_vertices, const int p_index_count, const int *p_indices);
+
+ _FORCE_INLINE_ void _untile();
_FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip, RasterizerStorageGLES2::Material *p_material);
_FORCE_INLINE_ void _copy_texscreen(const Rect2 &p_rect);
@@ -114,8 +142,8 @@ public:
virtual void reset_canvas();
RasterizerStorageGLES2::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map);
+ _FORCE_INLINE_ void _bind_shader(RasterizerStorageGLES2::Material *p_material);
- void _bind_quad_buffer();
void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src);
void initialize();
diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp
index a1a0b9e2c6..165ffc0412 100644
--- a/drivers/gles2/rasterizer_gles2.cpp
+++ b/drivers/gles2/rasterizer_gles2.cpp
@@ -345,9 +345,6 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re
RasterizerStorageGLES2::RenderTarget *rt = storage->render_target_owner.getornull(p_render_target);
ERR_FAIL_COND(!rt);
- canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true);
- canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false);
-
canvas->state.canvas_shader.set_custom_shader(0);
canvas->state.canvas_shader.bind();
@@ -359,7 +356,7 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re
// TODO normals
- canvas->draw_generic_textured_rect(p_screen_rect, Rect2(0, 0, 1, -1));
+ canvas->draw_generic_textured_rect(p_screen_rect, Rect2(0, 1, 1, -1));
glBindTexture(GL_TEXTURE_2D, 0);
canvas->canvas_end();
diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h
index c1fbf73254..38c0ccaac2 100644
--- a/drivers/gles2/rasterizer_storage_gles2.h
+++ b/drivers/gles2/rasterizer_storage_gles2.h
@@ -252,7 +252,7 @@ public:
int mipmaps;
bool active;
- GLenum tex_id;
+ GLuint tex_id;
uint16_t stored_cube_sides;
@@ -429,6 +429,8 @@ public:
bool uses_screen_texture;
bool uses_screen_uv;
bool uses_time;
+ bool uses_modelview_matrix;
+ bool uses_vertex;
} canvas_item;
diff --git a/drivers/gles2/shader_gles2.cpp b/drivers/gles2/shader_gles2.cpp
index e9b58cb272..89c1b6490d 100644
--- a/drivers/gles2/shader_gles2.cpp
+++ b/drivers/gles2/shader_gles2.cpp
@@ -122,13 +122,11 @@ GLint ShaderGLES2::get_uniform_location(int p_index) const {
}
bool ShaderGLES2::bind() {
-
- if (active != this || !version || new_conditional_version.key != conditional_version.key) {
- conditional_version = new_conditional_version;
- version = get_current_version();
- } else {
+ if (!is_dirty())
return false;
- }
+
+ conditional_version = new_conditional_version;
+ version = get_current_version();
ERR_FAIL_COND_V(!version, false);
@@ -1109,3 +1107,7 @@ ShaderGLES2::ShaderGLES2() {
ShaderGLES2::~ShaderGLES2() {
finish();
}
+
+bool ShaderGLES2::is_dirty() const {
+ return active != this || !version || new_conditional_version.key != conditional_version.key;
+}
diff --git a/drivers/gles2/shader_gles2.h b/drivers/gles2/shader_gles2.h
index cb515c199c..99513abfe9 100644
--- a/drivers/gles2/shader_gles2.h
+++ b/drivers/gles2/shader_gles2.h
@@ -208,6 +208,7 @@ public:
GLint get_uniform_location(int p_index) const;
static _FORCE_INLINE_ ShaderGLES2 *get_active() { return active; }
+ bool is_dirty() const;
bool bind();
void unbind();
void bind_uniforms();
diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl
index 29d81bb2c4..a63c7675d8 100644
--- a/drivers/gles2/shaders/canvas.glsl
+++ b/drivers/gles2/shaders/canvas.glsl
@@ -20,13 +20,6 @@ varying vec4 color_interp;
uniform highp vec2 color_texpixel_size;
-#ifdef USE_TEXTURE_RECT
-
-uniform vec4 dst_rect;
-uniform vec4 src_rect;
-
-#endif
-
uniform highp float time;
VERTEX_SHADER_GLOBALS
@@ -44,35 +37,9 @@ void main() {
vec4 color = color_attrib;
-#ifdef USE_TEXTURE_RECT
-
- if (dst_rect.z < 0.0) { // Transpose is encoded as negative dst_rect.z
- uv_interp = src_rect.xy + abs(src_rect.zw) * vertex.yx;
- } else {
- uv_interp = src_rect.xy + abs(src_rect.zw) * vertex;
- }
-
- vec4 outvec = vec4(0.0, 0.0, 0.0, 1.0);
-
- // This is what is done in the GLES 3 bindings and should
- // take care of flipped rects.
- //
- // But it doesn't.
- // I don't know why, will need to investigate further.
-
- outvec.xy = dst_rect.xy + abs(dst_rect.zw) * select(vertex, vec2(1.0, 1.0) - vertex, lessThan(src_rect.zw, vec2(0.0, 0.0)));
-
- // outvec.xy = dst_rect.xy + abs(dst_rect.zw) * vertex;
-#else
vec4 outvec = vec4(vertex.xy, 0.0, 1.0);
-#ifdef USE_UV_ATTRIBUTE
uv_interp = uv_attrib;
-#else
- uv_interp = vertex.xy;
-#endif
-
-#endif
{
vec2 src_vtx=outvec.xy;