diff --git a/servers/visual/rasterizer/rasterizer.h b/servers/visual/rasterizer/rasterizer.h index b4061917c14..207949a6d40 100644 --- a/servers/visual/rasterizer/rasterizer.h +++ b/servers/visual/rasterizer/rasterizer.h @@ -740,6 +740,18 @@ public: struct Item { + //commands are allocated in blocks of 4k to improve performance + //and cache coherence. + //blocks always grow but never shrink. + + struct CommandBlock { + enum { + MAX_SIZE = 4096 + }; + uint32_t usage; + uint8_t *memory; + }; + struct Command { enum Type { @@ -755,6 +767,7 @@ public: TYPE_CLIP_IGNORE, }; + Command *next; Type type; virtual ~Command() {} }; @@ -871,7 +884,7 @@ public: //VS::MaterialBlendMode blend_mode; int light_mask; int z_final; - Vector commands; + mutable bool custom_rect; mutable bool rect_dirty; mutable Rect2 rect; @@ -903,8 +916,8 @@ public: return rect; //must update rect - int s = commands.size(); - if (s == 0) { + + if (commands == NULL) { rect = Rect2(); rect_dirty = false; @@ -915,11 +928,10 @@ public: bool found_xform = false; bool first = true; - const Item::Command *const *cmd = &commands[0]; + const Item::Command *c = commands; - for (int i = 0; i < s; i++) { + while (c) { - const Item::Command *c = cmd[i]; Rect2 r; switch (c->type) { @@ -981,12 +993,12 @@ public: const Item::CommandTransform *transform = static_cast(c); xf = transform->xform; found_xform = true; + + } //passthrough + default: { + c = c->next; continue; - } break; - - case Item::Command::TYPE_CLIP_IGNORE: { - - } break; + } } if (found_xform) { @@ -997,18 +1009,90 @@ public: if (first) { rect = r; first = false; - } else + } else { rect = rect.merge(r); + } + c = c->next; } rect_dirty = false; return rect; } + Command *commands; + Command *last_command; + Vector blocks; + uint32_t current_block; + + template + T *alloc_command() { + T *command; + if (commands == NULL) { + // As the most common use case of canvas items is to + // use only one command, the first is done with it's + // own allocation. The rest of them use blocks. + command = memnew(T); + command->next = NULL; + commands = command; + last_command = command; + } else { + //Subsequent commands go into a block. + + while (true) { + if (unlikely(current_block == (uint32_t)blocks.size())) { + // If we need more blocks, we allocate them + // (they won't be freed until this CanvasItem is + // deleted, though). + CommandBlock cb; + cb.memory = (uint8_t *)memalloc(CommandBlock::MAX_SIZE); + cb.usage = 0; + blocks.push_back(cb); + } + + CommandBlock *c = &blocks.write[current_block]; + size_t space_left = CommandBlock::MAX_SIZE - c->usage; + if (space_left < sizeof(T)) { + current_block++; + continue; + } + + //allocate block and add to the linked list + void *memory = c->memory + c->usage; + command = memnew_placement(memory, T); + command->next = NULL; + last_command->next = command; + last_command = command; + c->usage += sizeof(T); + break; + } + } + + rect_dirty = true; + return command; + } + void clear() { - for (int i = 0; i < commands.size(); i++) - memdelete(commands[i]); - commands.clear(); + Command *c = commands; + while (c) { + Command *n = c->next; + if (c == commands) { + memdelete(commands); + } else { + c->~Command(); + } + c = n; + } + { + uint32_t cbc = MIN((current_block + 1), blocks.size()); + CommandBlock *blockptr = blocks.ptrw(); + for (uint32_t i = 0; i < cbc; i++) { + blockptr[i].usage = 0; + } + } + + last_command = NULL; + commands = NULL; + current_block = 0; clip = false; rect_dirty = true; final_clip_owner = NULL; @@ -1016,6 +1100,9 @@ public: light_masked = false; } Item() { + commands = NULL; + last_command = NULL; + current_block = 0; light_mask = 1; vp_render = NULL; next = NULL; @@ -1035,6 +1122,9 @@ public: } virtual ~Item() { clear(); + for (int i = 0; i < blocks.size(); i++) { + memfree(blocks[i].memory); + } if (copy_back_buffer) memdelete(copy_back_buffer); } }; diff --git a/servers/visual/rasterizer/rasterizer_canvas_rd.cpp b/servers/visual/rasterizer/rasterizer_canvas_rd.cpp index 4d6910af326..d72be706772 100644 --- a/servers/visual/rasterizer/rasterizer_canvas_rd.cpp +++ b/servers/visual/rasterizer/rasterizer_canvas_rd.cpp @@ -495,9 +495,6 @@ Size2i RasterizerCanvasRD::_bind_texture_binding(TextureBindingID p_binding, RD: //////////////////// void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_item, RenderTargetFormat p_render_target_format, RD::TextureSamples p_samples, const Color &p_modulate, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip) { - int cc = p_item->commands.size(); - const Item::Command *const *commands = p_item->commands.ptr(); - //create an empty push constant PushConstant push_constant; Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; @@ -521,9 +518,9 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ bool reclip = false; - for (int i = 0; i < cc; i++) { + const Item::Command *c = p_item->commands; + while (c) { - const Item::Command *c = commands[i]; push_constant.flags = 0; //reset on each command for sanity switch (c->type) { @@ -1100,6 +1097,8 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_ } break; } + + c = c->next; } if (current_clip && reclip) { diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index e80f0e43e2e..8d71907df5f 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -184,7 +184,7 @@ void VisualServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transform2 VisualServerRaster::redraw_request(); } - if ((!ci->commands.empty() && p_clip_rect.intersects(global_rect)) || ci->vp_render || ci->copy_back_buffer) { + if ((ci->commands != NULL && p_clip_rect.intersects(global_rect)) || ci->vp_render || ci->copy_back_buffer) { //something to draw? ci->final_transform = xform; ci->final_modulate = Color(modulate.r * ci->self_modulate.r, modulate.g * ci->self_modulate.g, modulate.b * ci->self_modulate.b, modulate.a * ci->self_modulate.a); @@ -487,7 +487,7 @@ void VisualServerCanvas::canvas_item_add_line(RID p_item, const Point2 &p_from, Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandPrimitive *line = memnew(Item::CommandPrimitive); + Item::CommandPrimitive *line = canvas_item->alloc_command(); ERR_FAIL_COND(!line); if (p_width > 1.001) { @@ -506,9 +506,6 @@ void VisualServerCanvas::canvas_item_add_line(RID p_item, const Point2 &p_from, line->colors[i] = p_color; } line->specular_shininess = Color(1, 1, 1, 1); - - canvas_item->rect_dirty = true; - canvas_item->commands.push_back(line); } void VisualServerCanvas::canvas_item_add_polyline(RID p_item, const Vector &p_points, const Vector &p_colors, float p_width) { @@ -517,7 +514,7 @@ void VisualServerCanvas::canvas_item_add_polyline(RID p_item, const Vectoralloc_command(); ERR_FAIL_COND(!pline); pline->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, RID(), RID(), RID(), VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, RID()); @@ -601,8 +598,6 @@ void VisualServerCanvas::canvas_item_add_polyline(RID p_item, const Vectorrect_dirty = true; - canvas_item->commands.push_back(pline); } void VisualServerCanvas::canvas_item_add_multiline(RID p_item, const Vector &p_points, const Vector &p_colors, float p_width) { @@ -611,7 +606,7 @@ void VisualServerCanvas::canvas_item_add_multiline(RID p_item, const Vectoralloc_command(); ERR_FAIL_COND(!pline); pline->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, RID(), RID(), RID(), VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, RID()); @@ -624,9 +619,6 @@ void VisualServerCanvas::canvas_item_add_multiline(RID p_item, const Vectorpolygon.create(Vector(), p_points, p_colors); } else { } - - canvas_item->rect_dirty = true; - canvas_item->commands.push_back(pline); } void VisualServerCanvas::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, const Color &p_color) { @@ -634,13 +626,10 @@ void VisualServerCanvas::canvas_item_add_rect(RID p_item, const Rect2 &p_rect, c Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandRect *rect = memnew(Item::CommandRect); + Item::CommandRect *rect = canvas_item->alloc_command(); ERR_FAIL_COND(!rect); rect->modulate = p_color; rect->rect = p_rect; - canvas_item->rect_dirty = true; - - canvas_item->commands.push_back(rect); } void VisualServerCanvas::canvas_item_add_circle(RID p_item, const Point2 &p_pos, float p_radius, const Color &p_color) { @@ -648,7 +637,7 @@ void VisualServerCanvas::canvas_item_add_circle(RID p_item, const Point2 &p_pos, Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandPolygon *circle = memnew(Item::CommandPolygon); + Item::CommandPolygon *circle = canvas_item->alloc_command(); ERR_FAIL_COND(!circle); circle->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, RID(), RID(), RID(), VS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST, VS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED, RID()); @@ -679,9 +668,6 @@ void VisualServerCanvas::canvas_item_add_circle(RID p_item, const Point2 &p_pos, Vector color; color.push_back(p_color); circle->polygon.create(indices, points, color); - - canvas_item->rect_dirty = true; - canvas_item->commands.push_back(circle); } void VisualServerCanvas::canvas_item_add_texture_rect(RID p_item, const Rect2 &p_rect, RID p_texture, bool p_tile, const Color &p_modulate, bool p_transpose, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat) { @@ -689,7 +675,7 @@ void VisualServerCanvas::canvas_item_add_texture_rect(RID p_item, const Rect2 &p Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandRect *rect = memnew(Item::CommandRect); + Item::CommandRect *rect = canvas_item->alloc_command(); ERR_FAIL_COND(!rect); rect->modulate = p_modulate; rect->rect = p_rect; @@ -716,8 +702,6 @@ void VisualServerCanvas::canvas_item_add_texture_rect(RID p_item, const Rect2 &p } rect->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); rect->specular_shininess = p_specular_color_shininess; - canvas_item->rect_dirty = true; - canvas_item->commands.push_back(rect); } void VisualServerCanvas::canvas_item_add_texture_rect_region(RID p_item, const Rect2 &p_rect, RID p_texture, const Rect2 &p_src_rect, const Color &p_modulate, bool p_transpose, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, bool p_clip_uv, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat) { @@ -725,7 +709,7 @@ void VisualServerCanvas::canvas_item_add_texture_rect_region(RID p_item, const R Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandRect *rect = memnew(Item::CommandRect); + Item::CommandRect *rect = canvas_item->alloc_command(); ERR_FAIL_COND(!rect); rect->modulate = p_modulate; rect->rect = p_rect; @@ -763,10 +747,6 @@ void VisualServerCanvas::canvas_item_add_texture_rect_region(RID p_item, const R if (p_clip_uv) { rect->flags |= RasterizerCanvas::CANVAS_RECT_CLIP_UV; } - - canvas_item->rect_dirty = true; - - canvas_item->commands.push_back(rect); } void VisualServerCanvas::canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, VS::NinePatchAxisMode p_x_axis_mode, VS::NinePatchAxisMode p_y_axis_mode, bool p_draw_center, const Color &p_modulate, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat) { @@ -774,7 +754,7 @@ void VisualServerCanvas::canvas_item_add_nine_patch(RID p_item, const Rect2 &p_r Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandNinePatch *style = memnew(Item::CommandNinePatch); + Item::CommandNinePatch *style = canvas_item->alloc_command(); ERR_FAIL_COND(!style); style->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); style->specular_shininess = p_specular_color_shininess; @@ -788,9 +768,6 @@ void VisualServerCanvas::canvas_item_add_nine_patch(RID p_item, const Rect2 &p_r style->margin[MARGIN_BOTTOM] = p_bottomright.y; style->axis_x = p_x_axis_mode; style->axis_y = p_y_axis_mode; - canvas_item->rect_dirty = true; - - canvas_item->commands.push_back(style); } void VisualServerCanvas::canvas_item_add_primitive(RID p_item, const Vector &p_points, const Vector &p_colors, const Vector &p_uvs, RID p_texture, float p_width, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat) { @@ -800,7 +777,7 @@ void VisualServerCanvas::canvas_item_add_primitive(RID p_item, const Vectoralloc_command(); ERR_FAIL_COND(!prim); for (int i = 0; i < p_points.size(); i++) { @@ -821,9 +798,6 @@ void VisualServerCanvas::canvas_item_add_primitive(RID p_item, const Vectortexture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); prim->specular_shininess = p_specular_color_shininess; - canvas_item->rect_dirty = true; - - canvas_item->commands.push_back(prim); } void VisualServerCanvas::canvas_item_add_polygon(RID p_item, const Vector &p_points, const Vector &p_colors, const Vector &p_uvs, RID p_texture, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat) { @@ -841,16 +815,12 @@ void VisualServerCanvas::canvas_item_add_polygon(RID p_item, const Vector indices = Geometry::triangulate_polygon(p_points); ERR_FAIL_COND_MSG(indices.empty(), "Invalid polygon data, triangulation failed."); - Item::CommandPolygon *polygon = memnew(Item::CommandPolygon); + Item::CommandPolygon *polygon = canvas_item->alloc_command(); ERR_FAIL_COND(!polygon); polygon->primitive = VS::PRIMITIVE_TRIANGLES; polygon->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); polygon->specular_shininess = p_specular_color_shininess; polygon->polygon.create(indices, p_points, p_colors, p_uvs); - - canvas_item->rect_dirty = true; - - canvas_item->commands.push_back(polygon); } void VisualServerCanvas::canvas_item_add_triangle_array(RID p_item, const Vector &p_indices, const Vector &p_points, const Vector &p_colors, const Vector &p_uvs, const Vector &p_bones, const Vector &p_weights, RID p_texture, int p_count, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat) { @@ -867,16 +837,13 @@ void VisualServerCanvas::canvas_item_add_triangle_array(RID p_item, const Vector const Vector &indices = p_indices; - Item::CommandPolygon *polygon = memnew(Item::CommandPolygon); + Item::CommandPolygon *polygon = canvas_item->alloc_command(); ERR_FAIL_COND(!polygon); polygon->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); polygon->specular_shininess = p_specular_color_shininess; polygon->polygon.create(indices, p_points, p_colors, p_uvs, p_bones, p_weights); polygon->primitive = VS::PRIMITIVE_TRIANGLES; - canvas_item->rect_dirty = true; - - canvas_item->commands.push_back(polygon); } void VisualServerCanvas::canvas_item_add_set_transform(RID p_item, const Transform2D &p_transform) { @@ -884,11 +851,9 @@ void VisualServerCanvas::canvas_item_add_set_transform(RID p_item, const Transfo Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandTransform *tr = memnew(Item::CommandTransform); + Item::CommandTransform *tr = canvas_item->alloc_command(); ERR_FAIL_COND(!tr); tr->xform = p_transform; - - canvas_item->commands.push_back(tr); } void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, const Transform2D &p_transform, const Color &p_modulate, RID p_texture, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat) { @@ -896,22 +861,20 @@ void VisualServerCanvas::canvas_item_add_mesh(RID p_item, const RID &p_mesh, con Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandMesh *m = memnew(Item::CommandMesh); + Item::CommandMesh *m = canvas_item->alloc_command(); ERR_FAIL_COND(!m); m->mesh = p_mesh; m->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); m->specular_shininess = p_specular_color_shininess; m->transform = p_transform; m->modulate = p_modulate; - - canvas_item->commands.push_back(m); } void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat) { Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandParticles *part = memnew(Item::CommandParticles); + Item::CommandParticles *part = canvas_item->alloc_command(); ERR_FAIL_COND(!part); part->particles = p_particles; part->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, RID()); @@ -919,9 +882,6 @@ void VisualServerCanvas::canvas_item_add_particles(RID p_item, RID p_particles, //take the chance and request processing for them, at least once until they become visible again VSG::storage->particles_request_process(p_particles); - - canvas_item->rect_dirty = true; - canvas_item->commands.push_back(part); } void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture, RID p_normal_map, RID p_specular_map, const Color &p_specular_color_shininess, VisualServer::CanvasItemTextureFilter p_filter, VisualServer::CanvasItemTextureRepeat p_repeat) { @@ -929,14 +889,11 @@ void VisualServerCanvas::canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandMultiMesh *mm = memnew(Item::CommandMultiMesh); + Item::CommandMultiMesh *mm = canvas_item->alloc_command(); ERR_FAIL_COND(!mm); mm->multimesh = p_mesh; mm->texture_binding.create(canvas_item->texture_filter, canvas_item->texture_repeat, p_texture, p_normal_map, p_specular_map, p_filter, p_repeat, mm->multimesh); mm->specular_shininess = p_specular_color_shininess; - - canvas_item->rect_dirty = true; - canvas_item->commands.push_back(mm); } void VisualServerCanvas::canvas_item_add_clip_ignore(RID p_item, bool p_ignore) { @@ -944,11 +901,9 @@ void VisualServerCanvas::canvas_item_add_clip_ignore(RID p_item, bool p_ignore) Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - Item::CommandClipIgnore *ci = memnew(Item::CommandClipIgnore); + Item::CommandClipIgnore *ci = canvas_item->alloc_command(); ERR_FAIL_COND(!ci); ci->ignore = p_ignore; - - canvas_item->commands.push_back(ci); } void VisualServerCanvas::canvas_item_set_sort_children_by_y(RID p_item, bool p_enable) {