Implement CanvasGroup and CanvasItem clipping

-Allows merging several 2D objects into a single draw operation
-Use current node to clip children nodes
-Further fixes to Vulkan barriers
-Changed font texture generation to white, fixes dark eges when blurred
-Other small misc fixes to backbuffer code.
This commit is contained in:
reduz 2020-10-28 15:34:27 -03:00
parent 2eaedcf14e
commit 8ab9b39707
20 changed files with 562 additions and 56 deletions

View file

@ -167,8 +167,15 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo
p_z = ci->z_index;
}
RasterizerCanvas::Item *canvas_group_from = nullptr;
bool use_canvas_group = ci->canvas_group != nullptr && (ci->canvas_group->fit_empty || ci->commands != nullptr);
if (use_canvas_group) {
int zidx = p_z - RS::CANVAS_ITEM_Z_MIN;
canvas_group_from = z_last_list[zidx];
}
for (int i = 0; i < child_item_count; i++) {
if (!child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y)) {
if ((!child_items[i]->behind && !use_canvas_group) || (ci->sort_y && child_items[i]->sort_y)) {
continue;
}
if (ci->sort_y) {
@ -182,6 +189,70 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo
ci->copy_back_buffer->screen_rect = xform.xform(ci->copy_back_buffer->rect).clip(p_clip_rect);
}
if (use_canvas_group) {
int zidx = p_z - RS::CANVAS_ITEM_Z_MIN;
if (canvas_group_from == nullptr) {
// no list before processing this item, means must put stuff in group from the beginning of list.
canvas_group_from = z_list[zidx];
} else {
// there was a list before processing, so begin group from this one.
canvas_group_from = canvas_group_from->next;
}
if (canvas_group_from) {
// Has a place to begin the group from!
//compute a global rect (in global coords) for children in the same z layer
Rect2 rect_accum;
RasterizerCanvas::Item *c = canvas_group_from;
while (c) {
if (c == canvas_group_from) {
rect_accum = c->global_rect_cache;
} else {
rect_accum = rect_accum.merge(c->global_rect_cache);
}
c = c->next;
}
// We have two choices now, if user has drawn something, we must assume users wants to draw the "mask", so compute the size based on this.
// If nothing has been drawn, we just take it over and draw it ourselves.
if (ci->canvas_group->fit_empty && (ci->commands == nullptr ||
(ci->commands->next == nullptr && ci->commands->type == Item::Command::TYPE_RECT && (static_cast<Item::CommandRect *>(ci->commands)->flags & RasterizerCanvas::CANVAS_RECT_IS_GROUP)))) {
// No commands, or sole command is the one used to draw, so we (re)create the draw command.
ci->clear();
if (rect_accum == Rect2()) {
rect_accum.size = Size2(1, 1);
}
rect_accum = rect_accum.grow(ci->canvas_group->fit_margin);
//draw it?
RasterizerCanvas::Item::CommandRect *crect = ci->alloc_command<RasterizerCanvas::Item::CommandRect>();
crect->flags = RasterizerCanvas::CANVAS_RECT_IS_GROUP; // so we can recognize it later
crect->rect = xform.affine_inverse().xform(rect_accum);
crect->modulate = Color(1, 1, 1, 1);
//the global rect is used to do the copying, so update it
global_rect = rect_accum.grow(ci->canvas_group->clear_margin); //grow again by clear margin
global_rect.position += p_clip_rect.position;
} else {
global_rect.position -= p_clip_rect.position;
global_rect = global_rect.merge(rect_accum); //must use both rects for this
global_rect = global_rect.grow(ci->canvas_group->clear_margin); //grow by clear margin
global_rect.position += p_clip_rect.position;
}
// Very important that this is cleared after used in RasterizerCanvas to avoid
// potential crashes.
canvas_group_from->canvas_group_owner = ci;
}
}
if (ci->update_when_visible) {
RenderingServerRaster::redraw_request();
}
@ -211,7 +282,7 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo
}
for (int i = 0; i < child_item_count; i++) {
if (child_items[i]->behind || (ci->sort_y && child_items[i]->sort_y)) {
if (child_items[i]->behind || use_canvas_group || (ci->sort_y && child_items[i]->sort_y)) {
continue;
}
if (ci->sort_y) {
@ -935,6 +1006,27 @@ void RenderingServerCanvas::canvas_item_set_use_parent_material(RID p_item, bool
canvas_item->use_parent_material = p_enable;
}
void RenderingServerCanvas::canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin, bool p_fit_empty, float p_fit_margin, bool p_blur_mipmaps) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
if (p_mode == RS::CANVAS_GROUP_MODE_DISABLED) {
if (canvas_item->canvas_group != nullptr) {
memdelete(canvas_item->canvas_group);
canvas_item->canvas_group = nullptr;
}
} else {
if (canvas_item->canvas_group == nullptr) {
canvas_item->canvas_group = memnew(RasterizerCanvas::Item::CanvasGroup);
}
canvas_item->canvas_group->mode = p_mode;
canvas_item->canvas_group->fit_empty = p_fit_empty;
canvas_item->canvas_group->fit_margin = p_fit_margin;
canvas_item->canvas_group->blur_mipmaps = p_blur_mipmaps;
canvas_item->canvas_group->clear_margin = p_clear_margin;
}
}
RID RenderingServerCanvas::canvas_light_create() {
RasterizerCanvas::Light *clight = memnew(RasterizerCanvas::Light);
clight->light_internal = RSG::canvas_render->light_create();