diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index fb150d6820eb..4ca479342332 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -111,6 +111,8 @@ void RasterizerCanvasGLES2::canvas_begin() { void RasterizerCanvasGLES2::canvas_end() { + _flush(); + glBindBuffer(GL_ARRAY_BUFFER, 0); for (int i = 0; i < VS::ARRAY_MAX; i++) { @@ -170,589 +172,626 @@ RasterizerStorageGLES2::Texture *RasterizerCanvasGLES2::_bind_canvas_texture(con void RasterizerCanvasGLES2::_set_texture_rect_mode(bool p_enable, bool p_ninepatch) { } -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) { +bool RasterizerCanvasGLES2::_prepare_ninepatch(RasterizerCanvas::Item::CommandNinePatch *np) { + float buffer[16 * 2 + 16 * 2]; - glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); + if (data.polygon_buffer_size - data.polygon_mem_offset < sizeof(buffer)) + return false; - uint32_t buffer_ofs = 0; + RasterizerStorageGLES2::Texture *tex; - 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; + if (np->texture.is_valid()) + tex = storage->texture_owner.getornull(np->texture); + else + tex = NULL; - 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; + if (!tex) { + print_line("TODO: ninepatch without texture"); + return true; } - 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; - } else { - glDisableVertexAttribArray(VS::ARRAY_TEX_UV); - } + RenderItem::NinepatchCommand *cmd = render_commands.allocate(); + if (!cmd) + return false; - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(int) * p_index_count, p_indices); + Size2 texpixel_size(1.0 / tex->width, 1.0 / tex->height); - glDrawElements(GL_TRIANGLES, p_index_count, GL_UNSIGNED_INT, 0); + { - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -} + // first row -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) { + buffer[(0 * 4 * 4) + 0] = np->rect.position.x; + buffer[(0 * 4 * 4) + 1] = np->rect.position.y; - glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); + buffer[(0 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; + buffer[(0 * 4 * 4) + 3] = np->source.position.y * texpixel_size.y; - uint32_t buffer_ofs = 0; + buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; + buffer[(0 * 4 * 4) + 5] = np->rect.position.y; - 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; + buffer[(0 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(0 * 4 * 4) + 7] = np->source.position.y * texpixel_size.y; - 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; - } + 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; - 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); - } + buffer[(0 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(0 * 4 * 4) + 11] = np->source.position.y * texpixel_size.y; - glDrawArrays(p_primitive, 0, p_vertex_count); + buffer[(0 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; + buffer[(0 * 4 * 4) + 13] = np->rect.position.y; - glBindBuffer(GL_ARRAY_BUFFER, 0); -} + buffer[(0 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; + buffer[(0 * 4 * 4) + 15] = np->source.position.y * texpixel_size.y; -void RasterizerCanvasGLES2::_draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs) { + // second row - static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN }; + buffer[(1 * 4 * 4) + 0] = np->rect.position.x; + buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP]; - int color_offset = 0; - int uv_offset = 0; - int stride = 2; + buffer[(1 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; + buffer[(1 * 4 * 4) + 3] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; - if (p_colors) { - color_offset = stride; - stride += 4; - } + 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]; - if (p_uvs) { - uv_offset = stride; - stride += 2; - } + buffer[(1 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(1 * 4 * 4) + 7] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; - float buffer_data[(2 + 2 + 4) * 4]; + 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]; - 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; - } + buffer[(1 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(1 * 4 * 4) + 11] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; - 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; - } - } + 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]; - 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; - } - } + buffer[(1 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; + buffer[(1 * 4 * 4) + 15] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; - glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - glBufferSubData(GL_ARRAY_BUFFER, 0, p_points * stride * 4 * sizeof(float), buffer_data); + // thrid row - glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride * sizeof(float), NULL); + 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]; - 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); - } + buffer[(2 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; + buffer[(2 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; - 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); - } + 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]; - glDrawArrays(prim[p_points], 0, p_points); + buffer[(2 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(2 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; - glBindBuffer(GL_ARRAY_BUFFER, 0); -} + 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]; -void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip) { + buffer[(2 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(2 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; - int command_count = p_item->commands.size(); - Item::Command **commands = p_item->commands.ptrw(); + 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]; - for (int i = 0; i < command_count; i++) { + buffer[(2 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; + buffer[(2 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; - Item::Command *command = commands[i]; + // fourth row - switch (command->type) { + buffer[(3 * 4 * 4) + 0] = np->rect.position.x; + buffer[(3 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y; - case Item::Command::TYPE_LINE: { + buffer[(3 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; + buffer[(3 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y) * texpixel_size.y; - Item::CommandLine *line = static_cast(command); + 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; - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - state.canvas_shader.bind(); + buffer[(3 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; + buffer[(3 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y) * texpixel_size.y; - _set_uniforms(); + 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; - _bind_canvas_texture(RID(), RID()); + buffer[(3 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; + buffer[(3 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y) * texpixel_size.y; - glDisableVertexAttribArray(VS::ARRAY_COLOR); - glVertexAttrib4fv(VS::ARRAY_COLOR, line->color.components); + 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; - state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); + buffer[(3 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; + buffer[(3 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y) * texpixel_size.y; - if (line->width <= 1) { - Vector2 verts[2] = { - Vector2(line->from.x, line->from.y), - Vector2(line->to.x, line->to.y) - }; + // print_line(String::num((np->source.position.y + np->source.size.y) * texpixel_size.y)); + } - _draw_gui_primitive(2, verts, NULL, NULL); - } else { - Vector2 t = (line->from - line->to).normalized().tangent() * line->width * 0.5; + cmd->command = np; + cmd->vertices_offset = data.polygon_mem_offset; - Vector2 verts[4] = { - line->from - t, - line->from + t, - line->to + t, - line->to - t - }; + memcpy(data.polygon_mem_buffer + data.polygon_mem_offset, buffer, sizeof(buffer)); + data.polygon_mem_offset += sizeof(buffer); - _draw_gui_primitive(4, verts, NULL, NULL); - } + return true; +} - } break; +bool RasterizerCanvasGLES2::_prepare_polygon(RasterizerCanvas::Item::Command *command, 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) { + const uint32_t indices_size = sizeof(int) * p_index_count; + if (indices_size > data.polygon_index_buffer_size - data.polygon_index_mem_offset) + return false; - case Item::Command::TYPE_RECT: { + const uint32_t vertices_size = sizeof(Vector2) * p_vertex_count; - Item::CommandRect *r = static_cast(command); + uint32_t colors_size; - glDisableVertexAttribArray(VS::ARRAY_COLOR); - glVertexAttrib4fv(VS::ARRAY_COLOR, r->modulate.components); + const bool use_colors = !p_singlecolor && p_colors; - _bind_quad_buffer(); + if (use_colors) + colors_size = sizeof(Color) * p_vertex_count; + else + colors_size = 0; - 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(); + uint32_t uvs_size; - RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(r->texture, r->normal_map); + if (p_uvs) + uvs_size = sizeof(Vector2) * p_vertex_count; + else + uvs_size = 0; - if (!tex) { - Rect2 dst_rect = Rect2(r->rect.position, r->rect.size); + if (vertices_size + colors_size + uvs_size > data.polygon_buffer_size - data.polygon_mem_offset) + return false; - if (dst_rect.size.width < 0) { - dst_rect.position.x += dst_rect.size.width; - dst_rect.size.width *= -1; - } - if (dst_rect.size.height < 0) { - dst_rect.position.y += dst_rect.size.height; - dst_rect.size.height *= -1; - } + RenderItem::PolygonCommand *cmd = render_commands.allocate(); + if (!cmd) + return false; - 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)); + cmd->command = command; - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } else { + cmd->count = p_index_count; + cmd->color = p_colors ? *p_colors : Color(1, 1, 1, 1); - bool untile = false; + cmd->vertices_offset = data.polygon_mem_offset; - 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; - } + memcpy(data.polygon_mem_buffer + data.polygon_mem_offset, p_vertices, vertices_size); + data.polygon_mem_offset += vertices_size; - 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); + cmd->use_colors = use_colors; + cmd->colors_offset = data.polygon_mem_offset; - Rect2 dst_rect = Rect2(r->rect.position, r->rect.size); + memcpy(data.polygon_mem_buffer + data.polygon_mem_offset, p_colors, colors_size); + data.polygon_mem_offset += colors_size; - if (dst_rect.size.width < 0) { - dst_rect.position.x += dst_rect.size.width; - dst_rect.size.width *= -1; - } - if (dst_rect.size.height < 0) { - dst_rect.position.y += dst_rect.size.height; - dst_rect.size.height *= -1; - } + cmd->use_uvs = p_uvs; + cmd->uvs_offset = data.polygon_mem_offset; - if (r->flags & CANVAS_RECT_FLIP_H) { - src_rect.size.x *= -1; - } + memcpy(data.polygon_mem_buffer + data.polygon_mem_offset, p_uvs, uvs_size); + data.polygon_mem_offset += uvs_size; - if (r->flags & CANVAS_RECT_FLIP_V) { - src_rect.size.y *= -1; - } + cmd->indices_offset = data.polygon_index_mem_offset; - if (r->flags & CANVAS_RECT_TRANSPOSE) { - dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform - } + memcpy(data.polygon_index_mem_buffer + data.polygon_index_mem_offset, p_indices, indices_size); + data.polygon_index_mem_offset += indices_size; - state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); + return true; +} - 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)); +void RasterizerCanvasGLES2::_flush_rects(const RenderItem::RectsCommand *rect) { + if (rect->use_texture) { + RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(rect->texture, RID()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + if (tex) { + Size2 texpixel_size(1.0 / tex->width, 1.0 / tex->height); + state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); + } + } + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT_ATTRIB, true); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - 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); - } - } + if (state.canvas_shader.bind()) + _set_uniforms(); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - } break; + glEnableVertexAttribArray(VS::ARRAY_COLOR); + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + glEnableVertexAttribArray(VS::ARRAY_TEX_UV2); + + const uint32_t qv_size = sizeof(float) * 2; + const uint32_t rect_size = sizeof(float) * 4; + const uint32_t colors_size = sizeof(float) * 4; + const uint32_t stride = qv_size + colors_size + rect_size * 2; + + glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, stride, (uint8_t *)0 + rect->vertices_offset); + glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, stride, ((uint8_t *)0) + rect->vertices_offset + qv_size); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 4, GL_FLOAT, GL_FALSE, stride, (uint8_t *)0 + rect->vertices_offset + qv_size + colors_size); + glVertexAttribPointer(VS::ARRAY_TEX_UV2, 4, GL_FLOAT, GL_FALSE, stride, (uint8_t *)0 + rect->vertices_offset + qv_size + colors_size + rect_size); + + if (rect->untile) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } - case Item::Command::TYPE_NINEPATCH: { + glDrawArrays(GL_TRIANGLES, 0, rect->count); + ++storage->info.render.draw_call_count; - Item::CommandNinePatch *np = static_cast(command); + if (rect->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.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(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} - glDisableVertexAttribArray(VS::ARRAY_COLOR); - glVertexAttrib4fv(VS::ARRAY_COLOR, np->color.components); +void RasterizerCanvasGLES2::_flush_polygon(const RenderItem::PolygonCommand *cmd) { + switch (cmd->command->type) { + case Item::Command::TYPE_POLYGON: { + Item::CommandPolygon *polygon = static_cast(cmd->command); - RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(np->texture, np->normal_map); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT_ATTRIB, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); - if (!tex) { - print_line("TODO: ninepatch without texture"); - continue; - } + if (state.canvas_shader.bind()) + _set_uniforms(); - Size2 texpixel_size(1.0 / tex->width, 1.0 / tex->height); + RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(polygon->texture, polygon->normal_map); - // state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); + if (texture) { + Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); + } + } break; + case Item::Command::TYPE_CIRCLE: { + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT_ATTRIB, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - // prepare vertex buffer + if (state.canvas_shader.bind()) + _set_uniforms(); - float buffer[16 * 2 + 16 * 2]; + _bind_canvas_texture(RID(), RID()); + } break; + } - { + glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - // first row + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)0) + cmd->vertices_offset); - buffer[(0 * 4 * 4) + 0] = np->rect.position.x; - buffer[(0 * 4 * 4) + 1] = np->rect.position.y; + if (cmd->use_colors) { + glEnableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), ((uint8_t *)0) + cmd->colors_offset); + } else { + glDisableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttrib4f(VS::ARRAY_COLOR, cmd->color.r, cmd->color.g, cmd->color.b, cmd->color.a); + } - buffer[(0 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(0 * 4 * 4) + 3] = np->source.position.y * texpixel_size.y; + if (cmd->use_uvs) { + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)0) + cmd->uvs_offset); + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + } - buffer[(0 * 4 * 4) + 4] = np->rect.position.x + np->margin[MARGIN_LEFT]; - buffer[(0 * 4 * 4) + 5] = np->rect.position.y; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); - buffer[(0 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(0 * 4 * 4) + 7] = np->source.position.y * texpixel_size.y; + glDrawElements(GL_TRIANGLES, cmd->count, GL_UNSIGNED_INT, ((uint8_t *)0) + cmd->indices_offset); + ++storage->info.render.draw_call_count; - 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; + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} - buffer[(0 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(0 * 4 * 4) + 11] = np->source.position.y * texpixel_size.y; +bool RasterizerCanvasGLES2::_prepare_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) { + const uint32_t vertices_size = sizeof(Vector2) * p_vertex_count; - buffer[(0 * 4 * 4) + 12] = np->rect.position.x + np->rect.size.x; - buffer[(0 * 4 * 4) + 13] = np->rect.position.y; + uint32_t colors_size; - buffer[(0 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(0 * 4 * 4) + 15] = np->source.position.y * texpixel_size.y; + const bool use_colors = !p_singlecolor && p_colors; - // second row + if (use_colors) + colors_size = sizeof(Color) * p_vertex_count; + else + colors_size = 0; - buffer[(1 * 4 * 4) + 0] = np->rect.position.x; - buffer[(1 * 4 * 4) + 1] = np->rect.position.y + np->margin[MARGIN_TOP]; + uint32_t uvs_size; - buffer[(1 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(1 * 4 * 4) + 3] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + if (p_uvs) + uvs_size = sizeof(Vector2) * p_vertex_count; + else + uvs_size = 0; - 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]; + if (vertices_size + colors_size + uvs_size > data.polygon_buffer_size - data.polygon_mem_offset) + return false; - buffer[(1 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(1 * 4 * 4) + 7] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + RenderItem::GenericCommand *cmd = render_commands.allocate(); + if (!cmd) + return false; - 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]; + cmd->primitive = p_primitive; + cmd->count = p_vertex_count; - buffer[(1 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(1 * 4 * 4) + 11] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + cmd->color = p_colors ? *p_colors : Color(1, 1, 1, 1); - 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]; + cmd->vertices_offset = data.polygon_mem_offset; - buffer[(1 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(1 * 4 * 4) + 15] = (np->source.position.y + np->margin[MARGIN_TOP]) * texpixel_size.y; + memcpy(data.polygon_mem_buffer + data.polygon_mem_offset, p_vertices, vertices_size); + data.polygon_mem_offset += vertices_size; - // thrid row + cmd->use_colors = use_colors; + cmd->colors_offset = data.polygon_mem_offset; - 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]; + memcpy(data.polygon_mem_buffer + data.polygon_mem_offset, p_colors, colors_size); + data.polygon_mem_offset += colors_size; - buffer[(2 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(2 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + cmd->use_uvs = p_uvs; + cmd->uvs_offset = data.polygon_mem_offset; - 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]; + memcpy(data.polygon_mem_buffer + data.polygon_mem_offset, p_uvs, uvs_size); + data.polygon_mem_offset += uvs_size; - buffer[(2 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(2 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + return true; +} - 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]; +void RasterizerCanvasGLES2::_flush_generic(const RenderItem::GenericCommand *cmd) { - buffer[(2 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(2 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT_ATTRIB, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); - 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]; + if (state.canvas_shader.bind()) + _set_uniforms(); - buffer[(2 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(2 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y - np->margin[MARGIN_BOTTOM]) * texpixel_size.y; + _bind_canvas_texture(RID(), RID()); - // fourth row + glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - buffer[(3 * 4 * 4) + 0] = np->rect.position.x; - buffer[(3 * 4 * 4) + 1] = np->rect.position.y + np->rect.size.y; + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)0) + cmd->vertices_offset); - buffer[(3 * 4 * 4) + 2] = np->source.position.x * texpixel_size.x; - buffer[(3 * 4 * 4) + 3] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + if (cmd->use_colors) { + glEnableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), ((uint8_t *)0) + cmd->colors_offset); + } else { + glDisableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttrib4f(VS::ARRAY_COLOR, cmd->color.r, cmd->color.g, cmd->color.b, cmd->color.a); + } - 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; + if (cmd->use_uvs) { + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), ((uint8_t *)0) + cmd->uvs_offset); + } else { + glDisableVertexAttribArray(VS::ARRAY_TEX_UV); + } - buffer[(3 * 4 * 4) + 6] = (np->source.position.x + np->margin[MARGIN_LEFT]) * texpixel_size.x; - buffer[(3 * 4 * 4) + 7] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + glDrawArrays(cmd->primitive, 0, cmd->count); + ++storage->info.render.draw_call_count; - 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; + glBindBuffer(GL_ARRAY_BUFFER, 0); +} - buffer[(3 * 4 * 4) + 10] = (np->source.position.x + np->source.size.x - np->margin[MARGIN_RIGHT]) * texpixel_size.x; - buffer[(3 * 4 * 4) + 11] = (np->source.position.y + np->source.size.y) * texpixel_size.y; +void RasterizerCanvasGLES2::_flush_ninepatch(const RenderItem::NinepatchCommand *cmd) { + Item::CommandNinePatch *np = cmd->command; - 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; + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT_ATTRIB, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); - buffer[(3 * 4 * 4) + 14] = (np->source.position.x + np->source.size.x) * texpixel_size.x; - buffer[(3 * 4 * 4) + 15] = (np->source.position.y + np->source.size.y) * texpixel_size.y; + if (state.canvas_shader.bind()) + _set_uniforms(); - // print_line(String::num((np->source.position.y + np->source.size.y) * texpixel_size.y)); - } + glDisableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttrib4fv(VS::ARRAY_COLOR, np->color.components); - glBindBuffer(GL_ARRAY_BUFFER, data.ninepatch_vertices); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * (16 + 16) * 2, buffer); + RasterizerStorageGLES2::Texture *tex = _bind_canvas_texture(np->texture, np->normal_map); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ninepatch_elements); + if (!tex) { + print_line("TODO: ninepatch without texture"); + return; + } - glEnableVertexAttribArray(VS::ARRAY_VERTEX); - glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + Size2 texpixel_size(1.0 / tex->width, 1.0 / tex->height); - 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)); + // state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); + state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); - glDrawElements(GL_TRIANGLES, 18 * 3 - (np->draw_center ? 0 : 6), GL_UNSIGNED_BYTE, NULL); + glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ninepatch_elements); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glEnableVertexAttribArray(VS::ARRAY_VERTEX); + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); - } break; + glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (uint8_t *)0 + cmd->vertices_offset); + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (uint8_t *)0 + cmd->vertices_offset + sizeof(float) * 2); - case Item::Command::TYPE_CIRCLE: { + glDrawElements(GL_TRIANGLES, 18 * 3 - (np->draw_center ? 0 : 6), GL_UNSIGNED_BYTE, NULL); + ++storage->info.render.draw_call_count; - Item::CommandCircle *circle = static_cast(command); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); +bool RasterizerCanvasGLES2::_prepare_gui_primitive(RasterizerCanvas::Item::Command *command, int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs) { + int color_offset = 0; + int uv_offset = 0; + int stride = 2; - if (state.canvas_shader.bind()) - _set_uniforms(); + if (p_colors) { + color_offset = stride; + stride += 4; + } - static const int num_points = 32; + if (p_uvs) { + uv_offset = stride; + stride += 2; + } - Vector2 points[num_points + 1]; - points[num_points] = circle->pos; + const uint32_t size = p_points * stride * 4 * sizeof(float); - int indices[num_points * 3]; + if (size > data.polygon_buffer_size - data.polygon_mem_offset) + return false; - 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; - } + RenderItem::GuiPrimitiveCommand *cmd = render_commands.allocate(); + if (!cmd) + return false; - _bind_canvas_texture(RID(), RID()); + cmd->command = command; - _draw_polygon(indices, num_points * 3, num_points + 1, points, NULL, &circle->color, true); - } break; + cmd->count = p_points; - case Item::Command::TYPE_POLYGON: { + cmd->offset = data.polygon_mem_offset; - Item::CommandPolygon *polygon = static_cast(command); + cmd->use_colors = p_colors; + cmd->color_offset = data.polygon_mem_offset + color_offset * sizeof(float); - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); + cmd->use_uvs = p_uvs; + cmd->uvs_offset = data.polygon_mem_offset + uv_offset * sizeof(float); + cmd->stride = stride * sizeof(float); - if (state.canvas_shader.bind()) - _set_uniforms(); + float buffer_data[(2 + 2 + 4) * 4]; - RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(polygon->texture, polygon->normal_map); + 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; + } - if (texture) { - Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); - state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); - } + 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; + } + } - _draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1); - } break; + 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; + } + } - case Item::Command::TYPE_POLYLINE: { - Item::CommandPolyLine *pline = static_cast(command); + memcpy(data.polygon_mem_buffer + data.polygon_mem_offset, buffer_data, size); + data.polygon_mem_offset += size; - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); + return true; +} - if (state.canvas_shader.bind()) - _set_uniforms(); +void RasterizerCanvasGLES2::_flush_gui_primitive(const RenderItem::GuiPrimitiveCommand *cmd) { - _bind_canvas_texture(RID(), RID()); + static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLE_FAN }; - 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; + switch (cmd->command->type) { + case Item::Command::TYPE_LINE: { + Item::CommandLine *line = static_cast(cmd->command); - 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; - } - } else { - _draw_generic(GL_LINES, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1); - } - } - } break; + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT_ATTRIB, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); + state.canvas_shader.bind(); - case Item::Command::TYPE_PRIMITIVE: { + _set_uniforms(); - Item::CommandPrimitive *primitive = static_cast(command); - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); - state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); + _bind_canvas_texture(RID(), RID()); - if (state.canvas_shader.bind()) - _set_uniforms(); + glDisableVertexAttribArray(VS::ARRAY_COLOR); + glVertexAttrib4fv(VS::ARRAY_COLOR, line->color.components); - ERR_CONTINUE(primitive->points.size() < 1); + state.canvas_shader.set_uniform(CanvasShaderGLES2::MODELVIEW_MATRIX, state.uniforms.modelview_matrix); + } break; + case Item::Command::TYPE_PRIMITIVE: { + Item::CommandPrimitive *primitive = static_cast(cmd->command); - RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(primitive->texture, primitive->normal_map); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT_ATTRIB, false); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, true); - if (texture) { - Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); - state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); - } + if (state.canvas_shader.bind()) + _set_uniforms(); - 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); - } else if (primitive->colors.empty()) { - glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); - } + ERR_BREAK(primitive->points.size() < 1); - _draw_gui_primitive(primitive->points.size(), primitive->points.ptr(), primitive->colors.ptr(), primitive->uvs.ptr()); - } break; + RasterizerStorageGLES2::Texture *texture = _bind_canvas_texture(primitive->texture, primitive->normal_map); - case Item::Command::TYPE_TRANSFORM: { - Item::CommandTransform *transform = static_cast(command); - state.uniforms.extra_matrix = transform->xform; - state.canvas_shader.set_uniform(CanvasShaderGLES2::EXTRA_MATRIX, state.uniforms.extra_matrix); - } break; + if (texture) { + Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height); + state.canvas_shader.set_uniform(CanvasShaderGLES2::COLOR_TEXPIXEL_SIZE, texpixel_size); + } - case Item::Command::TYPE_PARTICLES: { + 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); + } else if (primitive->colors.empty()) { + glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); + } + } break; + } - } break; + glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); - case Item::Command::TYPE_CLIP_IGNORE: { + glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, cmd->stride, (uint8_t *)0 + cmd->offset); - Item::CommandClipIgnore *ci = static_cast(command); - if (current_clip) { - if (ci->ignore != reclip) { - if (ci->ignore) { - glDisable(GL_SCISSOR_TEST); - reclip = true; - } else { - glEnable(GL_SCISSOR_TEST); + if (cmd->use_colors) { + glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, cmd->stride, (uint8_t *)0 + cmd->color_offset); + glEnableVertexAttribArray(VS::ARRAY_COLOR); + } - int x = current_clip->final_clip_rect.position.x; - int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y); - int w = current_clip->final_clip_rect.size.x; - int h = current_clip->final_clip_rect.size.y; + if (cmd->use_uvs) { + glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, cmd->stride, (uint8_t *)0 + cmd->uvs_offset); + glEnableVertexAttribArray(VS::ARRAY_TEX_UV); + } - if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP]) - y = current_clip->final_clip_rect.position.y; + glDrawArrays(prim[cmd->count], 0, cmd->count); + ++storage->info.render.draw_call_count; - glScissor(x, y, w, h); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} - reclip = false; - } +void RasterizerCanvasGLES2::_canvas_render_command(Item::Command *command, Item *current_clip, bool &reclip) { + switch (command->type) { + case Item::Command::TYPE_TRANSFORM: { + Item::CommandTransform *transform = static_cast(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: { + + } break; + + case Item::Command::TYPE_CLIP_IGNORE: { + + Item::CommandClipIgnore *ci = static_cast(command); + if (current_clip) { + if (ci->ignore != reclip) { + if (ci->ignore) { + glDisable(GL_SCISSOR_TEST); + reclip = true; + } else { + glEnable(GL_SCISSOR_TEST); + + int x = current_clip->final_clip_rect.position.x; + int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y); + int w = current_clip->final_clip_rect.size.x; + int h = current_clip->final_clip_rect.size.y; + + if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP]) + y = current_clip->final_clip_rect.position.y; + + glScissor(x, y, w, h); + + reclip = false; } } + } - } break; + } break; - default: { - print_line("other"); - } break; - } + default: { + print_line("other"); + } break; } } @@ -797,204 +836,494 @@ void RasterizerCanvasGLES2::_copy_texscreen(const Rect2 &p_rect) { */ } -void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) { +void RasterizerCanvasGLES2::_canvas_item_process(Item *p_item) { - Item *current_clip = NULL; + Item::Command **commands = p_item->commands.ptrw(); + bool prepared = true; - RasterizerStorageGLES2::Shader *shader_cache = NULL; + int i = 0; + const int size = p_item->commands.size(); - bool rebind_shader = true; + while (i < size) { + Item::Command *command = commands[i]; - Size2 rt_size = Size2(storage->frame.current_rt->width, storage->frame.current_rt->height); + switch (command->type) { + case Item::Command::TYPE_RECT: { + Item::CommandRect *r = static_cast(command); - state.current_tex = RID(); - state.current_tex_ptr = NULL; - state.current_normal = RID(); + RasterizerStorageGLES2::Texture *texture = storage->texture_owner.getornull(r->texture); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + Size2 texpixel_size; + if (texture) + texpixel_size = Size2(1.0 / texture->width, 1.0 / texture->height); - int last_blend_mode = -1; + RenderItem::RectsCommand *rcmd = push_command(); - RID canvas_last_material = RID(); + rcmd->vertices_offset = data.polygon_mem_offset; + rcmd->count = 0; + rcmd->untile = r->flags & CANVAS_RECT_TILE && !(texture->flags & VS::TEXTURE_FLAG_REPEAT); + rcmd->texture = r->texture; + rcmd->use_texture = true; - while (p_item_list) { + while (true) { + Rect2 dst_rect; + Rect2 src_rect; + + if (!texture) { + dst_rect = Rect2(r->rect.position, r->rect.size); + + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } - Item *ci = p_item_list; + src_rect = Rect2(0, 0, 1, 1); + } else { + 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 (current_clip != ci->final_clip_owner) { + if (dst_rect.size.width < 0) { + dst_rect.position.x += dst_rect.size.width; + dst_rect.size.width *= -1; + } + if (dst_rect.size.height < 0) { + dst_rect.position.y += dst_rect.size.height; + dst_rect.size.height *= -1; + } - current_clip = ci->final_clip_owner; + if (r->flags & CANVAS_RECT_FLIP_H) { + src_rect.size.x *= -1; + } - if (current_clip) { - glEnable(GL_SCISSOR_TEST); - int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y); - if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP]) - y = current_clip->final_clip_rect.position.y; - glScissor(current_clip->final_clip_rect.position.x, y, current_clip->final_clip_rect.size.width, current_clip->final_clip_rect.size.height); - } else { - glDisable(GL_SCISSOR_TEST); - } - } + if (r->flags & CANVAS_RECT_FLIP_V) { + src_rect.size.y *= -1; + } - // TODO: copy back buffer + if (r->flags & CANVAS_RECT_TRANSPOSE) { + dst_rect.size.x *= -1; // Encoding in the dst_rect.z uniform + } + } - if (ci->copy_back_buffer) { - if (ci->copy_back_buffer->full) { - _copy_texscreen(Rect2()); - } else { - _copy_texscreen(ci->copy_back_buffer->rect); - } - } + struct Vertex { + Vector2 position; + Color color; + Rect2 src; + Rect2 dest; + }; - Item *material_owner = ci->material_owner ? ci->material_owner : ci; + const uint8_t count = 6; - RID material = material_owner->material; + Vertex vertices[count] = { + { Vector2(0, 0), r->modulate, src_rect, dst_rect }, + { Vector2(0, 1), r->modulate, src_rect, dst_rect }, + { Vector2(1, 1), r->modulate, src_rect, dst_rect }, + { Vector2(1, 1), r->modulate, src_rect, dst_rect }, + { Vector2(1, 0), r->modulate, src_rect, dst_rect }, + { Vector2(0, 0), r->modulate, src_rect, dst_rect }, + }; + + if (data.polygon_buffer_size - data.polygon_mem_offset < sizeof(vertices)) { + RenderItem::RectsCommand cmddata = *rcmd; + _flush(); + + rcmd = push_command(); + *rcmd = cmddata; + + rcmd->vertices_offset = data.polygon_mem_offset; + rcmd->count = 0; + rcmd->use_texture = false; + } + + memcpy(data.polygon_mem_buffer + data.polygon_mem_offset, vertices, sizeof(vertices)); + data.polygon_mem_offset += sizeof(vertices); + + rcmd->count += count; - if (material != canvas_last_material || rebind_shader) { + if (++i >= size) + break; - RasterizerStorageGLES2::Material *material_ptr = storage->material_owner.getornull(material); - RasterizerStorageGLES2::Shader *shader_ptr = NULL; + command = commands[i]; + if (command->type != Item::Command::TYPE_RECT) + break; - if (material_ptr) { - shader_ptr = material_ptr->shader; + Item::CommandRect *next_r = static_cast(command); - if (shader_ptr && shader_ptr->mode != VS::SHADER_CANVAS_ITEM) { - shader_ptr = NULL; // not a canvas item shader, don't use. + if (next_r->flags != r->flags) + break; + + if (next_r->texture != rcmd->texture) + break; + + r = static_cast(command); } - } - if (shader_ptr) { - if (shader_ptr->canvas_item.uses_screen_texture) { - _copy_texscreen(Rect2()); + continue; + } break; + case Item::Command::TYPE_CIRCLE: { + + Item::CommandCircle *circle = static_cast(command); + + static const int num_points = 32; + + Vector2 points[num_points + 1]; + points[num_points] = circle->pos; + + int indices[num_points * 3]; + + 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 (shader_ptr != shader_cache) { + prepared = _prepare_polygon(command, indices, num_points * 3, num_points + 1, points, NULL, &circle->color, true); + } break; + case Item::Command::TYPE_POLYGON: { + Item::CommandPolygon *polygon = static_cast(command); + prepared = _prepare_polygon(command, polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1); + } break; + case Item::Command::TYPE_NINEPATCH: { + Item::CommandNinePatch *ninepatch = static_cast(command); + prepared = _prepare_ninepatch(ninepatch); + } break; + case Item::Command::TYPE_POLYLINE: { + Item::CommandPolyLine *pline = static_cast(command); - if (shader_ptr->canvas_item.uses_time) { - VisualServerRaster::redraw_request(); - } + if (pline->triangles.size()) { + prepared = _prepare_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 offset = 0; - state.canvas_shader.set_custom_shader(shader_ptr->custom_code_id); - state.canvas_shader.bind(); + while (todo) { + const int max_current = (data.polygon_buffer_size - data.polygon_mem_offset) / (sizeof(real_t) * 4); + const int to_draw = MIN(max_current, todo); + + prepared = _prepare_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); + + if (!prepared) { + _flush(); + continue; + } + + todo -= to_draw; + offset += to_draw * 2; + } + } else + prepared = _prepare_generic(GL_LINES, pline->lines.size(), pline->lines.ptr(), NULL, pline->line_colors.ptr(), pline->line_colors.size() == 1); } + } break; + case Item::Command::TYPE_LINE: { - int tc = material_ptr->textures.size(); - RID *textures = material_ptr->textures.ptrw(); + Item::CommandLine *line = static_cast(command); - ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = shader_ptr->texture_hints.ptrw(); + if (line->width <= 1) { + Vector2 verts[2] = { + Vector2(line->from.x, line->from.y), + Vector2(line->to.x, line->to.y) + }; - for (int i = 0; i < tc; i++) { + prepared = _prepare_gui_primitive(command, 2, verts, NULL, NULL); + } else { + Vector2 t = (line->from - line->to).normalized().tangent() * line->width * 0.5; - glActiveTexture(GL_TEXTURE2 + i); + Vector2 verts[4] = { + line->from - t, + line->from + t, + line->to + t, + line->to - t + }; - RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i]); + prepared = _prepare_gui_primitive(command, 4, verts, NULL, NULL); + } + } break; + case Item::Command::TYPE_PRIMITIVE: { + Item::CommandPrimitive *primitive = static_cast(command); - if (!t) { + ERR_CONTINUE(primitive->points.size() < 1); - switch (texture_hints[i]) { - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: - case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { - glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: { - glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex); - } break; - case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { - glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); - } break; - default: { - glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); - } break; - } + prepared = _prepare_gui_primitive(command, primitive->points.size(), primitive->points.ptr(), primitive->colors.ptr(), primitive->uvs.ptr()); + } break; + default: { + RenderItem::PassThroughCommand *cmd = render_commands.allocate(); + prepared = cmd; - continue; - } + if (prepared) + cmd->command = command; + } break; + } + + if (!prepared) + _flush(); + else + ++i; + } +} + +void RasterizerCanvasGLES2::_flush() { + if (render_commands.empty()) + return; + + glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); + + glBufferSubData(GL_ARRAY_BUFFER, 0, data.polygon_mem_offset, data.polygon_mem_buffer); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, data.polygon_index_mem_offset, data.polygon_index_mem_buffer); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + data.polygon_mem_offset = 0; + data.polygon_index_mem_offset = 0; + + RenderItem::CommandBase *cmd; + for (RenderCommands::Ptr cmds = render_commands.ptr(); cmd = cmds.current(); cmds.next()) { + switch (cmd->type) { + case RenderItem::TYPE_BEGINLIST: { + RenderItem::BeginListCommand *begin = static_cast(cmd); + + state.current_clip = NULL; + state.shader_cache = NULL; + state.rebind_shader = true; + state.rt_size = Size2(storage->frame.current_rt->width, storage->frame.current_rt->height); + state.current_tex = RID(); + state.current_tex_ptr = NULL; + state.current_normal = RID(); + state.last_blend_mode = -1; + state.canvas_last_material = RID(); + state.p_modulate = begin->p_modulate; + state.reclip = false; + + data.polygon_mem_offset = 0; + data.polygon_index_mem_offset = 0; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + } break; + case RenderItem::TYPE_ENDLIST: { + if (state.current_clip) { + glDisable(GL_SCISSOR_TEST); + } + } break; + case RenderItem::TYPE_PASSTHROUGH: { + RenderItem::PassThroughCommand *passthrough = static_cast(cmd); + _canvas_render_command(passthrough->command, NULL, state.reclip); + } break; + case RenderItem::TYPE_RECTS: { + RenderItem::RectsCommand *rects = static_cast(cmd); + _flush_rects(rects); + } break; + case RenderItem::TYPE_POLYGON: { + RenderItem::PolygonCommand *polygon = static_cast(cmd); + _flush_polygon(polygon); + } break; + case RenderItem::TYPE_NINEPATCH: { + RenderItem::NinepatchCommand *ninepatch = static_cast(cmd); + _flush_ninepatch(ninepatch); + } break; + case RenderItem::TYPE_GENERIC: { + RenderItem::GenericCommand *generic = static_cast(cmd); + _flush_generic(generic); + } break; + case RenderItem::TYPE_GUI_PRIMITIVE: { + RenderItem::GuiPrimitiveCommand *gui = static_cast(cmd); + _flush_gui_primitive(gui); + } break; + case RenderItem::TYPE_CHANGEITEM: { + RenderItem::ChangeItemCommand *change = static_cast(cmd); + Item *ci = change->item; + + if (state.reclip) { + glEnable(GL_SCISSOR_TEST); + int y = storage->frame.current_rt->height - (state.current_clip->final_clip_rect.position.y + state.current_clip->final_clip_rect.size.y); + if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP]) + y = state.current_clip->final_clip_rect.position.y; + glScissor(state.current_clip->final_clip_rect.position.x, y, state.current_clip->final_clip_rect.size.width, state.current_clip->final_clip_rect.size.height); + } + + state.rebind_shader = true; // hacked in for now. - t = t->get_ptr(); + if (state.current_clip != ci->final_clip_owner) { - if (t->redraw_if_visible) { - VisualServerRaster::redraw_request(); + state.current_clip = ci->final_clip_owner; + + if (state.current_clip) { + glEnable(GL_SCISSOR_TEST); + int y = storage->frame.current_rt->height - (state.current_clip->final_clip_rect.position.y + state.current_clip->final_clip_rect.size.y); + if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP]) + y = state.current_clip->final_clip_rect.position.y; + glScissor(state.current_clip->final_clip_rect.position.x, y, state.current_clip->final_clip_rect.size.width, state.current_clip->final_clip_rect.size.height); + } else { + glDisable(GL_SCISSOR_TEST); } + } + + // TODO: copy back buffer - glBindTexture(t->target, t->tex_id); + if (ci->copy_back_buffer) { + if (ci->copy_back_buffer->full) { + _copy_texscreen(Rect2()); + } else { + _copy_texscreen(ci->copy_back_buffer->rect); + } } - } else { - state.canvas_shader.set_custom_shader(0); - state.canvas_shader.bind(); - } - shader_cache = shader_ptr; + Item *material_owner = ci->material_owner ? ci->material_owner : ci; - canvas_last_material = material; + RID material = material_owner->material; - rebind_shader = false; - } + if (material != state.canvas_last_material || state.rebind_shader) { + + RasterizerStorageGLES2::Material *material_ptr = storage->material_owner.getornull(material); + RasterizerStorageGLES2::Shader *shader_ptr = NULL; + + if (material_ptr) { + shader_ptr = material_ptr->shader; + + if (shader_ptr && shader_ptr->mode != VS::SHADER_CANVAS_ITEM) { + shader_ptr = NULL; // not a canvas item shader, don't use. + } + } + + if (shader_ptr) { + if (shader_ptr->canvas_item.uses_screen_texture) { + _copy_texscreen(Rect2()); + } + + if (shader_ptr != state.shader_cache) { + + if (shader_ptr->canvas_item.uses_time) { + VisualServerRaster::redraw_request(); + } + + state.canvas_shader.set_custom_shader(shader_ptr->custom_code_id); + state.canvas_shader.bind(); + } + + int tc = material_ptr->textures.size(); + RID *textures = material_ptr->textures.ptrw(); + + ShaderLanguage::ShaderNode::Uniform::Hint *texture_hints = shader_ptr->texture_hints.ptrw(); + + for (int i = 0; i < tc; i++) { + + glActiveTexture(GL_TEXTURE2 + i); + + RasterizerStorageGLES2::Texture *t = storage->texture_owner.getornull(textures[i]); + + if (!t) { + + switch (texture_hints[i]) { + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK_ALBEDO: + case ShaderLanguage::ShaderNode::Uniform::HINT_BLACK: { + glBindTexture(GL_TEXTURE_2D, storage->resources.black_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_ANISO: { + glBindTexture(GL_TEXTURE_2D, storage->resources.aniso_tex); + } break; + case ShaderLanguage::ShaderNode::Uniform::HINT_NORMAL: { + glBindTexture(GL_TEXTURE_2D, storage->resources.normal_tex); + } break; + default: { + glBindTexture(GL_TEXTURE_2D, storage->resources.white_tex); + } break; + } - int blend_mode = shader_cache ? shader_cache->canvas_item.blend_mode : RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX; - bool unshaded = true || (shader_cache && blend_mode != RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX); - bool reclip = false; + continue; + } - if (last_blend_mode != blend_mode) { + t = t->get_ptr(); - switch (blend_mode) { + if (t->redraw_if_visible) { + VisualServerRaster::redraw_request(); + } - case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX: { - glBlendEquation(GL_FUNC_ADD); - if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(t->target, t->tex_id); + } } else { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + state.canvas_shader.set_custom_shader(0); + state.canvas_shader.bind(); } - } break; - case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_ADD: { - - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - - } break; - case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_SUB: { - - glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - } break; - case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MUL: { - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_DST_COLOR, GL_ZERO); - } break; - case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_PMALPHA: { - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } break; - } - } + state.shader_cache = shader_ptr; - state.uniforms.final_modulate = unshaded ? ci->final_modulate : Color(ci->final_modulate.r * p_modulate.r, ci->final_modulate.g * p_modulate.g, ci->final_modulate.b * p_modulate.b, ci->final_modulate.a * p_modulate.a); + state.canvas_last_material = material; - state.uniforms.modelview_matrix = ci->final_transform; - state.uniforms.extra_matrix = Transform2D(); + state.rebind_shader = false; + } - _set_uniforms(); + int blend_mode = state.shader_cache ? state.shader_cache->canvas_item.blend_mode : RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX; + bool unshaded = true || (state.shader_cache && blend_mode != RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX); + state.reclip = false; + + if (state.last_blend_mode != blend_mode) { + + switch (blend_mode) { + + case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MIX: { + glBlendEquation(GL_FUNC_ADD); + if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + } break; + case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_ADD: { + + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + } break; + case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_SUB: { + + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } break; + case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_MUL: { + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_DST_COLOR, GL_ZERO); + } break; + case RasterizerStorageGLES2::Shader::CanvasItem::BLEND_MODE_PMALPHA: { + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } break; + } + } - _canvas_item_render_commands(p_item_list, NULL, reclip); + state.uniforms.final_modulate = unshaded ? ci->final_modulate : Color(ci->final_modulate.r * state.p_modulate.r, ci->final_modulate.g * state.p_modulate.g, ci->final_modulate.b * state.p_modulate.b, ci->final_modulate.a * state.p_modulate.a); - rebind_shader = true; // hacked in for now. + state.uniforms.modelview_matrix = ci->final_transform; + state.uniforms.extra_matrix = Transform2D(); - if (reclip) { - glEnable(GL_SCISSOR_TEST); - int y = storage->frame.current_rt->height - (current_clip->final_clip_rect.position.y + current_clip->final_clip_rect.size.y); - if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_VFLIP]) - y = current_clip->final_clip_rect.position.y; - glScissor(current_clip->final_clip_rect.position.x, y, current_clip->final_clip_rect.size.width, current_clip->final_clip_rect.size.height); + _set_uniforms(); + } break; } + } + + render_commands.clear(); +} + +void RasterizerCanvasGLES2::canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform) { + RenderItem::BeginListCommand *begin = push_command(); + begin->p_modulate = p_modulate; + + while (p_item_list) { + RenderItem::ChangeItemCommand *cmd = push_command(); + cmd->item = p_item_list; + + _canvas_item_process(p_item_list); p_item_list = p_item_list->next; } - if (current_clip) { - glDisable(GL_SCISSOR_TEST); - } + push_command(); } void RasterizerCanvasGLES2::canvas_debug_viewport_shadows(Light *p_lights_with_shadow) { @@ -1040,7 +1369,8 @@ void RasterizerCanvasGLES2::draw_generic_textured_rect(const Rect2 &p_rect, cons 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)); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDrawArrays(GL_TRIANGLES, 0, 6); + ++storage->info.render.draw_call_count; } void RasterizerCanvasGLES2::draw_window_margins(int *black_margin, RID *black_image) { @@ -1053,14 +1383,16 @@ void RasterizerCanvasGLES2::initialize() { glGenBuffers(1, &data.canvas_quad_vertices); glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); - const float qv[8] = { + const float qv[12] = { 0, 0, 0, 1, 1, 1, - 1, 0 + 1, 1, + 1, 0, + 0, 0 }; - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, qv, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, qv, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); } @@ -1073,10 +1405,14 @@ void RasterizerCanvasGLES2::initialize() { glGenBuffers(1, &data.polygon_buffer); glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); glBufferData(GL_ARRAY_BUFFER, poly_size, NULL, GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); data.polygon_buffer_size = poly_size; - glBindBuffer(GL_ARRAY_BUFFER, 0); + data.polygon_mem_buffer = (uint8_t *)memalloc(poly_size); + ERR_FAIL_COND(!data.polygon_buffer); + zeromem(data.polygon_mem_buffer, poly_size); + data.polygon_mem_offset = 0; uint32_t index_size = GLOBAL_DEF("rendering/limits/buffers/canvas_polygon_index_size_kb", 128); index_size *= 1024; // kb @@ -1084,6 +1420,13 @@ void RasterizerCanvasGLES2::initialize() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_size, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + data.polygon_index_buffer_size = index_size; + + data.polygon_index_mem_buffer = (uint8_t *)memalloc(index_size); + ERR_FAIL_COND(!data.polygon_index_mem_buffer); + zeromem(data.polygon_index_mem_buffer, index_size); + data.polygon_index_mem_offset = 0; } // ninepatch buffers @@ -1153,11 +1496,22 @@ void RasterizerCanvasGLES2::initialize() { state.canvas_shader.init(); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true); + state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT_ATTRIB, false); state.canvas_shader.bind(); + + uint32_t render_commands_buffer_size = GLOBAL_DEF("rendering/limits/buffers/canvas_render_commands_buffer_size_kb", 128); + render_commands_buffer_size *= 1024; + + render_commands.init(render_commands_buffer_size); } void RasterizerCanvasGLES2::finalize() { + glDeleteBuffers(1, &data.polygon_buffer); + glDeleteBuffers(1, &data.polygon_index_buffer); + + memfree(data.polygon_mem_buffer); + memfree(data.polygon_index_mem_buffer); } RasterizerCanvasGLES2::RasterizerCanvasGLES2() { diff --git a/drivers/gles2/rasterizer_canvas_gles2.h b/drivers/gles2/rasterizer_canvas_gles2.h index 4eab8c6038e4..53194e4b4a80 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.h +++ b/drivers/gles2/rasterizer_canvas_gles2.h @@ -59,10 +59,17 @@ class RasterizerCanvasGLES2 : public RasterizerCanvas { GLuint polygon_index_buffer; uint32_t polygon_buffer_size; + uint32_t polygon_index_buffer_size; GLuint ninepatch_vertices; GLuint ninepatch_elements; + uint8_t *polygon_mem_buffer; + uint32_t polygon_mem_offset; + + uint8_t *polygon_index_mem_buffer; + uint32_t polygon_index_mem_offset; + } data; struct State { @@ -80,8 +87,228 @@ class RasterizerCanvasGLES2 : public RasterizerCanvas { Transform vp; + Item *current_clip; + RasterizerStorageGLES2::Shader *shader_cache; + bool rebind_shader; + Size2 rt_size; + RID canvas_last_material; + int last_blend_mode; + bool reclip; + Color p_modulate; + } state; + struct RenderItem { + enum Type { + TYPE_PASSTHROUGH, + TYPE_CHANGEITEM, + TYPE_POLYGON, + TYPE_GENERIC, + TYPE_GUI_PRIMITIVE, + TYPE_BEGINLIST, + TYPE_ENDLIST, + TYPE_NINEPATCH, + TYPE_RECTS + }; + + struct CommandBase { + Type type; + }; + + struct BeginListCommand : public CommandBase { + static const Type _type = TYPE_BEGINLIST; + Color p_modulate; + }; + + struct EndListCommand : public CommandBase { + static const Type _type = TYPE_ENDLIST; + }; + + struct PassThroughCommand : public CommandBase { + static const Type _type = TYPE_PASSTHROUGH; + RasterizerCanvas::Item::Command *command; + }; + + struct ChangeItemCommand : public CommandBase { + static const Type _type = TYPE_CHANGEITEM; + RasterizerCanvas::Item *item; + }; + + struct NinepatchCommand : public CommandBase { + static const Type _type = TYPE_NINEPATCH; + RasterizerCanvas::Item::CommandNinePatch *command; + + uint32_t vertices_offset; + }; + + struct RectsCommand : public CommandBase { + static const Type _type = TYPE_RECTS; + + uint32_t vertices_offset; + uint32_t count; + + bool use_texture; + RID texture; + + bool untile; + }; + + struct PolygonCommand : public CommandBase { + static const Type _type = TYPE_POLYGON; + RasterizerCanvas::Item::Command *command; + + uint32_t count; + + Color color; + + uint32_t vertices_offset; + + bool use_colors; + uint32_t colors_offset; + + bool use_uvs; + uint32_t uvs_offset; + + uint32_t indices_offset; + }; + + struct GenericCommand : public CommandBase { + static const Type _type = TYPE_GENERIC; + GLuint primitive; + + uint32_t count; + + Color color; + + uint32_t vertices_offset; + + bool use_colors; + uint32_t colors_offset; + + bool use_uvs; + uint32_t uvs_offset; + }; + + struct GuiPrimitiveCommand : public CommandBase { + static const Type _type = TYPE_GUI_PRIMITIVE; + RasterizerCanvas::Item::Command *command; + uint32_t count; + + uint32_t offset; + + bool use_colors; + uint32_t color_offset; + + bool use_uvs; + uint32_t uvs_offset; + + uint32_t stride; + }; + }; + + struct RenderCommands { + + struct Item { + uint32_t size; + }; + + struct Ptr { + + Ptr(RenderCommands *owner) { + this->owner = owner; + offset = 0; + + read_current(); + } + + _FORCE_INLINE_ bool next() { + if (curr == NULL) + return false; + + read_current(); + return curr; + } + + _FORCE_INLINE_ RenderItem::CommandBase *current() { + return curr; + } + + private: + uint32_t offset; + RenderItem::CommandBase *curr; + + RenderCommands *owner; + + _FORCE_INLINE_ void read_current() { + if (offset >= owner->offset) { + curr = NULL; + return; + } + + Item *item = reinterpret_cast(owner->buffer + offset); + offset += item->size; + + curr = reinterpret_cast(item + 1); + } + }; + + RenderCommands() { + buffer = NULL; + size = 0; + clear(); + } + + ~RenderCommands() { + memfree(buffer); + } + + _FORCE_INLINE_ Ptr ptr() { + return Ptr(this); + } + + _FORCE_INLINE_ bool empty() { + return offset == 0; + } + + template + _FORCE_INLINE_ T *allocate() { + const uint32_t item_size = sizeof(Item) + sizeof(T); + + if (size - offset < item_size) + return NULL; + + uint8_t *ptr = buffer + offset; + + Item *item = reinterpret_cast(ptr); + item->size = item_size; + + T *result = reinterpret_cast(ptr + sizeof(Item)); + result->type = T::_type; + + offset += item_size; + + return result; + } + + _FORCE_INLINE_ void clear() { + offset = 0; + } + + void init(const uint32_t size) { + ERR_FAIL_COND(buffer); // do not allow re-initialization + + clear(); + + buffer = reinterpret_cast(memrealloc(buffer, size)); + this->size = size; + } + + private: + uint8_t *buffer; + uint32_t size; + uint32_t offset; + } render_commands; + typedef void Texture; RasterizerSceneGLES2 *scene_render; @@ -99,11 +326,36 @@ class RasterizerCanvasGLES2 : public RasterizerCanvas { _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_ bool _prepare_gui_primitive(RasterizerCanvas::Item::Command *command, int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs); + _FORCE_INLINE_ bool _prepare_polygon(RasterizerCanvas::Item::Command *cmd, 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_ bool _prepare_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_ bool _prepare_ninepatch(RasterizerCanvas::Item::CommandNinePatch *command); + + _FORCE_INLINE_ void _canvas_item_process(Item *p_item); + + _FORCE_INLINE_ void _flush_gui_primitive(const RenderItem::GuiPrimitiveCommand *cmd); + _FORCE_INLINE_ void _flush_polygon(const RenderItem::PolygonCommand *cmd); + _FORCE_INLINE_ void _flush_generic(const RenderItem::GenericCommand *cmd); + _FORCE_INLINE_ void _flush_ninepatch(const RenderItem::NinepatchCommand *cmd); + _FORCE_INLINE_ void _flush_rects(const RenderItem::RectsCommand *cmd); + + template + _FORCE_INLINE_ T *push_command() { + T *cmd = render_commands.allocate(); + if (!cmd) { + _flush(); + + cmd = render_commands.allocate(); + ERR_FAIL_COND_V(!cmd, NULL); + } + + return cmd; + } + + void _flush(); + + _FORCE_INLINE_ void _canvas_render_command(Item::Command *command, Item *current_clip, bool &reclip); - _FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip); _FORCE_INLINE_ void _copy_texscreen(const Rect2 &p_rect); virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform); diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 9ea20ff15a76..ba9f36c35d57 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -376,6 +376,7 @@ void RasterizerGLES2::blit_render_target_to_screen(RID p_render_target, const Re ERR_FAIL_COND(!rt); canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, true); + canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT_ATTRIB, false); canvas->state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_UV_ATTRIBUTE, false); canvas->state.canvas_shader.bind(); diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 0dc506d9914a..eb580593200e 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -1991,6 +1991,11 @@ int RasterizerStorageGLES2::get_captured_render_info(VS::RenderInfo p_info) { } int RasterizerStorageGLES2::get_render_info(VS::RenderInfo p_info) { + switch (p_info) { + case VS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME: + return info.render.draw_call_count; + } + return 0; } diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index 11c6ab9b761e..2d2474757ae2 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -13,7 +13,13 @@ uniform highp mat4 modelview_matrix; uniform highp mat4 extra_matrix; attribute highp vec2 vertex; // attrib:0 attribute vec4 color_attrib; // attrib:3 + +#ifdef USE_TEXTURE_RECT_ATTRIB +attribute vec4 src_rect_attrib; // attrib:4 +attribute vec4 dst_rect_attrib; // attrib:5 +#else attribute vec2 uv_attrib; // attrib:4 +#endif varying vec2 uv_interp; varying vec4 color_interp; @@ -43,7 +49,25 @@ vec2 select(vec2 a, vec2 b, bvec2 c) { void main() { vec4 color = color_attrib; +#ifdef USE_TEXTURE_RECT_ATTRIB + if (dst_rect_attrib.z < 0.0) { // Transpose is encoded as negative dst_rect_attrib.z + uv_interp = src_rect_attrib.xy + abs(src_rect_attrib.zw) * vertex.yx; + } else { + uv_interp = src_rect_attrib.xy + abs(src_rect_attrib.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_attrib.xy + abs(dst_rect_attrib.zw) * select(vertex, vec2(1.0, 1.0) - vertex, lessThan(src_rect_attrib.zw, vec2(0.0, 0.0))); + // outvec.xy = dst_rect_attrib.xy + abs(dst_rect_attrib.zw) * vertex; +#else #ifdef USE_TEXTURE_RECT if (dst_rect.z < 0.0) { // Transpose is encoded as negative dst_rect.z @@ -74,6 +98,7 @@ void main() { #endif +#endif { vec2 src_vtx=outvec.xy; VERTEX_SHADER_CODE