summaryrefslogtreecommitdiff
path: root/drivers/gles3
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gles3')
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp1042
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h111
-rw-r--r--drivers/gles3/rasterizer_gles3.h5
-rw-r--r--drivers/gles3/shaders/canvas.glsl17
-rw-r--r--drivers/gles3/storage/config.cpp2
-rw-r--r--drivers/gles3/storage/config.h2
-rw-r--r--drivers/gles3/storage/texture_storage.cpp2
-rw-r--r--drivers/gles3/storage/texture_storage.h5
8 files changed, 706 insertions, 480 deletions
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 0ffede0992..ab232e712c 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -124,6 +124,26 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
// Clear out any state that may have been left from the 3D pass.
reset_canvas();
+ if (state.canvas_instance_data_buffers[state.current_buffer].fence != GLsync()) {
+ GLint syncStatus;
+ glGetSynciv(state.canvas_instance_data_buffers[state.current_buffer].fence, GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus);
+ if (syncStatus == GL_UNSIGNALED) {
+ // If older than 2 frames, wait for sync OpenGL can have up to 3 frames in flight, any more and we need to sync anyway.
+ if (state.canvas_instance_data_buffers[state.current_buffer].last_frame_used < RSG::rasterizer->get_frame_number() - 2) {
+ glClientWaitSync(state.canvas_instance_data_buffers[state.current_buffer].fence, 0, 100000000); // wait for up to 100ms
+ } else {
+ // Used in last frame or frame before that. OpenGL can get up to two frames behind, so these buffers may still be in use
+ // Allocate a new buffer and use that.
+ _allocate_instance_data_buffer();
+ }
+ } else {
+ // Already finished all rendering commands, we can use it.
+ state.canvas_instance_data_buffers[state.current_buffer].last_frame_used = RSG::rasterizer->get_frame_number();
+ glDeleteSync(state.canvas_instance_data_buffers[state.current_buffer].fence);
+ state.canvas_instance_data_buffers[state.current_buffer].fence = GLsync();
+ }
+ }
+
// TODO: Setup Directional Lights
// TODO: Setup lights
@@ -207,6 +227,8 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
Item *ci = p_item_list;
Item *canvas_group_owner = nullptr;
+ uint32_t starting_index = 0;
+
while (ci) {
if (ci->copy_back_buffer && canvas_group_owner == nullptr) {
backbuffer_copy = true;
@@ -245,8 +267,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
if (ci->canvas_group_owner != nullptr) {
if (canvas_group_owner == nullptr) {
// Canvas group begins here, render until before this item
-
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false);
item_count = 0;
Rect2i group_rect = ci->canvas_group_owner->global_rect_cache;
@@ -271,7 +292,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
}
if (ci == canvas_group_owner) {
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, true);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, true);
item_count = 0;
if (ci->canvas_group->blur_mipmaps) {
@@ -284,7 +305,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
if (backbuffer_copy) {
//render anything pending, including clearing if no items
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false);
item_count = 0;
texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps);
@@ -306,7 +327,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
items[item_count++] = ci;
if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) {
- _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
+ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false);
//then reset
item_count = 0;
}
@@ -318,46 +339,43 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
RenderingServerDefault::redraw_request();
}
+ state.canvas_instance_data_buffers[state.current_buffer].fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+
// Clear out state used in 2D pass
reset_canvas();
+ state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size();
}
-void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer) {
- GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool p_to_backbuffer) {
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
- Item *current_clip = nullptr;
-
- Transform2D canvas_transform_inverse = p_canvas_transform_inverse;
canvas_begin(p_to_render_target, p_to_backbuffer);
- RID prev_material;
+ if (p_item_count <= 0) {
+ // Nothing to draw, just call canvas_begin() to clear the render target and return.
+ return;
+ }
+
uint32_t index = 0;
- GLES3::CanvasShaderData::BlendMode last_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX;
- Color last_blend_color;
- GLES3::CanvasShaderData *shader_data_cache = nullptr;
+ Item *current_clip = nullptr;
+
+ // Record Batches.
+ // First item always forms its own batch.
+ bool batch_broken = false;
+ _new_batch(batch_broken, index);
- state.current_tex = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE);
- state.current_tex_ptr = nullptr;
- state.current_normal = RID();
- state.current_specular = RID();
- state.canvas_texscreen_used = false;
- state.current_shader_version = state.canvas_shader_default_version;
+ // Override the start position and index as we want to start from where we finished off last time.
+ state.canvas_instance_batches[state.current_batch_index].start = r_last_index * sizeof(InstanceData);
+ index = 0;
+ _align_instance_data_buffer(index);
for (int i = 0; i < p_item_count; i++) {
Item *ci = items[i];
- if (current_clip != ci->final_clip_owner) {
- _render_batch(index);
-
+ if (ci->final_clip_owner != state.canvas_instance_batches[state.current_batch_index].clip) {
+ _new_batch(batch_broken, index);
+ state.canvas_instance_batches[state.current_batch_index].clip = ci->final_clip_owner;
current_clip = ci->final_clip_owner;
- //setup clip
- if (current_clip) {
- glEnable(GL_SCISSOR_TEST);
- glScissor(current_clip->final_clip_rect.position.x, current_clip->final_clip_rect.position.y, current_clip->final_clip_rect.size.x, current_clip->final_clip_rect.size.y);
- } else {
- glDisable(GL_SCISSOR_TEST);
- }
}
RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material;
@@ -366,105 +384,68 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
material = default_canvas_group_material;
}
- if (material != prev_material) {
- _render_batch(index);
+ GLES3::CanvasShaderData *shader_data_cache = nullptr;
+ if (material != state.canvas_instance_batches[state.current_batch_index].material) {
+ _new_batch(batch_broken, index);
+
GLES3::CanvasMaterialData *material_data = nullptr;
if (material.is_valid()) {
material_data = static_cast<GLES3::CanvasMaterialData *>(material_storage->material_get_data(material, RS::SHADER_CANVAS_ITEM));
}
+ shader_data_cache = nullptr;
if (material_data) {
if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) {
- // Bind uniform buffer and textures
- material_data->bind_uniforms();
- state.current_shader_version = material_data->shader_data->version;
shader_data_cache = material_data->shader_data;
- } else {
- state.current_shader_version = state.canvas_shader_default_version;
- shader_data_cache = nullptr;
}
- } else {
- state.current_shader_version = state.canvas_shader_default_version;
- shader_data_cache = nullptr;
}
- prev_material = material;
+
+ state.canvas_instance_batches[state.current_batch_index].material = material;
+ state.canvas_instance_batches[state.current_batch_index].material_data = material_data;
}
GLES3::CanvasShaderData::BlendMode blend_mode = shader_data_cache ? shader_data_cache->blend_mode : GLES3::CanvasShaderData::BLEND_MODE_MIX;
- _render_item(p_to_render_target, ci, canvas_transform_inverse, current_clip, p_lights, index, blend_mode, last_blend_mode, last_blend_color);
+ _record_item_commands(ci, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken);
}
- // Render last command
- _render_batch(index);
-}
-
-void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, uint32_t &r_index, GLES3::CanvasShaderData::BlendMode p_blend_mode, GLES3::CanvasShaderData::BlendMode &r_last_blend_mode, Color &r_last_blend_color) {
- // Used by Polygon and Mesh.
- static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
-
- RS::CanvasItemTextureFilter current_filter = state.default_filter;
- RS::CanvasItemTextureRepeat current_repeat = state.default_repeat;
-
- if (p_item->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) {
- current_filter = p_item->texture_filter;
- }
-
- if (p_item->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) {
- current_repeat = p_item->texture_repeat;
- }
-
- Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
- Transform2D draw_transform; // Used by transform command
-
- Color base_color = p_item->final_modulate;
-
- uint32_t base_flags = 0;
-
- bool reclip = false;
-
- bool skipping = false;
-
- const Item::Command *c = p_item->commands;
- while (c) {
- if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) {
- c = c->next;
- continue;
- }
- if (c->type != Item::Command::TYPE_MESH) {
- // For Meshes, this gets updated below.
- _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world);
- }
-
- for (int i = 0; i < 4; i++) {
- state.instance_data_array[r_index].modulation[i] = 0.0;
- state.instance_data_array[r_index].ninepatch_margins[i] = 0.0;
- state.instance_data_array[r_index].src_rect[i] = 0.0;
- state.instance_data_array[r_index].dst_rect[i] = 0.0;
- state.instance_data_array[r_index].lights[i] = uint32_t(0);
- }
- state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0;
- state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0;
+ // Copy over all data needed for rendering.
+ glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].ubo);
+#ifdef WEB_ENABLED
+ glBufferSubData(GL_UNIFORM_BUFFER, r_last_index * sizeof(InstanceData), sizeof(InstanceData) * index, state.instance_data_array);
+#else
+ // On Desktop and mobile we map the memory without synchronizing for maximum speed.
+ void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, r_last_index * sizeof(InstanceData), index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
+ memcpy(ubo, state.instance_data_array, index * sizeof(InstanceData));
+ glUnmapBuffer(GL_UNIFORM_BUFFER);
+#endif
- state.instance_data_array[r_index].pad[0] = 0.0;
- state.instance_data_array[r_index].pad[1] = 0.0;
+ glDisable(GL_SCISSOR_TEST);
+ current_clip = nullptr;
- state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config
+ GLES3::CanvasShaderData::BlendMode last_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX;
- GLES3::CanvasShaderData::BlendMode blend_mode = p_blend_mode;
- Color blend_color;
+ state.current_tex = RID();
- if (c->type == Item::Command::TYPE_RECT) {
- const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c);
- if (rect->flags & CANVAS_RECT_LCD) {
- blend_mode = GLES3::CanvasShaderData::BLEND_MODE_LCD;
- blend_color = rect->modulate;
+ for (uint32_t i = 0; i <= state.current_batch_index; i++) {
+ //setup clip
+ if (current_clip != state.canvas_instance_batches[i].clip) {
+ current_clip = state.canvas_instance_batches[i].clip;
+ if (current_clip) {
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(current_clip->final_clip_rect.position.x, current_clip->final_clip_rect.position.y, current_clip->final_clip_rect.size.x, current_clip->final_clip_rect.size.y);
+ } else {
+ glDisable(GL_SCISSOR_TEST);
}
}
- if (r_last_blend_mode != blend_mode || r_last_blend_color != blend_color) {
- _render_batch(r_index);
+ GLES3::CanvasMaterialData *material_data = state.canvas_instance_batches[i].material_data;
+ CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant;
+ _bind_material(material_data, variant);
- if (r_last_blend_mode == GLES3::CanvasShaderData::BLEND_MODE_DISABLED) {
+ GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode;
+
+ if (last_blend_mode != blend_mode) {
+ if (last_blend_mode == GLES3::CanvasShaderData::BLEND_MODE_DISABLED) {
// re-enable it
glEnable(GL_BLEND);
} else if (blend_mode == GLES3::CanvasShaderData::BLEND_MODE_DISABLED) {
@@ -475,7 +456,6 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
switch (blend_mode) {
case GLES3::CanvasShaderData::BLEND_MODE_DISABLED: {
// Nothing to do here.
-
} break;
case GLES3::CanvasShaderData::BLEND_MODE_LCD: {
glBlendEquation(GL_FUNC_ADD);
@@ -484,6 +464,7 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
} else {
glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE);
}
+ Color blend_color = state.canvas_instance_batches[state.current_batch_index].blend_color;
glBlendColor(blend_color.r, blend_color.g, blend_color.b, blend_color.a);
} break;
@@ -532,32 +513,112 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
} break;
}
- r_last_blend_mode = blend_mode;
- r_last_blend_color = blend_color;
+ last_blend_mode = blend_mode;
+ }
+
+ _render_batch(p_lights, i);
+ }
+
+ state.current_batch_index = 0;
+ state.canvas_instance_batches.clear();
+ r_last_index += index;
+}
+
+void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken) {
+ RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter;
+
+ if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) {
+ _new_batch(r_batch_broken, r_index);
+
+ state.canvas_instance_batches[state.current_batch_index].filter = texture_filter;
+ }
+
+ RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? state.default_repeat : p_item->texture_repeat;
+
+ if (texture_repeat != state.canvas_instance_batches[state.current_batch_index].repeat) {
+ _new_batch(r_batch_broken, r_index);
+
+ state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat;
+ }
+
+ Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
+ Transform2D draw_transform; // Used by transform command
+
+ Color base_color = p_item->final_modulate;
+ uint32_t base_flags = 0;
+ Size2 texpixel_size;
+
+ bool reclip = false;
+
+ bool skipping = false;
+
+ const Item::Command *c = p_item->commands;
+ while (c) {
+ if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) {
+ c = c->next;
+ continue;
+ }
+
+ if (c->type != Item::Command::TYPE_MESH) {
+ // For Meshes, this gets updated below.
+ _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world);
+ }
+
+ // Zero out most fields.
+ for (int i = 0; i < 4; i++) {
+ state.instance_data_array[r_index].modulation[i] = 0.0;
+ state.instance_data_array[r_index].ninepatch_margins[i] = 0.0;
+ state.instance_data_array[r_index].src_rect[i] = 0.0;
+ state.instance_data_array[r_index].dst_rect[i] = 0.0;
+ state.instance_data_array[r_index].lights[i] = uint32_t(0);
+ }
+ state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0;
+ state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0;
+
+ state.instance_data_array[r_index].pad[0] = 0.0;
+ state.instance_data_array[r_index].pad[1] = 0.0;
+
+ state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config
+
+ Color blend_color;
+ if (c->type == Item::Command::TYPE_RECT) {
+ const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c);
+ if (rect->flags & CANVAS_RECT_LCD) {
+ p_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_LCD;
+ blend_color = rect->modulate * base_color;
+ }
+ }
+
+ if (p_blend_mode != state.canvas_instance_batches[state.current_batch_index].blend_mode || blend_color != state.canvas_instance_batches[state.current_batch_index].blend_color) {
+ _new_batch(r_batch_broken, r_index);
+ state.canvas_instance_batches[state.current_batch_index].blend_mode = p_blend_mode;
+ state.canvas_instance_batches[state.current_batch_index].blend_color = blend_color;
}
switch (c->type) {
case Item::Command::TYPE_RECT: {
const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c);
- if (rect->flags & CANVAS_RECT_TILE) {
- current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED;
+ if (rect->flags & CANVAS_RECT_TILE && state.canvas_instance_batches[state.current_batch_index].repeat != RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED) {
+ _new_batch(r_batch_broken, r_index);
+ state.canvas_instance_batches[state.current_batch_index].repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED;
}
- if (rect->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_RECT) {
- _render_batch(r_index);
-
- state.current_primitive_points = 0;
- state.current_command = Item::Command::TYPE_RECT;
+ if (rect->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_RECT) {
+ _new_batch(r_batch_broken, r_index);
+ state.canvas_instance_batches[state.current_batch_index].tex = rect->texture;
+ state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT;
+ state.canvas_instance_batches[state.current_batch_index].command = c;
+ state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_QUAD;
}
- _bind_canvas_texture(rect->texture, current_filter, current_repeat, r_index);
- GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_QUAD);
+
+ _prepare_canvas_texture(rect->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
Rect2 src_rect;
Rect2 dst_rect;
if (rect->texture != RID()) {
- src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * state.current_pixel_size, rect->source.size * state.current_pixel_size) : Rect2(0, 0, 1, 1);
+ src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1);
dst_rect = Rect2(rect->rect.position, rect->rect.size);
if (dst_rect.size.width < 0) {
@@ -625,36 +686,32 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
state.instance_data_array[r_index].dst_rect[2] = dst_rect.size.width;
state.instance_data_array[r_index].dst_rect[3] = dst_rect.size.height;
- r_index++;
- if (r_index >= state.max_instances_per_batch - 1) {
- _render_batch(r_index);
- }
+ _add_to_batch(r_index, r_batch_broken);
} break;
case Item::Command::TYPE_NINEPATCH: {
const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c);
- if (np->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_NINEPATCH) {
- _render_batch(r_index);
-
- state.current_primitive_points = 0;
- state.current_command = Item::Command::TYPE_NINEPATCH;
+ if (np->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_NINEPATCH) {
+ _new_batch(r_batch_broken, r_index);
+ state.canvas_instance_batches[state.current_batch_index].tex = np->texture;
+ state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH;
+ state.canvas_instance_batches[state.current_batch_index].command = c;
+ state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_NINEPATCH;
}
- //bind textures
- _bind_canvas_texture(np->texture, current_filter, current_repeat, r_index);
- GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_NINEPATCH);
+ _prepare_canvas_texture(np->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
Rect2 src_rect;
Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y);
if (np->texture == RID()) {
- state.current_pixel_size = Size2(1, 1);
+ texpixel_size = Size2(1, 1);
src_rect = Rect2(0, 0, 1, 1);
} else {
if (np->source != Rect2()) {
- src_rect = Rect2(np->source.position.x * state.current_pixel_size.width, np->source.position.y * state.current_pixel_size.height, np->source.size.x * state.current_pixel_size.width, np->source.size.y * state.current_pixel_size.height);
+ src_rect = Rect2(np->source.position.x * texpixel_size.width, np->source.position.y * texpixel_size.height, np->source.size.x * texpixel_size.width, np->source.size.y * texpixel_size.height);
state.instance_data_array[r_index].color_texture_pixel_size[0] = 1.0 / np->source.size.width;
state.instance_data_array[r_index].color_texture_pixel_size[1] = 1.0 / np->source.size.height;
@@ -690,32 +747,26 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
state.instance_data_array[r_index].ninepatch_margins[2] = np->margin[SIDE_RIGHT];
state.instance_data_array[r_index].ninepatch_margins[3] = np->margin[SIDE_BOTTOM];
- r_index++;
- if (r_index >= state.max_instances_per_batch - 1) {
- _render_batch(r_index);
- }
+ _add_to_batch(r_index, r_batch_broken);
// Restore if overridden.
- state.instance_data_array[r_index].color_texture_pixel_size[0] = state.current_pixel_size.x;
- state.instance_data_array[r_index].color_texture_pixel_size[1] = state.current_pixel_size.y;
+ state.instance_data_array[r_index].color_texture_pixel_size[0] = texpixel_size.x;
+ state.instance_data_array[r_index].color_texture_pixel_size[1] = texpixel_size.y;
} break;
case Item::Command::TYPE_POLYGON: {
const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c);
- PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id);
- ERR_CONTINUE(!pb);
+ // Polygon's can't be batched, so always create a new batch
+ _new_batch(r_batch_broken, r_index);
- if (polygon->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_POLYGON) {
- _render_batch(r_index);
+ state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture;
+ state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON;
+ state.canvas_instance_batches[state.current_batch_index].command = c;
+ state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
- state.current_primitive_points = 0;
- state.current_command = Item::Command::TYPE_POLYGON;
- }
- _bind_canvas_texture(polygon->texture, current_filter, current_repeat, r_index);
- GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES);
+ _prepare_canvas_texture(polygon->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
- state.current_primitive = polygon->primitive;
state.instance_data_array[r_index].modulation[0] = base_color.r;
state.instance_data_array[r_index].modulation[1] = base_color.g;
state.instance_data_array[r_index].modulation[2] = base_color.b;
@@ -727,39 +778,20 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
state.instance_data_array[r_index].ninepatch_margins[j] = 0;
}
- _bind_instance_data_buffer(1);
- glBindVertexArray(pb->vertex_array);
-
- if (pb->color_disabled) {
- glVertexAttrib4f(RS::ARRAY_COLOR, pb->color.r, pb->color.g, pb->color.b, pb->color.a);
- }
-
- if (pb->index_buffer != 0) {
- glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr);
- } else {
- glDrawArrays(prim[polygon->primitive], 0, pb->count);
- }
- glBindVertexArray(0);
- state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
-
- state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size();
-
- if (pb->color_disabled) {
- // Reset so this doesn't pollute other draw calls.
- glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0);
- }
+ _add_to_batch(r_index, r_batch_broken);
} break;
case Item::Command::TYPE_PRIMITIVE: {
const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c);
- if (state.current_primitive_points != primitive->point_count || state.current_command != Item::Command::TYPE_PRIMITIVE) {
- _render_batch(r_index);
- state.current_primitive_points = primitive->point_count;
- state.current_command = Item::Command::TYPE_PRIMITIVE;
+ if (primitive->point_count != state.canvas_instance_batches[state.current_batch_index].primitive_points || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_PRIMITIVE) {
+ _new_batch(r_batch_broken, r_index);
+ state.canvas_instance_batches[state.current_batch_index].tex = RID();
+ state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count;
+ state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE;
+ state.canvas_instance_batches[state.current_batch_index].command = c;
+ state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_PRIMITIVE;
}
- _bind_canvas_texture(RID(), current_filter, current_repeat, r_index);
- GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_PRIMITIVE);
for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) {
state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j].x;
@@ -770,14 +802,16 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
- r_index++;
+
+ _add_to_batch(r_index, r_batch_broken);
+
if (primitive->point_count == 4) {
// Reset base data
_update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world);
state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0;
state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0;
- state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config
+ state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config
for (uint32_t j = 0; j < 3; j++) {
//second half of triangle
@@ -789,78 +823,39 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r);
state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
}
- r_index++;
- }
- if (r_index >= state.max_instances_per_batch - 1) {
- _render_batch(r_index);
+
+ _add_to_batch(r_index, r_batch_broken);
}
} break;
case Item::Command::TYPE_MESH:
case Item::Command::TYPE_MULTIMESH:
case Item::Command::TYPE_PARTICLES: {
- GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
- RID mesh;
- RID mesh_instance;
- RID texture;
- Color modulate(1, 1, 1, 1);
- uint32_t instance_count = 1;
- GLuint multimesh_buffer = 0;
- uint32_t multimesh_stride = 0;
- uint32_t multimesh_color_offset = 0;
- bool multimesh_uses_color = false;
- bool multimesh_uses_custom_data = false;
+ // Mesh's can't be batched, so always create a new batch
+ _new_batch(r_batch_broken, r_index);
+ Color modulate(1, 1, 1, 1);
+ state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES;
if (c->type == Item::Command::TYPE_MESH) {
const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
- mesh = m->mesh;
- mesh_instance = m->mesh_instance;
- texture = m->texture;
- modulate = m->modulate;
+ state.canvas_instance_batches[state.current_batch_index].tex = m->texture;
_update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, state.instance_data_array[r_index].world);
+ modulate = m->modulate;
} else if (c->type == Item::Command::TYPE_MULTIMESH) {
const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c);
- RID multimesh = mm->multimesh;
- mesh = mesh_storage->multimesh_get_mesh(multimesh);
- texture = mm->texture;
-
- if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) {
- break;
- }
-
- instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh);
-
- if (instance_count == 0) {
- break;
+ state.canvas_instance_batches[state.current_batch_index].tex = mm->texture;
+ uint32_t instance_count = GLES3::MeshStorage::get_singleton()->multimesh_get_instances_to_draw(mm->multimesh);
+ if (instance_count > 1) {
+ state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED;
}
-
- multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh);
- multimesh_stride = mesh_storage->multimesh_get_stride(multimesh);
- multimesh_color_offset = mesh_storage->multimesh_get_color_offset(multimesh);
- multimesh_uses_color = mesh_storage->multimesh_uses_colors(multimesh);
- multimesh_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh);
+ } else if (c->type == Item::Command::TYPE_PARTICLES) {
+ WARN_PRINT_ONCE("Particles not supported yet, sorry :(");
}
- // TODO: implement particles here
+ state.canvas_instance_batches[state.current_batch_index].command = c;
+ state.canvas_instance_batches[state.current_batch_index].command_type = c->type;
- if (mesh.is_null()) {
- break;
- }
-
- if (texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_PRIMITIVE) {
- _render_batch(r_index);
- state.current_primitive_points = 0;
- state.current_command = c->type;
- }
-
- _bind_canvas_texture(texture, current_filter, current_repeat, r_index);
- if (instance_count == 1) {
- GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES);
- } else {
- GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_INSTANCED);
- }
-
- uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh);
+ _prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
state.instance_data_array[r_index].modulation[0] = base_color.r * modulate.r;
state.instance_data_array[r_index].modulation[1] = base_color.g * modulate.g;
@@ -872,75 +867,9 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
state.instance_data_array[r_index].dst_rect[j] = 0;
state.instance_data_array[r_index].ninepatch_margins[j] = 0;
}
- _bind_instance_data_buffer(1);
- for (uint32_t j = 0; j < surf_count; j++) {
- void *surface = mesh_storage->mesh_get_surface(mesh, j);
-
- RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface);
- ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX);
-
- GLuint vertex_array_gl = 0;
- GLuint index_array_gl = 0;
-
- uint32_t input_mask = 0; // 2D meshes always use the same vertex format
- if (mesh_instance.is_valid()) {
- mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl);
- } else {
- mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array_gl);
- }
-
- index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0);
- bool use_index_buffer = false;
- glBindVertexArray(vertex_array_gl);
- if (index_array_gl != 0) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl);
- use_index_buffer = true;
- }
-
- if (instance_count > 1) {
- // Bind instance buffers.
- glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer);
- glEnableVertexAttribArray(1);
- glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0));
- glVertexAttribDivisor(1, 1);
- glEnableVertexAttribArray(2);
- glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4));
- glVertexAttribDivisor(2, 1);
-
- if (multimesh_uses_color || multimesh_uses_custom_data) {
- glEnableVertexAttribArray(5);
- glVertexAttribIPointer(5, 4, GL_UNSIGNED_INT, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_color_offset * sizeof(float)));
- glVertexAttribDivisor(5, 1);
- }
- }
-
- GLenum primitive_gl = prim[int(primitive)];
- if (instance_count == 1) {
- if (use_index_buffer) {
- glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0);
- } else {
- glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface));
- }
- } else {
- if (use_index_buffer) {
- glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count);
- } else {
- glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count);
- }
- }
-
- state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
-
- state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size();
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- if (instance_count > 1) {
- glDisableVertexAttribArray(5);
- glDisableVertexAttribArray(6);
- glDisableVertexAttribArray(7);
- glDisableVertexAttribArray(8);
- }
- }
+ _add_to_batch(r_index, r_batch_broken);
} break;
+
case Item::Command::TYPE_TRANSFORM: {
const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
draw_transform = transform->xform;
@@ -950,30 +879,30 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c);
if (current_clip) {
if (ci->ignore != reclip) {
+ _new_batch(r_batch_broken, r_index);
if (ci->ignore) {
- glDisable(GL_SCISSOR_TEST);
+ state.canvas_instance_batches[state.current_batch_index].clip = nullptr;
reclip = true;
} else {
- // Scissor area is already set
- glEnable(GL_SCISSOR_TEST);
+ state.canvas_instance_batches[state.current_batch_index].clip = current_clip;
reclip = false;
}
}
}
} break;
+
case Item::Command::TYPE_ANIMATION_SLICE: {
- /*
const Item::CommandAnimationSlice *as = static_cast<const Item::CommandAnimationSlice *>(c);
- double current_time = RendererCompositorRD::singleton->get_total_time();
+ double current_time = RSG::rasterizer->get_total_time();
double local_time = Math::fposmod(current_time - as->offset, as->animation_length);
skipping = !(local_time >= as->slice_begin && local_time < as->slice_end);
RenderingServerDefault::redraw_request(); // animation visible means redraw request
- */
} break;
}
c = c->next;
+ r_batch_broken = false;
}
if (current_clip && reclip) {
@@ -982,66 +911,245 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item
}
}
-void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) {
- if (r_index > 0) {
- _bind_instance_data_buffer(r_index);
- glBindVertexArray(data.canvas_quad_array);
- if (state.current_primitive_points == 0) {
- glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, r_index);
- } else {
- static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES };
- glDrawArraysInstanced(prim[state.current_primitive_points], 0, state.current_primitive_points, r_index);
- }
- glBindBuffer(GL_UNIFORM_BUFFER, 0);
+void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) {
+ ERR_FAIL_COND(!state.canvas_instance_batches[state.current_batch_index].command);
- state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
- state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size();
- //copy the new data into the base of the batch
- for (int i = 0; i < 4; i++) {
- state.instance_data_array[0].modulation[i] = state.instance_data_array[r_index].modulation[i];
- state.instance_data_array[0].ninepatch_margins[i] = state.instance_data_array[r_index].ninepatch_margins[i];
- state.instance_data_array[0].src_rect[i] = state.instance_data_array[r_index].src_rect[i];
- state.instance_data_array[0].dst_rect[i] = state.instance_data_array[r_index].dst_rect[i];
- state.instance_data_array[0].lights[i] = state.instance_data_array[r_index].lights[i];
- }
- state.instance_data_array[0].flags = state.instance_data_array[r_index].flags;
- state.instance_data_array[0].color_texture_pixel_size[0] = state.instance_data_array[r_index].color_texture_pixel_size[0];
- state.instance_data_array[0].color_texture_pixel_size[1] = state.instance_data_array[r_index].color_texture_pixel_size[1];
-
- state.instance_data_array[0].pad[0] = state.instance_data_array[r_index].pad[0];
- state.instance_data_array[0].pad[1] = state.instance_data_array[r_index].pad[1];
- for (int i = 0; i < 6; i++) {
- state.instance_data_array[0].world[i] = state.instance_data_array[r_index].world[i];
- }
+ // Used by Polygon and Mesh.
+ static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP };
- r_index = 0;
+ _bind_canvas_texture(state.canvas_instance_batches[p_index].tex, state.canvas_instance_batches[p_index].filter, state.canvas_instance_batches[p_index].repeat);
+
+ // Bind the region of the UBO used by this batch.
+ // If region exceeds the boundary of the UBO, just ignore.
+ uint32_t range_bytes = data.max_instances_per_batch * sizeof(InstanceData);
+ if (state.canvas_instance_batches[p_index].start >= (data.max_instances_per_ubo - 1) * sizeof(InstanceData)) {
+ return;
+ } else if (state.canvas_instance_batches[p_index].start >= (data.max_instances_per_ubo - data.max_instances_per_batch) * sizeof(InstanceData)) {
+ // If we have less than a full batch at the end, we can just draw it anyway.
+ // OpenGL will complain about the UBO being smaller than expected, but it should render fine.
+ range_bytes = (data.max_instances_per_ubo - 1) * sizeof(InstanceData) - state.canvas_instance_batches[p_index].start;
+ }
+
+ uint32_t range_start = state.canvas_instance_batches[p_index].start;
+ glBindBufferRange(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].ubo, range_start, range_bytes);
+
+ switch (state.canvas_instance_batches[p_index].command_type) {
+ case Item::Command::TYPE_RECT:
+ case Item::Command::TYPE_NINEPATCH: {
+ glBindVertexArray(data.indexed_quad_array);
+ glDrawElements(GL_TRIANGLES, state.canvas_instance_batches[p_index].instance_count * 6, GL_UNSIGNED_INT, 0);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ glBindVertexArray(0);
+
+ } break;
+
+ case Item::Command::TYPE_POLYGON: {
+ const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(state.canvas_instance_batches[p_index].command);
+
+ PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id);
+ ERR_FAIL_COND(!pb);
+
+ glBindVertexArray(pb->vertex_array);
+
+ if (pb->color_disabled && pb->color != Color(1.0, 1.0, 1.0, 1.0)) {
+ glVertexAttrib4f(RS::ARRAY_COLOR, pb->color.r, pb->color.g, pb->color.b, pb->color.a);
+ }
+
+ if (pb->index_buffer != 0) {
+ glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr);
+ } else {
+ glDrawArrays(prim[polygon->primitive], 0, pb->count);
+ }
+ glBindVertexArray(0);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ if (pb->color_disabled && pb->color != Color(1.0, 1.0, 1.0, 1.0)) {
+ // Reset so this doesn't pollute other draw calls.
+ glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0);
+ }
+ } break;
+
+ case Item::Command::TYPE_PRIMITIVE: {
+ glBindVertexArray(data.canvas_quad_array);
+ const GLenum primitive[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES };
+ int instance_count = state.canvas_instance_batches[p_index].instance_count;
+ if (instance_count > 1) {
+ glDrawArraysInstanced(primitive[state.canvas_instance_batches[p_index].primitive_points], 0, state.canvas_instance_batches[p_index].primitive_points, instance_count);
+ } else {
+ glDrawArrays(primitive[state.canvas_instance_batches[p_index].primitive_points], 0, state.canvas_instance_batches[p_index].primitive_points);
+ }
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ } break;
+
+ case Item::Command::TYPE_MESH:
+ case Item::Command::TYPE_MULTIMESH:
+ case Item::Command::TYPE_PARTICLES: {
+ GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
+ RID mesh;
+ RID mesh_instance;
+ RID texture;
+ uint32_t instance_count = 1;
+ GLuint multimesh_buffer = 0;
+ uint32_t multimesh_stride = 0;
+ uint32_t multimesh_color_offset = 0;
+ bool multimesh_uses_color = false;
+ bool multimesh_uses_custom_data = false;
+
+ if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_MESH) {
+ const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(state.canvas_instance_batches[p_index].command);
+ mesh = m->mesh;
+ mesh_instance = m->mesh_instance;
+ } else if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_MULTIMESH) {
+ const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(state.canvas_instance_batches[p_index].command);
+ RID multimesh = mm->multimesh;
+ mesh = mesh_storage->multimesh_get_mesh(multimesh);
+
+ if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) {
+ break;
+ }
+
+ instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh);
+
+ if (instance_count == 0) {
+ break;
+ }
+
+ multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh);
+ multimesh_stride = mesh_storage->multimesh_get_stride(multimesh);
+ multimesh_color_offset = mesh_storage->multimesh_get_color_offset(multimesh);
+ multimesh_uses_color = mesh_storage->multimesh_uses_colors(multimesh);
+ multimesh_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh);
+ } else if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_PARTICLES) {
+ // Do nothing for now.
+ }
+
+ ERR_FAIL_COND(mesh.is_null());
+
+ uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh);
+
+ for (uint32_t j = 0; j < surf_count; j++) {
+ void *surface = mesh_storage->mesh_get_surface(mesh, j);
+
+ RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface);
+ ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX);
+
+ GLuint vertex_array_gl = 0;
+ GLuint index_array_gl = 0;
+
+ uint32_t input_mask = 0; // 2D meshes always use the same vertex format
+ if (mesh_instance.is_valid()) {
+ mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl);
+ } else {
+ mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array_gl);
+ }
+
+ index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0);
+ bool use_index_buffer = false;
+ glBindVertexArray(vertex_array_gl);
+ if (index_array_gl != 0) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl);
+ use_index_buffer = true;
+ }
+
+ if (instance_count > 1) {
+ // Bind instance buffers.
+ glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer);
+ glEnableVertexAttribArray(1);
+ glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0));
+ glVertexAttribDivisor(1, 1);
+ glEnableVertexAttribArray(2);
+ glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4));
+ glVertexAttribDivisor(2, 1);
+
+ if (multimesh_uses_color || multimesh_uses_custom_data) {
+ glEnableVertexAttribArray(5);
+ glVertexAttribIPointer(5, 4, GL_UNSIGNED_INT, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_color_offset * sizeof(float)));
+ glVertexAttribDivisor(5, 1);
+ }
+ }
+
+ GLenum primitive_gl = prim[int(primitive)];
+ if (instance_count == 1) {
+ if (use_index_buffer) {
+ glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0);
+ } else {
+ glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface));
+ }
+ } else if (instance_count > 1) {
+ if (use_index_buffer) {
+ glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count);
+ } else {
+ glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count);
+ }
+ }
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ if (instance_count > 1) {
+ glDisableVertexAttribArray(5);
+ glDisableVertexAttribArray(6);
+ glDisableVertexAttribArray(7);
+ glDisableVertexAttribArray(8);
+ }
+ }
+
+ } break;
+ case Item::Command::TYPE_TRANSFORM:
+ case Item::Command::TYPE_CLIP_IGNORE:
+ case Item::Command::TYPE_ANIMATION_SLICE: {
+ // Can ignore these as they only impact batch creation.
+ } break;
}
}
-void RasterizerCanvasGLES3::_bind_instance_data_buffer(uint32_t p_max_index) {
- if (p_max_index == 0) {
+void RasterizerCanvasGLES3::_add_to_batch(uint32_t &r_index, bool &r_batch_broken) {
+ if (r_index >= data.max_instances_per_ubo - 1) {
+ WARN_PRINT_ONCE("Trying to draw too many items. Please increase maximum number of items in the project settings 'rendering/gl_compatibility/item_buffer_size'");
return;
}
- // If the previous operation is not done yet, allocate a new buffer
- if (state.fences[state.current_buffer] != GLsync()) {
- GLint syncStatus;
- glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus);
- if (syncStatus == GL_UNSIGNALED) {
- _allocate_instance_data_buffer();
+
+ if (state.canvas_instance_batches[state.current_batch_index].instance_count >= data.max_instances_per_batch) {
+ _new_batch(r_batch_broken, r_index);
+ }
+
+ state.canvas_instance_batches[state.current_batch_index].instance_count++;
+ r_index++;
+}
+
+void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) {
+ if (state.canvas_instance_batches.size() == 0) {
+ state.canvas_instance_batches.push_back(Batch());
+ return;
+ }
+
+ if (r_batch_broken || state.canvas_instance_batches[state.current_batch_index].instance_count == 0) {
+ return;
+ }
+
+ r_batch_broken = true;
+
+ // Copy the properties of the current batch, we will manually update the things that changed.
+ Batch new_batch = state.canvas_instance_batches[state.current_batch_index];
+ new_batch.instance_count = 0;
+ new_batch.start = state.canvas_instance_batches[state.current_batch_index].start + state.canvas_instance_batches[state.current_batch_index].instance_count * sizeof(InstanceData);
+
+ state.current_batch_index++;
+ state.canvas_instance_batches.push_back(new_batch);
+ _align_instance_data_buffer(r_index);
+}
+
+void RasterizerCanvasGLES3::_bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant) {
+ if (p_material_data) {
+ if (p_material_data->shader_data->version.is_valid() && p_material_data->shader_data->valid) {
+ // Bind uniform buffer and textures
+ p_material_data->bind_uniforms();
+ GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(p_material_data->shader_data->version, p_variant);
} else {
- glDeleteSync(state.fences[state.current_buffer]);
+ GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant);
}
+ } else {
+ GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant);
}
-
- glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]);
-#ifdef WEB_ENABLED
- //WebGL 2.0 does not support mapping buffers, so use slow glBufferSubData instead
- glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * p_max_index, state.instance_data_array);
-#else
- void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * p_max_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
- memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * p_max_index);
- glUnmapBuffer(GL_UNIFORM_BUFFER);
-#endif
}
RID RasterizerCanvasGLES3::light_create() {
@@ -1100,12 +1208,12 @@ void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backb
glBindTexture(GL_TEXTURE_2D, render_target->backbuffer);
}
- if (render_target->is_transparent) {
+ if (render_target->is_transparent || p_to_backbuffer) {
state.transparent_render_target = true;
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} else {
state.transparent_render_target = false;
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
}
if (render_target && render_target->clear_requested) {
@@ -1121,30 +1229,28 @@ void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backb
glBindTexture(GL_TEXTURE_2D, tex->tex_id);
}
-void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index) {
+void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat) {
GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
if (p_texture == RID()) {
- p_texture = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE);
+ p_texture = default_canvas_texture;
}
- if (state.current_tex == p_texture) {
- return; //nothing to do, its the same
+ if (state.current_tex == p_texture && state.current_filter_mode == p_base_filter && state.current_repeat_mode == p_base_repeat) {
+ return;
}
+
state.current_tex = p_texture;
+ state.current_filter_mode = p_base_filter;
+ state.current_repeat_mode = p_base_repeat;
GLES3::CanvasTexture *ct = nullptr;
GLES3::Texture *t = texture_storage->get_texture(p_texture);
if (t) {
- //regular texture
- if (!t->canvas_texture) {
- t->canvas_texture = memnew(GLES3::CanvasTexture);
- t->canvas_texture->diffuse = p_texture;
- }
-
+ ERR_FAIL_COND(!t->canvas_texture);
ct = t->canvas_texture;
} else {
ct = texture_storage->get_canvas_texture(p_texture);
@@ -1152,7 +1258,7 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe
if (!ct) {
// Invalid Texture RID.
- _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index);
+ _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat);
return;
}
@@ -1165,20 +1271,12 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe
GLES3::Texture *texture = texture_storage->get_texture(ct->diffuse);
if (!texture) {
- state.current_tex = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE);
- GLES3::Texture *tex = texture_storage->get_texture(state.current_tex);
- state.current_tex_ptr = tex;
- ct->size_cache = Size2i(tex->width, tex->height);
+ GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE));
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex->tex_id);
} else {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture->tex_id);
-
- state.current_tex = p_texture;
- state.current_tex_ptr = texture;
- ct->size_cache = Size2i(texture->width, texture->height);
-
texture->gl_set_filter(filter);
texture->gl_set_repeat(repeat);
}
@@ -1186,45 +1284,78 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe
GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map);
if (!normal_map) {
- state.current_normal = RID();
- ct->use_normal_cache = false;
- glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 6);
+ glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6);
GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_NORMAL));
glBindTexture(GL_TEXTURE_2D, tex->tex_id);
-
} else {
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6);
glBindTexture(GL_TEXTURE_2D, normal_map->tex_id);
- state.current_normal = ct->normal_map;
- ct->use_normal_cache = true;
- texture->gl_set_filter(filter);
- texture->gl_set_repeat(repeat);
+ normal_map->gl_set_filter(filter);
+ normal_map->gl_set_repeat(repeat);
}
GLES3::Texture *specular_map = texture_storage->get_texture(ct->specular);
if (!specular_map) {
- state.current_specular = RID();
- ct->use_specular_cache = false;
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE));
glBindTexture(GL_TEXTURE_2D, tex->tex_id);
} else {
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
glBindTexture(GL_TEXTURE_2D, specular_map->tex_id);
- state.current_specular = ct->specular;
- ct->use_specular_cache = true;
- texture->gl_set_filter(filter);
- texture->gl_set_repeat(repeat);
+ specular_map->gl_set_filter(filter);
+ specular_map->gl_set_repeat(repeat);
}
+}
- if (ct->use_specular_cache) {
+void RasterizerCanvasGLES3::_prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size) {
+ GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
+
+ if (p_texture == RID()) {
+ p_texture = default_canvas_texture;
+ }
+
+ GLES3::CanvasTexture *ct = nullptr;
+
+ GLES3::Texture *t = texture_storage->get_texture(p_texture);
+
+ if (t) {
+ //regular texture
+ if (!t->canvas_texture) {
+ t->canvas_texture = memnew(GLES3::CanvasTexture);
+ t->canvas_texture->diffuse = p_texture;
+ }
+
+ ct = t->canvas_texture;
+ } else {
+ ct = texture_storage->get_canvas_texture(p_texture);
+ }
+
+ if (!ct) {
+ // Invalid Texture RID.
+ _prepare_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index, r_texpixel_size);
+ return;
+ }
+
+ GLES3::Texture *texture = texture_storage->get_texture(ct->diffuse);
+ Size2i size_cache;
+ if (!texture) {
+ ct->diffuse = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE);
+ GLES3::Texture *tex = texture_storage->get_texture(ct->diffuse);
+ size_cache = Size2i(tex->width, tex->height);
+ } else {
+ size_cache = Size2i(texture->width, texture->height);
+ }
+
+ GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map);
+
+ if (ct->specular_color.a < 0.999) {
state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
} else {
state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
- if (ct->use_normal_cache) {
+ if (!normal_map) {
state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
} else {
state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED;
@@ -1235,11 +1366,11 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe
state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8;
state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255));
- state.current_pixel_size.x = 1.0 / float(ct->size_cache.x);
- state.current_pixel_size.y = 1.0 / float(ct->size_cache.y);
+ r_texpixel_size.x = 1.0 / float(size_cache.x);
+ r_texpixel_size.y = 1.0 / float(size_cache.y);
- state.instance_data_array[r_index].color_texture_pixel_size[0] = state.current_pixel_size.x;
- state.instance_data_array[r_index].color_texture_pixel_size[1] = state.current_pixel_size.y;
+ state.instance_data_array[r_index].color_texture_pixel_size[0] = r_texpixel_size.x;
+ state.instance_data_array[r_index].color_texture_pixel_size[1] = r_texpixel_size.y;
}
void RasterizerCanvasGLES3::reset_canvas() {
@@ -1431,20 +1562,43 @@ void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) {
// Creates a new uniform buffer and uses it right away
// This expands the instance buffer continually
-// In theory allocations can reach as high as number_of_draw_calls * 3 frames
+// In theory allocations can reach as high as number of windows * 3 frames
// because OpenGL can start rendering subsequent frames before finishing the current one
void RasterizerCanvasGLES3::_allocate_instance_data_buffer() {
GLuint new_buffer;
glGenBuffers(1, &new_buffer);
glBindBuffer(GL_UNIFORM_BUFFER, new_buffer);
- glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW);
+ glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_DYNAMIC_DRAW);
state.current_buffer = (state.current_buffer + 1);
- state.canvas_instance_data_buffers.insert(state.current_buffer, new_buffer);
- state.fences.insert(state.current_buffer, GLsync());
+ DataBuffer db;
+ db.ubo = new_buffer;
+ db.last_frame_used = RSG::rasterizer->get_frame_number();
+ state.canvas_instance_data_buffers.insert(state.current_buffer, db);
state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size();
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
+// Batch start positions need to be aligned to the device's GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT.
+// This needs to be called anytime a new batch is created.
+void RasterizerCanvasGLES3::_align_instance_data_buffer(uint32_t &r_index) {
+ if (GLES3::Config::get_singleton()->uniform_buffer_offset_alignment > int(sizeof(InstanceData))) {
+ uint32_t offset = state.canvas_instance_batches[state.current_batch_index].start % GLES3::Config::get_singleton()->uniform_buffer_offset_alignment;
+ if (offset > 0) {
+ // uniform_buffer_offset_alignment can be 4, 16, 32, or 256. Our instance batches are 128 bytes.
+ // Accordingly, this branch is only triggered if we are 128 bytes off.
+ uint32_t offset_bytes = GLES3::Config::get_singleton()->uniform_buffer_offset_alignment - offset;
+ state.canvas_instance_batches[state.current_batch_index].start += offset_bytes;
+ // Offset the instance array so it stays in sync with batch start points.
+ // This creates gaps in the instance buffer with wasted space, but we can't help it.
+ r_index += offset_bytes / sizeof(InstanceData);
+ if (r_index > 0) {
+ // In this case we need to copy over the basic data.
+ state.instance_data_array[r_index] = state.instance_data_array[r_index - 1];
+ }
+ }
+ }
+}
+
void RasterizerCanvasGLES3::set_time(double p_time) {
state.time = p_time;
}
@@ -1457,9 +1611,11 @@ RasterizerCanvasGLES3 *RasterizerCanvasGLES3::get_singleton() {
RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
singleton = this;
+ GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
GLES3::Config *config = GLES3::Config::get_singleton();
+ polygon_buffers.last_id = 1;
// quad buffer
{
glGenBuffers(1, &data.canvas_quad_vertices);
@@ -1583,25 +1739,58 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
int uniform_max_size = config->max_uniform_buffer_size;
if (uniform_max_size < 65536) {
- state.max_lights_per_render = 64;
- state.max_instances_per_batch = 128;
+ data.max_lights_per_render = 64;
+ data.max_instances_per_batch = 128;
} else {
- state.max_lights_per_render = 256;
- state.max_instances_per_batch = 512;
+ data.max_lights_per_render = 256;
+ data.max_instances_per_batch = 512;
}
- // Reserve 64 Uniform Buffers for instance data
- state.canvas_instance_data_buffers.resize(64);
- state.fences.resize(64);
- glGenBuffers(64, state.canvas_instance_data_buffers.ptr());
- for (int i = 0; i < 64; i++) {
- state.fences[i] = GLsync();
- glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_instance_data_buffers[i]);
- glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW);
+ // Reserve 3 Uniform Buffers for instance data Frame N, N+1 and N+2
+ data.max_instances_per_ubo = MAX(data.max_instances_per_batch, uint32_t(GLOBAL_GET("rendering/gl_compatibility/item_buffer_size")));
+ data.max_instance_buffer_size = data.max_instances_per_ubo * sizeof(InstanceData); // 16,384 instances * 128 bytes = 2,097,152 bytes = 2,048 kb
+ state.canvas_instance_data_buffers.resize(3);
+ state.canvas_instance_batches.reserve(200);
+
+ for (int i = 0; i < 3; i++) {
+ GLuint new_buffer;
+ glGenBuffers(1, &new_buffer);
+ glBindBuffer(GL_UNIFORM_BUFFER, new_buffer);
+ glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_DYNAMIC_DRAW);
+ DataBuffer db;
+ db.ubo = new_buffer;
+ db.last_frame_used = 0;
+ db.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ state.canvas_instance_data_buffers[i] = db;
}
glBindBuffer(GL_UNIFORM_BUFFER, 0);
- state.instance_data_array = memnew_arr(InstanceData, state.max_instances_per_batch);
+ state.instance_data_array = memnew_arr(InstanceData, data.max_instances_per_ubo);
+
+ {
+ const uint32_t no_of_instances = data.max_instances_per_batch;
+
+ glGenVertexArrays(1, &data.indexed_quad_array);
+ glBindVertexArray(data.indexed_quad_array);
+ glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices);
+
+ const uint32_t num_indices = 6;
+ const uint32_t quad_indices[num_indices] = { 0, 2, 1, 3, 2, 0 };
+
+ const uint32_t total_indices = no_of_instances * num_indices;
+ uint32_t *indices = new uint32_t[total_indices];
+ for (uint32_t i = 0; i < total_indices; i++) {
+ uint32_t quad = i / num_indices;
+ uint32_t quad_local = i % num_indices;
+ indices[i] = quad_indices[quad_local] + quad * num_indices;
+ }
+
+ glGenBuffers(1, &data.indexed_quad_buffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.indexed_quad_buffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * total_indices, indices, GL_STATIC_DRAW);
+ glBindVertexArray(0);
+ delete[] indices;
+ }
glGenBuffers(1, &state.canvas_state_buffer);
glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_state_buffer);
@@ -1610,12 +1799,12 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() {
String global_defines;
global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now
- global_defines += "#define MAX_LIGHTS " + itos(state.max_instances_per_batch) + "\n";
- global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(state.max_instances_per_batch) + "\n";
+ global_defines += "#define MAX_LIGHTS " + itos(data.max_instances_per_batch) + "\n";
+ global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(data.max_instances_per_batch) + "\n";
GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines);
- state.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create();
- GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD);
+ data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create();
+ GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD);
{
default_canvas_group_shader = material_storage->shader_allocate();
@@ -1642,16 +1831,16 @@ void fragment() {
material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader);
}
- state.current_shader_version = state.canvas_shader_default_version;
+ default_canvas_texture = texture_storage->canvas_texture_allocate();
+ texture_storage->canvas_texture_initialize(default_canvas_texture);
+
state.time = 0.0;
}
RasterizerCanvasGLES3::~RasterizerCanvasGLES3() {
GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
- memdelete_arr(state.instance_data_array);
-
- GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_free(state.canvas_shader_default_version);
+ material_storage->shaders.canvas_shader.version_free(data.canvas_shader_default_version);
material_storage->material_free(default_canvas_group_material);
material_storage->shader_free(default_canvas_group_shader);
singleton = nullptr;
@@ -1661,6 +1850,9 @@ RasterizerCanvasGLES3::~RasterizerCanvasGLES3() {
glDeleteBuffers(1, &data.canvas_quad_vertices);
glDeleteVertexArrays(1, &data.canvas_quad_array);
+
+ GLES3::TextureStorage::get_singleton()->canvas_texture_free(default_canvas_texture);
+ memfree(state.instance_data_array);
}
#endif // GLES3_ENABLED
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 15556e3193..15c2ca5710 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -125,6 +125,23 @@ public:
uint32_t pad2;
};
+ struct PolygonBuffers {
+ GLuint vertex_buffer;
+ GLuint vertex_array;
+ GLuint index_buffer;
+ int count = 0;
+ bool color_disabled = false;
+ Color color;
+ };
+
+ struct {
+ HashMap<PolygonID, PolygonBuffers> polygons;
+ PolygonID last_id = 0;
+ } polygon_buffers;
+
+ RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override;
+ void free_polygon(PolygonID p_polygon) override;
+
struct InstanceData {
float world[6];
float color_texture_pixel_size[2];
@@ -156,42 +173,71 @@ public:
GLuint canvas_quad_vertices;
GLuint canvas_quad_array;
+ GLuint indexed_quad_buffer;
+ GLuint indexed_quad_array;
+
GLuint particle_quad_vertices;
GLuint particle_quad_array;
GLuint ninepatch_vertices;
GLuint ninepatch_elements;
+
+ RID canvas_shader_default_version;
+
+ uint32_t max_lights_per_render;
+ uint32_t max_lights_per_item;
+ uint32_t max_instances_per_batch = 512;
+ uint32_t max_instances_per_ubo = 16384;
+ uint32_t max_instance_buffer_size = 16384 * 128;
} data;
+ struct Batch {
+ // Position in the UBO measured in bytes
+ uint32_t start = 0;
+ uint32_t instance_count = 0;
+
+ RID tex = RID();
+ RS::CanvasItemTextureFilter filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX;
+ RS::CanvasItemTextureRepeat repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
+
+ GLES3::CanvasShaderData::BlendMode blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX;
+ Color blend_color = Color(1.0, 1.0, 1.0, 1.0);
+
+ Item *clip = nullptr;
+
+ RID material = RID();
+ GLES3::CanvasMaterialData *material_data = nullptr;
+ CanvasShaderGLES3::ShaderVariant shader_variant = CanvasShaderGLES3::MODE_QUAD;
+
+ const Item::Command *command = nullptr;
+ Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch.
+ uint32_t primitive_points = 0;
+ };
+
+ struct DataBuffer {
+ GLuint ubo = 0;
+ uint64_t last_frame_used = -3;
+ GLsync fence = GLsync();
+ };
+
struct State {
GLuint canvas_state_buffer;
- LocalVector<GLuint> canvas_instance_data_buffers;
- LocalVector<GLsync> fences;
+ LocalVector<DataBuffer> canvas_instance_data_buffers;
+ LocalVector<Batch> canvas_instance_batches;
uint32_t current_buffer = 0;
+ uint32_t current_buffer_index = 0;
+ uint32_t current_batch_index = 0;
InstanceData *instance_data_array = nullptr;
- bool canvas_texscreen_used;
- RID canvas_shader_current_version;
- RID canvas_shader_default_version;
RID current_tex = RID();
- Size2 current_pixel_size = Size2();
- RID current_normal = RID();
- RID current_specular = RID();
- GLES3::Texture *current_tex_ptr;
- RID current_shader_version = RID();
- RS::PrimitiveType current_primitive = RS::PRIMITIVE_MAX;
- uint32_t current_primitive_points = 0;
- Item::Command::Type current_command = Item::Command::TYPE_RECT;
+ RS::CanvasItemTextureFilter current_filter_mode = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX;
+ RS::CanvasItemTextureRepeat current_repeat_mode = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX;
bool transparent_render_target = false;
double time = 0.0;
- uint32_t max_lights_per_render;
- uint32_t max_lights_per_item;
- uint32_t max_instances_per_batch;
-
RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
} state;
@@ -229,31 +275,18 @@ public:
bool free(RID p_rid) override;
void update() override;
- void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index);
-
- struct PolygonBuffers {
- GLuint vertex_buffer;
- GLuint vertex_array;
- GLuint index_buffer;
- int count = 0;
- bool color_disabled = false;
- Color color;
- };
-
- struct {
- HashMap<PolygonID, PolygonBuffers> polygons;
- PolygonID last_id = 0;
- } polygon_buffers;
-
- RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override;
- void free_polygon(PolygonID p_polygon) override;
+ void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat);
+ void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size);
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override;
- void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false);
- void _render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, uint32_t &r_index, GLES3::CanvasShaderData::BlendMode p_blend_mode, GLES3::CanvasShaderData::BlendMode &r_last_blend_mode, Color &r_last_blend_color);
- void _render_batch(uint32_t &p_max_index);
- void _bind_instance_data_buffer(uint32_t p_max_index);
+ void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool p_to_backbuffer = false);
+ void _record_item_commands(const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch);
+ void _render_batch(Light *p_lights, uint32_t p_index);
+ void _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant);
+ void _new_batch(bool &r_batch_broken, uint32_t &r_index);
+ void _add_to_batch(uint32_t &r_index, bool &r_batch_broken);
void _allocate_instance_data_buffer();
+ void _align_instance_data_buffer(uint32_t &r_index);
void set_time(double p_time);
diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h
index 97543af0d5..431515fd0d 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -103,8 +103,9 @@ public:
low_end = true;
}
- uint64_t get_frame_number() const { return frame; }
- double get_frame_delta_time() const { return delta; }
+ _ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; }
+ _ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; }
+ _ALWAYS_INLINE_ double get_total_time() const { return time_total; }
RasterizerGLES3();
~RasterizerGLES3();
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 8dae28b6ef..a177112476 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -60,20 +60,18 @@ out vec2 pixel_size_interp;
void main() {
vec4 instance_custom = vec4(0.0);
- draw_data_instance = gl_InstanceID;
-#ifdef USE_PRIMITIVE
- //weird bug,
- //this works
+#ifdef USE_PRIMITIVE
+ draw_data_instance = gl_InstanceID;
vec2 vertex;
vec2 uv;
vec4 color;
- if (gl_VertexID == 0) {
+ if (gl_VertexID % 3 == 0) {
vertex = draw_data[draw_data_instance].point_a;
uv = draw_data[draw_data_instance].uv_a;
color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_a_rg), unpackHalf2x16(draw_data[draw_data_instance].color_a_ba));
- } else if (gl_VertexID == 1) {
+ } else if (gl_VertexID % 3 == 1) {
vertex = draw_data[draw_data_instance].point_b;
uv = draw_data[draw_data_instance].uv_b;
color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_b_rg), unpackHalf2x16(draw_data[draw_data_instance].color_b_ba));
@@ -86,6 +84,7 @@ void main() {
vec4 bone_weights = vec4(0.0);
#elif defined(USE_ATTRIBUTES)
+ draw_data_instance = gl_InstanceID;
#ifdef USE_INSTANCING
draw_data_instance = 0;
#endif
@@ -103,9 +102,9 @@ void main() {
#endif
#else
-
- vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
- vec2 vertex_base = vertex_base_arr[gl_VertexID];
+ draw_data_instance = gl_VertexID / 6;
+ vec2 vertex_base_arr[6] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0));
+ vec2 vertex_base = vertex_base_arr[gl_VertexID % 6];
vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy);
vec4 color = draw_data[draw_data_instance].modulation;
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp
index 6cc65e7bb2..242c1ce0a9 100644
--- a/drivers/gles3/storage/config.cpp
+++ b/drivers/gles3/storage/config.cpp
@@ -86,6 +86,8 @@ Config::Config() {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size);
+ glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_offset_alignment);
+
// the use skeleton software path should be used if either float texture is not supported,
// OR max_vertex_texture_image_units is zero
use_skeleton_software = (float_texture_supported == false) || (max_vertex_texture_image_units == 0);
diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h
index b83c83f425..fe18345775 100644
--- a/drivers/gles3/storage/config.h
+++ b/drivers/gles3/storage/config.h
@@ -64,6 +64,8 @@ public:
int max_renderable_lights = 0;
int max_lights_per_object = 0;
+ int uniform_buffer_offset_alignment = 0;
+
// TODO implement wireframe in OpenGL
// bool generate_wireframes;
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 5950997f9b..442bd69b55 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1533,9 +1533,11 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) {
if (!rt->clear_requested) {
return;
}
+ glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo);
glClearBufferfv(GL_COLOR, 0, rt->clear_color.components);
rt->clear_requested = false;
+ glBindFramebuffer(GL_FRAMEBUFFER, system_fbo);
}
void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) {
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index 0d4cd9c7e3..7e083e48e8 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -126,11 +126,6 @@ struct CanvasTexture {
RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
-
- Size2i size_cache = Size2i(1, 1);
- bool use_normal_cache = false;
- bool use_specular_cache = false;
- bool cleared_cache = true;
};
/* CANVAS SHADOW */