Commit 92b27bcc by Juan Linietsky

Changed allocation strategy of CanvasItem draw commands.

They should now allocate memory in blocks and reuse the same memory every time the item is cleared and redrawn. This should improve performance considerably.
parent 24b16f3b
......@@ -742,6 +742,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 {
......@@ -757,6 +769,7 @@ public:
TYPE_CLIP_IGNORE,
};
Command *next;
Type type;
virtual ~Command() {}
};
......@@ -873,7 +886,7 @@ public:
//VS::MaterialBlendMode blend_mode;
int light_mask;
int z_final;
Vector<Command *> commands;
mutable bool custom_rect;
mutable bool rect_dirty;
mutable Rect2 rect;
......@@ -905,8 +918,8 @@ public:
return rect;
//must update rect
int s = commands.size();
if (s == 0) {
if (commands == NULL) {
rect = Rect2();
rect_dirty = false;
......@@ -917,11 +930,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) {
......@@ -983,12 +995,12 @@ public:
const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c);
xf = transform->xform;
found_xform = true;
continue;
} break;
case Item::Command::TYPE_CLIP_IGNORE: {
} break;
} //passthrough
default: {
c = c->next;
continue;
}
}
if (found_xform) {
......@@ -999,18 +1011,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<CommandBlock> blocks;
uint32_t current_block;
template <class T>
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;
......@@ -1018,6 +1102,9 @@ public:
light_masked = false;
}
Item() {
commands = NULL;
last_command = NULL;
current_block = 0;
light_mask = 1;
vp_render = NULL;
next = NULL;
......@@ -1037,6 +1124,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);
}
};
......
......@@ -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 *&current_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) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment