Skip to content

Commit c113e5b

Browse files
committed
2D: Fix use-after-free in batch rendering
Closes godotengine#96960 Fixes regression of godotengine#95574 using fix from godotengine#95666
1 parent 74de05a commit c113e5b

File tree

2 files changed

+91
-92
lines changed

2 files changed

+91
-92
lines changed

servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp

+90-91
Original file line numberDiff line numberDiff line change
@@ -2146,23 +2146,24 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target
21462146
current_batch->material_data = material_data;
21472147
}
21482148

2149-
Transform2D base_transform = p_canvas_transform_inverse * ci->final_transform;
2150-
if (!ci->repeat_size.x && !ci->repeat_size.y) {
2151-
_record_item_commands(ci, p_to_render_target, base_transform, current_clip, p_lights, instance_index, batch_broken, r_sdf_used);
2149+
if (ci->repeat_source_item == nullptr || ci->repeat_size == Vector2()) {
2150+
Transform2D base_transform = p_canvas_transform_inverse * ci->final_transform;
2151+
_record_item_commands(ci, p_to_render_target, base_transform, current_clip, p_lights, instance_index, batch_broken, r_sdf_used, current_batch);
21522152
} else {
21532153
Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2);
2154-
Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos;
2155-
Point2 pos = start_pos;
2156-
do {
2157-
do {
2158-
Transform2D transform = base_transform * Transform2D(0, pos / ci->xform_curr.get_scale());
2159-
_record_item_commands(ci, p_to_render_target, transform, current_clip, p_lights, instance_index, batch_broken, r_sdf_used);
2160-
pos.y += ci->repeat_size.y;
2161-
} while (pos.y < end_pos.y);
2162-
2163-
pos.x += ci->repeat_size.x;
2164-
pos.y = start_pos.y;
2165-
} while (pos.x < end_pos.x);
2154+
Point2 offset;
2155+
int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0;
2156+
int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0;
2157+
for (int ry = 0; ry <= repeat_times_y; ry++) {
2158+
offset.y = start_pos.y + ry * ci->repeat_size.y;
2159+
for (int rx = 0; rx <= repeat_times_x; rx++) {
2160+
offset.x = start_pos.x + rx * ci->repeat_size.x;
2161+
Transform2D base_transform = ci->final_transform;
2162+
base_transform.columns[2] += ci->repeat_source_item->final_transform.basis_xform(offset);
2163+
base_transform = p_canvas_transform_inverse * base_transform;
2164+
_record_item_commands(ci, p_to_render_target, base_transform, current_clip, p_lights, instance_index, batch_broken, r_sdf_used, current_batch);
2165+
}
2166+
}
21662167
}
21672168
}
21682169

@@ -2262,9 +2263,7 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target
22622263
state.last_instance_index += instance_index;
22632264
}
22642265

2265-
void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTarget p_render_target, const Transform2D &p_base_transform, Item *&r_current_clip, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used) {
2266-
Batch *current_batch = &state.canvas_instance_batches[state.current_batch_index];
2267-
2266+
void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTarget p_render_target, const Transform2D &p_base_transform, Item *&r_current_clip, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, Batch *&r_current_batch) {
22682267
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? default_filter : p_item->texture_filter;
22692268
RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? default_repeat : p_item->texture_repeat;
22702269

@@ -2310,9 +2309,9 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
23102309

23112310
light_mode = (light_count > 0 || using_directional_lights) ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED;
23122311

2313-
if (light_mode != current_batch->light_mode) {
2314-
current_batch = _new_batch(r_batch_broken);
2315-
current_batch->light_mode = light_mode;
2312+
if (light_mode != r_current_batch->light_mode) {
2313+
r_current_batch = _new_batch(r_batch_broken);
2314+
r_current_batch->light_mode = light_mode;
23162315
}
23172316

23182317
// new_instance_data should be called after the current_batch is set.
@@ -2338,11 +2337,11 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
23382337
instance_data->world[i] = world[i];
23392338
}
23402339

2341-
instance_data->flags = base_flags | current_batch->tex_flags; // Reset on each command for safety, keep canvas texture binding config.
2340+
instance_data->flags = base_flags | r_current_batch->tex_flags; // Reset on each command for safety, keep canvas texture binding config.
23422341

2343-
instance_data->color_texture_pixel_size[0] = current_batch->tex_texpixel_size.width;
2344-
instance_data->color_texture_pixel_size[1] = current_batch->tex_texpixel_size.height;
2345-
instance_data->specular_shininess = current_batch->tex_specular_shininess;
2342+
instance_data->color_texture_pixel_size[0] = r_current_batch->tex_texpixel_size.width;
2343+
instance_data->color_texture_pixel_size[1] = r_current_batch->tex_texpixel_size.height;
2344+
instance_data->specular_shininess = r_current_batch->tex_specular_shininess;
23462345

23472346
return instance_data;
23482347
};
@@ -2359,12 +2358,12 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
23592358
const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c);
23602359

23612360
// 1: If commands are different, start a new batch.
2362-
if (current_batch->command_type != Item::Command::TYPE_RECT) {
2363-
current_batch = _new_batch(r_batch_broken);
2364-
current_batch->command_type = Item::Command::TYPE_RECT;
2365-
current_batch->command = c;
2361+
if (r_current_batch->command_type != Item::Command::TYPE_RECT) {
2362+
r_current_batch = _new_batch(r_batch_broken);
2363+
r_current_batch->command_type = Item::Command::TYPE_RECT;
2364+
r_current_batch->command = c;
23662365
// default variant
2367-
current_batch->pipeline_variant = PIPELINE_VARIANT_QUAD;
2366+
r_current_batch->pipeline_variant = PIPELINE_VARIANT_QUAD;
23682367
}
23692368

23702369
if (bool(rect->flags & CANVAS_RECT_TILE)) {
@@ -2374,10 +2373,10 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
23742373
bool has_msdf = bool(rect->flags & CANVAS_RECT_MSDF);
23752374
TextureState tex_state(rect->texture, texture_filter, texture_repeat, has_msdf, use_linear_colors);
23762375

2377-
if (tex_state != current_batch->tex_state) {
2378-
current_batch = _new_batch(r_batch_broken);
2379-
current_batch->set_tex_state(tex_state);
2380-
_prepare_batch_texture(current_batch, rect->texture);
2376+
if (tex_state != r_current_batch->tex_state) {
2377+
r_current_batch = _new_batch(r_batch_broken);
2378+
r_current_batch->set_tex_state(tex_state);
2379+
_prepare_batch_texture(r_current_batch, rect->texture);
23812380
}
23822381

23832382
Color modulated = rect->modulate * base_color;
@@ -2388,19 +2387,19 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
23882387
bool has_blend = bool(rect->flags & CANVAS_RECT_LCD);
23892388
// Start a new batch if the blend mode has changed,
23902389
// or blend mode is enabled and the modulation has changed.
2391-
if (has_blend != current_batch->has_blend || (has_blend && modulated != current_batch->modulate)) {
2392-
current_batch = _new_batch(r_batch_broken);
2393-
current_batch->has_blend = has_blend;
2394-
current_batch->modulate = modulated;
2395-
current_batch->pipeline_variant = has_blend ? PIPELINE_VARIANT_QUAD_LCD_BLEND : PIPELINE_VARIANT_QUAD;
2390+
if (has_blend != r_current_batch->has_blend || (has_blend && modulated != r_current_batch->modulate)) {
2391+
r_current_batch = _new_batch(r_batch_broken);
2392+
r_current_batch->has_blend = has_blend;
2393+
r_current_batch->modulate = modulated;
2394+
r_current_batch->pipeline_variant = has_blend ? PIPELINE_VARIANT_QUAD_LCD_BLEND : PIPELINE_VARIANT_QUAD;
23962395
}
23972396

23982397
InstanceData *instance_data = new_instance_data();
23992398
Rect2 src_rect;
24002399
Rect2 dst_rect;
24012400

24022401
if (rect->texture.is_valid()) {
2403-
src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * current_batch->tex_texpixel_size, rect->source.size * current_batch->tex_texpixel_size) : Rect2(0, 0, 1, 1);
2402+
src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * r_current_batch->tex_texpixel_size, rect->source.size * r_current_batch->tex_texpixel_size) : Rect2(0, 0, 1, 1);
24042403
dst_rect = Rect2(rect->rect.position, rect->rect.size);
24052404

24062405
if (dst_rect.size.width < 0) {
@@ -2470,24 +2469,24 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
24702469
instance_data->dst_rect[2] = dst_rect.size.width;
24712470
instance_data->dst_rect[3] = dst_rect.size.height;
24722471

2473-
_add_to_batch(r_index, r_batch_broken, current_batch);
2472+
_add_to_batch(r_index, r_batch_broken, r_current_batch);
24742473
} break;
24752474

24762475
case Item::Command::TYPE_NINEPATCH: {
24772476
const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c);
24782477

2479-
if (current_batch->command_type != Item::Command::TYPE_NINEPATCH) {
2480-
current_batch = _new_batch(r_batch_broken);
2481-
current_batch->command_type = Item::Command::TYPE_NINEPATCH;
2482-
current_batch->command = c;
2483-
current_batch->pipeline_variant = PipelineVariant::PIPELINE_VARIANT_NINEPATCH;
2478+
if (r_current_batch->command_type != Item::Command::TYPE_NINEPATCH) {
2479+
r_current_batch = _new_batch(r_batch_broken);
2480+
r_current_batch->command_type = Item::Command::TYPE_NINEPATCH;
2481+
r_current_batch->command = c;
2482+
r_current_batch->pipeline_variant = PipelineVariant::PIPELINE_VARIANT_NINEPATCH;
24842483
}
24852484

24862485
TextureState tex_state(np->texture, texture_filter, texture_repeat, false, use_linear_colors);
2487-
if (tex_state != current_batch->tex_state) {
2488-
current_batch = _new_batch(r_batch_broken);
2489-
current_batch->set_tex_state(tex_state);
2490-
_prepare_batch_texture(current_batch, np->texture);
2486+
if (tex_state != r_current_batch->tex_state) {
2487+
r_current_batch = _new_batch(r_batch_broken);
2488+
r_current_batch->set_tex_state(tex_state);
2489+
_prepare_batch_texture(r_current_batch, np->texture);
24912490
}
24922491

24932492
InstanceData *instance_data = new_instance_data();
@@ -2499,7 +2498,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
24992498
src_rect = Rect2(0, 0, 1, 1);
25002499
} else {
25012500
if (np->source != Rect2()) {
2502-
src_rect = Rect2(np->source.position.x * current_batch->tex_texpixel_size.width, np->source.position.y * current_batch->tex_texpixel_size.height, np->source.size.x * current_batch->tex_texpixel_size.width, np->source.size.y * current_batch->tex_texpixel_size.height);
2501+
src_rect = Rect2(np->source.position.x * r_current_batch->tex_texpixel_size.width, np->source.position.y * r_current_batch->tex_texpixel_size.height, np->source.size.x * r_current_batch->tex_texpixel_size.width, np->source.size.y * r_current_batch->tex_texpixel_size.height);
25032502
instance_data->color_texture_pixel_size[0] = 1.0 / np->source.size.width;
25042503
instance_data->color_texture_pixel_size[1] = 1.0 / np->source.size.height;
25052504
} else {
@@ -2539,30 +2538,30 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
25392538
instance_data->ninepatch_margins[2] = np->margin[SIDE_RIGHT];
25402539
instance_data->ninepatch_margins[3] = np->margin[SIDE_BOTTOM];
25412540

2542-
_add_to_batch(r_index, r_batch_broken, current_batch);
2541+
_add_to_batch(r_index, r_batch_broken, r_current_batch);
25432542
} break;
25442543

25452544
case Item::Command::TYPE_POLYGON: {
25462545
const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c);
25472546

25482547
// Polygon's can't be batched, so always create a new batch
2549-
current_batch = _new_batch(r_batch_broken);
2548+
r_current_batch = _new_batch(r_batch_broken);
25502549

2551-
current_batch->command_type = Item::Command::TYPE_POLYGON;
2552-
current_batch->command = c;
2550+
r_current_batch->command_type = Item::Command::TYPE_POLYGON;
2551+
r_current_batch->command = c;
25532552

25542553
TextureState tex_state(polygon->texture, texture_filter, texture_repeat, false, use_linear_colors);
2555-
if (tex_state != current_batch->tex_state) {
2556-
current_batch = _new_batch(r_batch_broken);
2557-
current_batch->set_tex_state(tex_state);
2558-
_prepare_batch_texture(current_batch, polygon->texture);
2554+
if (tex_state != r_current_batch->tex_state) {
2555+
r_current_batch = _new_batch(r_batch_broken);
2556+
r_current_batch->set_tex_state(tex_state);
2557+
_prepare_batch_texture(r_current_batch, polygon->texture);
25592558
}
25602559

25612560
// pipeline variant
25622561
{
25632562
static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP };
25642563
ERR_CONTINUE(polygon->primitive < 0 || polygon->primitive >= RS::PRIMITIVE_MAX);
2565-
current_batch->pipeline_variant = variant[polygon->primitive];
2564+
r_current_batch->pipeline_variant = variant[polygon->primitive];
25662565
}
25672566

25682567
InstanceData *instance_data = new_instance_data();
@@ -2577,27 +2576,27 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
25772576
instance_data->modulation[2] = color.b;
25782577
instance_data->modulation[3] = color.a;
25792578

2580-
_add_to_batch(r_index, r_batch_broken, current_batch);
2579+
_add_to_batch(r_index, r_batch_broken, r_current_batch);
25812580
} break;
25822581

25832582
case Item::Command::TYPE_PRIMITIVE: {
25842583
const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c);
25852584

2586-
if (primitive->point_count != current_batch->primitive_points || current_batch->command_type != Item::Command::TYPE_PRIMITIVE) {
2587-
current_batch = _new_batch(r_batch_broken);
2588-
current_batch->command_type = Item::Command::TYPE_PRIMITIVE;
2589-
current_batch->command = c;
2590-
current_batch->primitive_points = primitive->point_count;
2585+
if (primitive->point_count != r_current_batch->primitive_points || r_current_batch->command_type != Item::Command::TYPE_PRIMITIVE) {
2586+
r_current_batch = _new_batch(r_batch_broken);
2587+
r_current_batch->command_type = Item::Command::TYPE_PRIMITIVE;
2588+
r_current_batch->command = c;
2589+
r_current_batch->primitive_points = primitive->point_count;
25912590

25922591
static const PipelineVariant variant[4] = { PIPELINE_VARIANT_PRIMITIVE_POINTS, PIPELINE_VARIANT_PRIMITIVE_LINES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES };
25932592
ERR_CONTINUE(primitive->point_count == 0 || primitive->point_count > 4);
2594-
current_batch->pipeline_variant = variant[primitive->point_count - 1];
2593+
r_current_batch->pipeline_variant = variant[primitive->point_count - 1];
25952594

25962595
TextureState tex_state(primitive->texture, texture_filter, texture_repeat, false, use_linear_colors);
2597-
if (tex_state != current_batch->tex_state) {
2598-
current_batch = _new_batch(r_batch_broken);
2599-
current_batch->set_tex_state(tex_state);
2600-
_prepare_batch_texture(current_batch, primitive->texture);
2596+
if (tex_state != r_current_batch->tex_state) {
2597+
r_current_batch = _new_batch(r_batch_broken);
2598+
r_current_batch->set_tex_state(tex_state);
2599+
_prepare_batch_texture(r_current_batch, primitive->texture);
26012600
}
26022601
}
26032602

@@ -2616,7 +2615,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
26162615
instance_data->colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
26172616
}
26182617

2619-
_add_to_batch(r_index, r_batch_broken, current_batch);
2618+
_add_to_batch(r_index, r_batch_broken, r_current_batch);
26202619

26212620
if (primitive->point_count == 4) {
26222621
instance_data = new_instance_data();
@@ -2636,29 +2635,29 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
26362635
instance_data->colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b);
26372636
}
26382637

2639-
_add_to_batch(r_index, r_batch_broken, current_batch);
2638+
_add_to_batch(r_index, r_batch_broken, r_current_batch);
26402639
}
26412640
} break;
26422641

26432642
case Item::Command::TYPE_MESH:
26442643
case Item::Command::TYPE_MULTIMESH:
26452644
case Item::Command::TYPE_PARTICLES: {
26462645
// Mesh's can't be batched, so always create a new batch
2647-
current_batch = _new_batch(r_batch_broken);
2648-
current_batch->command = c;
2649-
current_batch->command_type = c->type;
2646+
r_current_batch = _new_batch(r_batch_broken);
2647+
r_current_batch->command = c;
2648+
r_current_batch->command_type = c->type;
26502649

26512650
InstanceData *instance_data = nullptr;
26522651

26532652
Color modulate(1, 1, 1, 1);
26542653
if (c->type == Item::Command::TYPE_MESH) {
26552654
const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
26562655
TextureState tex_state(m->texture, texture_filter, texture_repeat, false, use_linear_colors);
2657-
current_batch->set_tex_state(tex_state);
2658-
_prepare_batch_texture(current_batch, m->texture);
2656+
r_current_batch->set_tex_state(tex_state);
2657+
_prepare_batch_texture(r_current_batch, m->texture);
26592658
instance_data = new_instance_data();
26602659

2661-
current_batch->mesh_instance_count = 1;
2660+
r_current_batch->mesh_instance_count = 1;
26622661
_update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, instance_data->world);
26632662
modulate = m->modulate;
26642663
} else if (c->type == Item::Command::TYPE_MULTIMESH) {
@@ -2671,14 +2670,14 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
26712670
break;
26722671
}
26732672

2674-
current_batch->mesh_instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh);
2675-
if (current_batch->mesh_instance_count == 0) {
2673+
r_current_batch->mesh_instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh);
2674+
if (r_current_batch->mesh_instance_count == 0) {
26762675
break;
26772676
}
26782677

26792678
TextureState tex_state(mm->texture, texture_filter, texture_repeat, false, use_linear_colors);
2680-
current_batch->set_tex_state(tex_state);
2681-
_prepare_batch_texture(current_batch, mm->texture);
2679+
r_current_batch->set_tex_state(tex_state);
2680+
_prepare_batch_texture(r_current_batch, mm->texture);
26822681
instance_data = new_instance_data();
26832682

26842683
instance_data->flags |= 1; // multimesh, trails disabled
@@ -2695,15 +2694,15 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
26952694

26962695
const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c);
26972696
TextureState tex_state(pt->texture, texture_filter, texture_repeat, false, use_linear_colors);
2698-
current_batch->set_tex_state(tex_state);
2699-
_prepare_batch_texture(current_batch, pt->texture);
2697+
r_current_batch->set_tex_state(tex_state);
2698+
_prepare_batch_texture(r_current_batch, pt->texture);
27002699

27012700
instance_data = new_instance_data();
27022701

27032702
uint32_t divisor = 1;
2704-
current_batch->mesh_instance_count = particles_storage->particles_get_amount(pt->particles, divisor);
2703+
r_current_batch->mesh_instance_count = particles_storage->particles_get_amount(pt->particles, divisor);
27052704
instance_data->flags |= (divisor & FLAGS_INSTANCING_MASK);
2706-
current_batch->mesh_instance_count /= divisor;
2705+
r_current_batch->mesh_instance_count /= divisor;
27072706

27082707
RID particles = pt->particles;
27092708

@@ -2741,7 +2740,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
27412740
instance_data->modulation[2] = modulated.b;
27422741
instance_data->modulation[3] = modulated.a;
27432742

2744-
_add_to_batch(r_index, r_batch_broken, current_batch);
2743+
_add_to_batch(r_index, r_batch_broken, r_current_batch);
27452744
} break;
27462745

27472746
case Item::Command::TYPE_TRANSFORM: {
@@ -2754,12 +2753,12 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
27542753
const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c);
27552754
if (r_current_clip) {
27562755
if (ci->ignore != reclip) {
2757-
current_batch = _new_batch(r_batch_broken);
2756+
r_current_batch = _new_batch(r_batch_broken);
27582757
if (ci->ignore) {
2759-
current_batch->clip = nullptr;
2758+
r_current_batch->clip = nullptr;
27602759
reclip = true;
27612760
} else {
2762-
current_batch->clip = r_current_clip;
2761+
r_current_batch->clip = r_current_clip;
27632762
reclip = false;
27642763
}
27652764
}

0 commit comments

Comments
 (0)