2D: Combine texture state to batch more subsequent commands together

This commit is contained in:
Stuart Carnie 2024-09-29 06:37:48 +10:00
parent 1fc8208765
commit 32f7ede6a9
No known key found for this signature in database
GPG Key ID: 848D9C9718D78B4F
10 changed files with 541 additions and 124 deletions

View File

@ -1211,8 +1211,7 @@ vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant Cle
varyings.layer = uint(attributes.a_position.w);
return varyings;
}
)",
ClearAttKey::DEPTH_INDEX];
)", ClearAttKey::DEPTH_INDEX];
return new_func(msl, @"vertClear", nil);
}

View File

@ -2056,6 +2056,10 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
case BT::Sampler: {
primary.dataType = MTLDataTypeSampler;
primary.arrayLength = 1;
for (uint32_t const &a : a_type.array) {
primary.arrayLength *= a;
}
} break;
default: {
@ -2063,7 +2067,7 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec
} break;
}
// Find array length.
// Find array length of image.
if (basetype == BT::Image || basetype == BT::SampledImage) {
primary.arrayLength = 1;
for (uint32_t const &a : a_type.array) {

View File

@ -359,7 +359,6 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit
RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, bool p_backbuffer) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
//re create canvas state
Vector<RD::Uniform> uniforms;
@ -438,8 +437,6 @@ RID RendererCanvasRenderRD::_create_base_uniform_set(RID p_to_render_target, boo
uniforms.push_back(u);
}
uniforms.append_array(material_storage->samplers_rd_get_default().get_uniforms(SAMPLERS_BINDING_FIRST_INDEX));
RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.default_version_rd_shader, BASE_UNIFORM_SET);
if (p_backbuffer) {
texture_storage->render_target_set_backbuffer_uniform_set(p_to_render_target, uniform_set);
@ -860,6 +857,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
state.current_data_buffer_index = (state.current_data_buffer_index + 1) % state.canvas_instance_data_buffers.size();
state.current_instance_buffer_index = 0;
state.current_texture_data_buffer_index = 0;
}
RID RendererCanvasRenderRD::light_create() {
@ -1627,6 +1625,15 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
default_samplers.default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
default_samplers.default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
Vector<RID> samplers = material_storage->samplers_rd_get_default().get_rids();
for (uint32_t i = 0; i < DEFAULT_MATERIAL_SAMPLER_COUNT; i++) {
default_material_samplers[i] = samplers[i];
}
}
{
// determine max number of texture slots we can use
batch_available_texture_slots = MIN(RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE) - TEXTURE_SLOTS_USED, BATCH_MAX_TEXTURES);
}
{ //shader variants
@ -1643,7 +1650,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
global_defines += "#define MAX_LIGHTS " + itos(DEFAULT_MAX_LIGHTS_PER_RENDER) + "\n";
}
global_defines += "\n#define SAMPLERS_BINDING_FIRST_INDEX " + itos(SAMPLERS_BINDING_FIRST_INDEX) + "\n";
global_defines += "#define BATCH_MAX_TEXTURES " + itos(batch_available_texture_slots) + "\n";
state.light_uniforms = memnew_arr(LightUniform, state.max_lights_per_render);
Vector<String> variants;
@ -1775,7 +1782,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
actions.renames["NORMAL_MAP"] = "normal_map";
actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth";
actions.renames["TEXTURE"] = "color_texture";
actions.renames["TEXTURE_PIXEL_SIZE"] = "draw_data.color_texture_pixel_size";
actions.renames["TEXTURE_PIXEL_SIZE"] = "_color_texture_pixel_size";
actions.renames["NORMAL_TEXTURE"] = "normal_texture";
actions.renames["SPECULAR_SHININESS_TEXTURE"] = "specular_texture";
actions.renames["SPECULAR_SHININESS"] = "specular_shininess";
@ -1820,6 +1827,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
actions.custom_samplers["TEXTURE"] = "texture_sampler";
actions.custom_samplers["NORMAL_TEXTURE"] = "texture_sampler";
actions.custom_samplers["SPECULAR_SHININESS_TEXTURE"] = "texture_sampler";
actions.sampler_array_name = "material_samplers";
actions.base_texture_binding_index = 1;
actions.texture_layout_set = MATERIAL_UNIFORM_SET;
actions.base_uniform_string = "material.";
@ -2048,9 +2056,11 @@ void fragment() {
for (int i = 0; i < 3; i++) {
DataBuffer db;
db.instance_buffers.push_back(RD::get_singleton()->storage_buffer_create(state.max_instance_buffer_size));
db.texture_data_buffers.push_back(BufferSize(RD::get_singleton()->storage_buffer_create(state.texture_data_array_size * sizeof(TextureData)), state.texture_data_array_size * sizeof(TextureData)));
state.canvas_instance_data_buffers[i] = db;
}
state.instance_data_array = memnew_arr(InstanceData, state.max_instances_per_buffer);
state.texture_data_array = memnew_arr(TextureData, state.texture_data_array_size);
}
}
@ -2219,7 +2229,9 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target
RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer);
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors);
_update_texture_data_buffer();
RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors, 1, 0, Rect2(), RDD::BreadcrumbMarker::UI_PASS);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, fb_uniform_set, BASE_UNIFORM_SET);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET);
@ -2264,14 +2276,37 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target
RD::get_singleton()->draw_list_end();
texture_info_map.clear();
state.next_texture_data_index = 0;
state.current_batch_index = 0;
state.canvas_instance_batches.clear();
state.last_instance_index += instance_index;
}
void RendererCanvasRenderRD::_update_texture_data_buffer() {
if (state.current_texture_data_buffer_index >= state.canvas_instance_data_buffers[state.current_data_buffer_index].texture_data_buffers.size()) {
_allocate_texture_data_buffer();
}
BufferSize &buf = state.canvas_instance_data_buffers[state.current_data_buffer_index].texture_data_buffers[state.current_texture_data_buffer_index];
if (buf.size < state.texture_data_array_size * sizeof(TextureData)) {
// the buffer was resized, we need to update the storage buffer size
RD::get_singleton()->free(buf);
buf.buffer = RD::get_singleton()->storage_buffer_create(state.texture_data_array_size * sizeof(TextureData));
buf.size = state.texture_data_array_size * sizeof(TextureData);
}
RD::get_singleton()->buffer_update(
buf,
0,
state.next_texture_data_index * sizeof(TextureData),
state.texture_data_array);
}
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) {
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? default_filter : p_item->texture_filter;
RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? default_repeat : p_item->texture_repeat;
const RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? default_filter : p_item->texture_filter;
const RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? default_repeat : p_item->texture_repeat;
Transform2D base_transform = p_base_transform;
@ -2321,7 +2356,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
}
// new_instance_data should be called after the current_batch is set.
auto new_instance_data = [&]() -> InstanceData * {
auto new_instance_data = [&](TextureInfo *p_info, BatchIndexes p_batch_indexes) -> InstanceData * {
InstanceData *instance_data = &state.instance_data_array[r_index];
// Zero out most fields.
for (int i = 0; i < 4; i++) {
@ -2331,8 +2366,8 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
instance_data->dst_rect[i] = 0.0;
}
instance_data->pad[0] = 0.0;
instance_data->pad[1] = 0.0;
instance_data->color_texture_pixel_size[0] = p_info->texpixel_size.width;
instance_data->color_texture_pixel_size[1] = p_info->texpixel_size.height;
instance_data->lights[0] = lights[0];
instance_data->lights[1] = lights[1];
@ -2343,11 +2378,11 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
instance_data->world[i] = world[i];
}
instance_data->flags = base_flags | r_current_batch->tex_info.flags; // Reset on each command for safety, keep canvas texture binding config.
instance_data->flags = base_flags | p_info->flags; // Reset on each command for safety, keep canvas texture binding config.
instance_data->color_texture_pixel_size[0] = r_current_batch->tex_info.texpixel_size.width;
instance_data->color_texture_pixel_size[1] = r_current_batch->tex_info.texpixel_size.height;
instance_data->specular_shininess = r_current_batch->tex_info.specular_shininess;
instance_data->texture_data_index = p_info->texture_data_index;
instance_data->pad = 0;
instance_data->batch_indexes = p_batch_indexes.batch_indexes;
return instance_data;
};
@ -2372,17 +2407,9 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->pipeline_variant = PIPELINE_VARIANT_QUAD;
}
RenderingServer::CanvasItemTextureRepeat rect_repeat = texture_repeat;
if (bool(rect->flags & CANVAS_RECT_TILE)) {
texture_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED;
}
bool has_msdf = bool(rect->flags & CANVAS_RECT_MSDF);
TextureState tex_state(rect->texture, texture_filter, texture_repeat, has_msdf, use_linear_colors);
if (tex_state != r_current_batch->tex_info.state) {
r_current_batch = _new_batch(r_batch_broken);
r_current_batch->tex_info.state = tex_state;
_prepare_batch_texture_info(r_current_batch, rect->texture);
rect_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED;
}
Color modulated = rect->modulate * base_color;
@ -2400,12 +2427,26 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->pipeline_variant = has_blend ? PIPELINE_VARIANT_QUAD_LCD_BLEND : PIPELINE_VARIANT_QUAD;
}
InstanceData *instance_data = new_instance_data();
bool has_msdf = bool(rect->flags & CANVAS_RECT_MSDF);
TextureState tex_state(rect->texture, texture_filter, rect_repeat, has_msdf, use_linear_colors);
TextureInfo *tex_info = texture_info_map.getptr(tex_state);
if (!tex_info) {
tex_info = &texture_info_map.insert(tex_state, TextureInfo())->value;
_prepare_batch_texture_info(rect->texture, tex_state, tex_info);
}
BatchIndexes indexes = _find_slots(r_current_batch, *tex_info);
if (!indexes.found_all()) {
r_current_batch = _new_batch(r_batch_broken);
indexes = _find_slots(r_current_batch, *tex_info);
}
InstanceData *instance_data = new_instance_data(tex_info, indexes);
Rect2 src_rect;
Rect2 dst_rect;
if (rect->texture.is_valid()) {
src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * r_current_batch->tex_info.texpixel_size, rect->source.size * r_current_batch->tex_info.texpixel_size) : Rect2(0, 0, 1, 1);
src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * tex_info->texpixel_size, rect->source.size * tex_info->texpixel_size) : Rect2(0, 0, 1, 1);
dst_rect = Rect2(rect->rect.position, rect->rect.size);
if (dst_rect.size.width < 0) {
@ -2490,13 +2531,19 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
}
TextureState tex_state(np->texture, texture_filter, texture_repeat, false, use_linear_colors);
if (tex_state != r_current_batch->tex_info.state) {
r_current_batch = _new_batch(r_batch_broken);
r_current_batch->tex_info.state = tex_state;
_prepare_batch_texture_info(r_current_batch, np->texture);
TextureInfo *tex_info = texture_info_map.getptr(tex_state);
if (!tex_info) {
tex_info = &texture_info_map.insert(tex_state, TextureInfo())->value;
_prepare_batch_texture_info(np->texture, tex_state, tex_info);
}
InstanceData *instance_data = new_instance_data();
BatchIndexes indexes = _find_slots(r_current_batch, *tex_info);
if (!indexes.found_all()) {
r_current_batch = _new_batch(r_batch_broken);
indexes = _find_slots(r_current_batch, *tex_info);
}
InstanceData *instance_data = new_instance_data(tex_info, indexes);
Rect2 src_rect;
Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y);
@ -2505,7 +2552,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
src_rect = Rect2(0, 0, 1, 1);
} else {
if (np->source != Rect2()) {
src_rect = Rect2(np->source.position.x * r_current_batch->tex_info.texpixel_size.width, np->source.position.y * r_current_batch->tex_info.texpixel_size.height, np->source.size.x * r_current_batch->tex_info.texpixel_size.width, np->source.size.y * r_current_batch->tex_info.texpixel_size.height);
src_rect = Rect2(np->source.position.x * tex_info->texpixel_size.width, np->source.position.y * tex_info->texpixel_size.height, np->source.size.x * tex_info->texpixel_size.width, np->source.size.y * tex_info->texpixel_size.height);
instance_data->color_texture_pixel_size[0] = 1.0 / np->source.size.width;
instance_data->color_texture_pixel_size[1] = 1.0 / np->source.size.height;
} else {
@ -2559,12 +2606,14 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->command = c;
TextureState tex_state(polygon->texture, texture_filter, texture_repeat, false, use_linear_colors);
if (tex_state != r_current_batch->tex_info.state) {
r_current_batch = _new_batch(r_batch_broken);
r_current_batch->tex_info.state = tex_state;
_prepare_batch_texture_info(r_current_batch, polygon->texture);
TextureInfo *tex_info = texture_info_map.getptr(tex_state);
if (!tex_info) {
tex_info = &texture_info_map.insert(tex_state, TextureInfo())->value;
_prepare_batch_texture_info(polygon->texture, tex_state, tex_info);
}
BatchIndexes indexes = _set_first_slot(r_current_batch, *tex_info);
// pipeline variant
{
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 };
@ -2572,7 +2621,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->pipeline_variant = variant[polygon->primitive];
}
InstanceData *instance_data = new_instance_data();
InstanceData *instance_data = new_instance_data(tex_info, indexes);
Color color = base_color;
if (use_linear_colors) {
@ -2600,16 +2649,22 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
static const PipelineVariant variant[4] = { PIPELINE_VARIANT_PRIMITIVE_POINTS, PIPELINE_VARIANT_PRIMITIVE_LINES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES, PIPELINE_VARIANT_PRIMITIVE_TRIANGLES };
ERR_CONTINUE(primitive->point_count == 0 || primitive->point_count > 4);
r_current_batch->pipeline_variant = variant[primitive->point_count - 1];
TextureState tex_state(primitive->texture, texture_filter, texture_repeat, false, use_linear_colors);
if (tex_state != r_current_batch->tex_info.state) {
r_current_batch = _new_batch(r_batch_broken);
r_current_batch->tex_info.state = tex_state;
_prepare_batch_texture_info(r_current_batch, primitive->texture);
}
}
InstanceData *instance_data = new_instance_data();
TextureState tex_state(primitive->texture, texture_filter, texture_repeat, false, use_linear_colors);
TextureInfo *tex_info = texture_info_map.getptr(tex_state);
if (!tex_info) {
tex_info = &texture_info_map.insert(tex_state, TextureInfo())->value;
_prepare_batch_texture_info(primitive->texture, tex_state, tex_info);
}
BatchIndexes indexes = _find_slots(r_current_batch, *tex_info);
if (!indexes.found_all()) {
r_current_batch = _new_batch(r_batch_broken);
indexes = _find_slots(r_current_batch, *tex_info);
}
InstanceData *instance_data = new_instance_data(tex_info, indexes);
for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) {
instance_data->points[j * 2 + 0] = primitive->points[j].x;
@ -2627,7 +2682,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
_add_to_batch(r_index, r_batch_broken, r_current_batch);
if (primitive->point_count == 4) {
instance_data = new_instance_data();
instance_data = new_instance_data(tex_info, indexes);
for (uint32_t j = 0; j < 3; j++) {
int offset = j == 0 ? 0 : 1;
@ -2636,7 +2691,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
instance_data->points[j * 2 + 1] = primitive->points[j + offset].y;
instance_data->uvs[j * 2 + 0] = primitive->uvs[j + offset].x;
instance_data->uvs[j * 2 + 1] = primitive->uvs[j + offset].y;
Color col = primitive->colors[j] * base_color;
Color col = primitive->colors[j + offset] * base_color;
if (use_linear_colors) {
col = col.srgb_to_linear();
}
@ -2663,9 +2718,13 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
if (c->type == Item::Command::TYPE_MESH) {
const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
TextureState tex_state(m->texture, texture_filter, texture_repeat, false, use_linear_colors);
r_current_batch->tex_info.state = tex_state;
_prepare_batch_texture_info(r_current_batch, m->texture);
instance_data = new_instance_data();
TextureInfo *tex_info = texture_info_map.getptr(tex_state);
if (!tex_info) {
tex_info = &texture_info_map.insert(tex_state, TextureInfo())->value;
_prepare_batch_texture_info(m->texture, tex_state, tex_info);
}
BatchIndexes indexes = _set_first_slot(r_current_batch, *tex_info);
instance_data = new_instance_data(tex_info, indexes);
r_current_batch->mesh_instance_count = 1;
_update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, instance_data->world);
@ -2686,9 +2745,14 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
}
TextureState tex_state(mm->texture, texture_filter, texture_repeat, false, use_linear_colors);
r_current_batch->tex_info.state = tex_state;
_prepare_batch_texture_info(r_current_batch, mm->texture);
instance_data = new_instance_data();
TextureInfo *tex_info = texture_info_map.getptr(tex_state);
if (!tex_info) {
tex_info = &texture_info_map.insert(tex_state, TextureInfo())->value;
_prepare_batch_texture_info(mm->texture, tex_state, tex_info);
}
BatchIndexes indexes = _set_first_slot(r_current_batch, *tex_info);
instance_data = new_instance_data(tex_info, indexes);
instance_data->flags |= 1; // multimesh, trails disabled
@ -2704,10 +2768,14 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c);
TextureState tex_state(pt->texture, texture_filter, texture_repeat, false, use_linear_colors);
r_current_batch->tex_info.state = tex_state;
_prepare_batch_texture_info(r_current_batch, pt->texture);
TextureInfo *tex_info = texture_info_map.getptr(tex_state);
if (!tex_info) {
tex_info = &texture_info_map.insert(tex_state, TextureInfo())->value;
_prepare_batch_texture_info(pt->texture, tex_state, tex_info);
}
BatchIndexes indexes = _set_first_slot(r_current_batch, *tex_info);
instance_data = new_instance_data();
instance_data = new_instance_data(tex_info, indexes);
uint32_t divisor = 1;
r_current_batch->mesh_instance_count = particles_storage->particles_get_amount(pt->particles, divisor);
@ -2795,21 +2863,171 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
}
}
RendererCanvasRenderRD::BatchIndexes RendererCanvasRenderRD::_find_slots(Batch *p_batch, TextureInfo &p_info) {
BatchIndexes indexes;
// first find existing slots
for (uint32_t i = 0; i < batch_available_texture_slots; i++) {
RID &rid = p_batch->batch_textures[i];
if (indexes.color == BATCH_INDEX_UNSET && rid == p_info.diffuse) {
indexes.color = i;
}
if (indexes.normal == BATCH_INDEX_UNSET && rid == p_info.normal) {
indexes.normal = i;
}
if (indexes.specular == BATCH_INDEX_UNSET && rid == p_info.specular) {
indexes.specular = i;
}
if (indexes.found_textures())
break;
}
// try to assign unset textures to unused slots
for (uint32_t i = BATCH_FIRST_OPEN_BATCH_TEXTURE_SLOT; !indexes.found_textures() && i < batch_available_texture_slots; i++) {
if ((p_batch->batch_textures_used & (uint32_t)(1 << i)) == 0) {
if (indexes.color == BATCH_INDEX_UNSET) {
indexes.color = i;
p_batch->batch_textures[i] = p_info.diffuse;
} else if (indexes.normal == BATCH_INDEX_UNSET) {
indexes.normal = i;
p_batch->batch_textures[i] = p_info.normal;
} else if (indexes.specular == BATCH_INDEX_UNSET) {
indexes.specular = i;
p_batch->batch_textures[i] = p_info.specular;
}
}
}
// find sampler in existing default material samplers slot
for (uint32_t i = 0; i < DEFAULT_MATERIAL_SAMPLER_COUNT; i++) {
if (default_material_samplers[i] == p_info.sampler) {
indexes.sampler = i;
break;
}
}
if (indexes.sampler == BATCH_INDEX_UNSET) {
// find it in dynamic slot
for (uint32_t i = 0; i < BATCH_MAX_DYNAMIC_SAMPLERS; i++) {
if (p_batch->batch_samplers[i] == p_info.sampler) {
indexes.sampler = DEFAULT_MATERIAL_SAMPLER_COUNT + i;
break;
}
}
}
// find sampler in unused slot
if (indexes.sampler == BATCH_INDEX_UNSET) {
for (uint32_t i = 0; i < BATCH_MAX_DYNAMIC_SAMPLERS; i++) {
if ((p_batch->batch_samplers_used & (uint32_t)(1 << i)) == 0) {
indexes.sampler = DEFAULT_MATERIAL_SAMPLER_COUNT + i;
p_batch->batch_samplers[i] = p_info.sampler;
break;
}
}
}
// if slots are found, increase usage count
if (indexes.found_all()) {
p_batch->batch_textures_used |= (uint32_t)(1 << indexes.color);
p_batch->batch_textures_used |= (uint32_t)(1 << indexes.normal);
p_batch->batch_textures_used |= (uint32_t)(1 << indexes.specular);
p_batch->batch_samplers_used |= (uint32_t)(1 << indexes.sampler);
}
return indexes;
}
RendererCanvasRenderRD::BatchIndexes RendererCanvasRenderRD::_set_first_slot(Batch *p_batch, TextureInfo &p_info) {
BatchIndexes indexes;
int next_texture_slot = BATCH_FIRST_OPEN_BATCH_TEXTURE_SLOT;
if (p_info.diffuse == p_batch->batch_textures[0]) {
indexes.color = 0;
} else {
indexes.color = next_texture_slot;
p_batch->batch_textures[next_texture_slot] = p_info.diffuse;
next_texture_slot++;
}
if (p_info.normal == p_batch->batch_textures[1]) {
indexes.normal = 1;
} else {
indexes.normal = next_texture_slot;
p_batch->batch_textures[next_texture_slot] = p_info.normal;
next_texture_slot++;
}
if (p_info.specular == p_batch->batch_textures[0]) {
indexes.specular = 0;
} else {
indexes.specular = next_texture_slot;
p_batch->batch_textures[next_texture_slot] = p_info.specular;
}
// is it a default sampler?
for (uint32_t i = 0; i < DEFAULT_MATERIAL_SAMPLER_COUNT; i++) {
if (default_material_samplers[i] == p_info.sampler) {
indexes.sampler = i;
break;
}
}
for (int i = 0; i < 3; i++) {
if (p_batch->batch_samplers[i] == p_info.sampler) {
indexes.sampler = DEFAULT_MATERIAL_SAMPLER_COUNT + (uint8_t)i;
break;
}
}
if (indexes.sampler == BATCH_INDEX_UNSET) {
indexes.sampler = DEFAULT_MATERIAL_SAMPLER_COUNT;
p_batch->batch_samplers[0] = p_info.sampler;
}
return indexes;
}
void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineVariants *p_pipeline_variants, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info) {
UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
ERR_FAIL_NULL(uniform_set_cache);
ERR_FAIL_NULL(p_batch->command);
PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
{
RD::Uniform u_diffuse(RD::UNIFORM_TYPE_TEXTURE, 0, p_batch->tex_info.diffuse);
RD::Uniform u_normal(RD::UNIFORM_TYPE_TEXTURE, 1, p_batch->tex_info.normal);
RD::Uniform u_specular(RD::UNIFORM_TYPE_TEXTURE, 2, p_batch->tex_info.specular);
RD::Uniform u_sampler(RD::UNIFORM_TYPE_SAMPLER, 3, p_batch->tex_info.sampler);
RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 4, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]);
RD::Uniform u_textures;
u_textures.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u_textures.binding = 0;
RID uniform_set = uniform_set_cache->get_cache(shader.default_version_rd_shader, BATCH_UNIFORM_SET, u_diffuse, u_normal, u_specular, u_sampler, u_instance_data);
for (uint32_t i = 0; i < batch_available_texture_slots; i++) {
if (p_batch->batch_textures[i].is_valid()) {
u_textures.append_id(p_batch->batch_textures[i]);
} else {
u_textures.append_id(default_texture_info.diffuse);
}
}
RD::Uniform u_samplers;
u_samplers.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
u_samplers.binding = 1;
for (uint32_t i = 0; i < DEFAULT_MATERIAL_SAMPLER_COUNT; i++) {
u_samplers.append_id(default_material_samplers[i]);
}
for (uint32_t i = 0; i < BATCH_MAX_DYNAMIC_SAMPLERS; i++) {
if (p_batch->batch_samplers[i].is_valid()) {
u_samplers.append_id(p_batch->batch_samplers[i]);
} else {
u_samplers.append_id(default_texture_info.sampler);
}
}
RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 2, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]);
RD::Uniform u_texture_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 3, state.canvas_instance_data_buffers[state.current_data_buffer_index].texture_data_buffers[state.current_texture_data_buffer_index]);
RID uniform_set = uniform_set_cache->get_cache(shader.default_version_rd_shader, BATCH_UNIFORM_SET, u_textures, u_samplers, u_instance_data, u_texture_data);
if (state.current_batch_uniform_set != uniform_set) {
state.current_batch_uniform_set = uniform_set;
RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, BATCH_UNIFORM_SET);
@ -2826,8 +3044,6 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
RD::get_singleton()->draw_list_set_blend_constants(p_draw_list, p_batch->modulate);
}
PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array);
RD::get_singleton()->draw_list_draw(p_draw_list, true, p_batch->instance_count);
@ -2848,8 +3064,6 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(pb->vertex_format_id, p_framebuffer_format);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, pb->vertex_array);
if (pb->indices.is_valid()) {
@ -2870,8 +3084,6 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][p_batch->pipeline_variant].get_render_pipeline(RD::INVALID_ID, p_framebuffer_format);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3u, primitive->point_count) - 1]);
uint32_t instance_count = p_batch->instance_count;
@ -2955,8 +3167,6 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
RID pipeline = p_pipeline_variants->variants[p_batch->light_mode][variant[primitive]].get_render_pipeline(vertex_format, p_framebuffer_format);
RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, pipeline);
PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
RID index_array = mesh_storage->mesh_surface_get_index_array(surface, 0);
@ -2986,7 +3196,12 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
RendererCanvasRenderRD::Batch *RendererCanvasRenderRD::_new_batch(bool &r_batch_broken) {
if (state.canvas_instance_batches.size() == 0) {
state.canvas_instance_batches.push_back(Batch());
return state.canvas_instance_batches.ptr();
Batch *new_batch = state.canvas_instance_batches.ptr();
// set the default textures and sampler
new_batch->batch_textures[0] = default_texture_info.diffuse;
new_batch->batch_textures[1] = default_texture_info.normal;
new_batch->batch_samplers[0] = default_texture_info.sampler;
return new_batch;
}
if (r_batch_broken || state.canvas_instance_batches[state.current_batch_index].instance_count == 0) {
@ -2997,9 +3212,14 @@ RendererCanvasRenderRD::Batch *RendererCanvasRenderRD::_new_batch(bool &r_batch_
// 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];
DEV_ASSERT(new_batch.batch_textures[0] == default_texture_info.diffuse);
DEV_ASSERT(new_batch.batch_textures[1] == default_texture_info.normal);
DEV_ASSERT(new_batch.batch_samplers[0] == default_texture_info.sampler);
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;
new_batch.instance_buffer_index = state.current_instance_buffer_index;
new_batch.reset_batch_usage();
state.current_batch_index++;
state.canvas_instance_batches.push_back(new_batch);
return &state.canvas_instance_batches[state.current_batch_index];
@ -3025,6 +3245,19 @@ void RendererCanvasRenderRD::_add_to_batch(uint32_t &r_index, bool &r_batch_brok
}
}
void RendererCanvasRenderRD::_allocate_texture_data_buffer() {
state.current_texture_data_buffer_index++;
if (state.current_texture_data_buffer_index < state.canvas_instance_data_buffers[state.current_data_buffer_index].texture_data_buffers.size()) {
// We already allocated another buffer in a previous frame, so we can just use it.
return;
}
// Allocate a new buffer.
RID buf = RD::get_singleton()->storage_buffer_create(state.texture_data_array_size * sizeof(TextureData));
state.canvas_instance_data_buffers[state.current_data_buffer_index].texture_data_buffers.push_back(BufferSize(buf, state.texture_data_array_size * sizeof(TextureData)));
}
void RendererCanvasRenderRD::_allocate_instance_buffer() {
state.current_instance_buffer_index++;
@ -3038,7 +3271,22 @@ void RendererCanvasRenderRD::_allocate_instance_buffer() {
state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.push_back(buf);
}
void RendererCanvasRenderRD::_prepare_batch_texture_info(Batch *p_current_batch, RID p_texture) const {
RendererCanvasRenderRD::TextureData *RendererCanvasRenderRD::_next_texture_data() {
if (state.next_texture_data_index >= state.texture_data_array_size) {
// If we've reached the limit of the current buffer, the size is doubled
// and the data is copied over to the new buffer.
state.texture_data_array_size *= 2;
TextureData *tmp_array = memnew_arr(TextureData, state.texture_data_array_size);
SWAP(tmp_array, state.texture_data_array);
memcpy(state.texture_data_array, tmp_array, state.next_texture_data_index * sizeof(TextureData));
memdelete_arr(tmp_array);
}
TextureData *ptr = &state.texture_data_array[state.next_texture_data_index];
state.next_texture_data_index++;
return ptr;
}
void RendererCanvasRenderRD::_prepare_batch_texture_info(RID p_texture, TextureState &p_state, TextureInfo *p_info) {
if (p_texture.is_null()) {
p_texture = default_canvas_texture;
}
@ -3046,38 +3294,44 @@ void RendererCanvasRenderRD::_prepare_batch_texture_info(Batch *p_current_batch,
RendererRD::TextureStorage::CanvasTextureInfo info =
RendererRD::TextureStorage::get_singleton()->canvas_texture_get_info(
p_texture,
p_current_batch->tex_info.state.texture_filter(),
p_current_batch->tex_info.state.texture_repeat(),
p_current_batch->tex_info.state.linear_colors(),
p_current_batch->tex_info.state.texture_is_data());
p_state.texture_filter(),
p_state.texture_repeat(),
p_state.linear_colors(),
p_state.texture_is_data());
// something odd happened
if (info.is_null()) {
_prepare_batch_texture_info(p_current_batch, default_canvas_texture);
_prepare_batch_texture_info(default_canvas_texture, p_state, p_info);
return;
}
p_current_batch->tex_info.diffuse = info.diffuse;
p_current_batch->tex_info.normal = info.normal;
p_current_batch->tex_info.specular = info.specular;
p_current_batch->tex_info.sampler = info.sampler;
// assign to the next slot in the texture data array
p_info->texture_data_index = state.next_texture_data_index;
TextureData *texture_data = _next_texture_data();
p_info->diffuse = info.diffuse;
p_info->normal = info.normal;
p_info->specular = info.specular;
p_info->sampler = info.sampler;
// cache values to be copied to instance data
if (info.specular_color.a < 0.999) {
p_current_batch->tex_info.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
p_info->flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
if (info.use_normal) {
p_current_batch->tex_info.flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
p_info->flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
}
uint8_t a = uint8_t(CLAMP(info.specular_color.a * 255.0, 0.0, 255.0));
uint8_t b = uint8_t(CLAMP(info.specular_color.b * 255.0, 0.0, 255.0));
uint8_t g = uint8_t(CLAMP(info.specular_color.g * 255.0, 0.0, 255.0));
uint8_t r = uint8_t(CLAMP(info.specular_color.r * 255.0, 0.0, 255.0));
p_current_batch->tex_info.specular_shininess = uint32_t(a) << 24 | uint32_t(b) << 16 | uint32_t(g) << 8 | uint32_t(r);
texture_data->specular_shininess = uint32_t(a) << 24 | uint32_t(b) << 16 | uint32_t(g) << 8 | uint32_t(r);
p_current_batch->tex_info.texpixel_size = Vector2(1.0 / float(info.size.width), 1.0 / float(info.size.height));
p_info->texpixel_size = Vector2(1.0 / float(info.size.width), 1.0 / float(info.size.height));
texture_data->color_texture_pixel_size[0] = p_info->texpixel_size.width;
texture_data->color_texture_pixel_size[1] = p_info->texpixel_size.height;
texture_data->pad = 0;
}
RendererCanvasRenderRD::~RendererCanvasRenderRD() {
@ -3128,8 +3382,13 @@ RendererCanvasRenderRD::~RendererCanvasRenderRD() {
for (uint32_t j = 0; j < state.canvas_instance_data_buffers[i].instance_buffers.size(); j++) {
RD::get_singleton()->free(state.canvas_instance_data_buffers[i].instance_buffers[j]);
}
for (uint32_t j = 0; j < state.canvas_instance_data_buffers[i].texture_data_buffers.size(); j++) {
RD::get_singleton()->free(state.canvas_instance_data_buffers[i].texture_data_buffers[j]);
}
}
memdelete_arr(state.texture_data_array);
RendererRD::TextureStorage::get_singleton()->canvas_texture_free(default_canvas_texture);
//pipelines don't need freeing, they are all gone after shaders are gone
}

View File

@ -48,7 +48,17 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
BATCH_UNIFORM_SET = 3,
};
const int SAMPLERS_BINDING_FIRST_INDEX = 10;
/// This is the number of texture uniforms used in the shader, such as atlas_texture and shadow_atlas_texture
static const uint32_t TEXTURE_SLOTS_USED = 4;
/// Most we can support is 64, based on the Batch::batch_textures_used bit mask, which allows for 64 slots
static const uint32_t BATCH_MAX_TEXTURES = 64;
static const uint32_t BATCH_MAX_SAMPLERS = 15;
/// The first open slot in batch_textures, as 0 and 1 are reserved.
static const uint32_t BATCH_FIRST_OPEN_BATCH_TEXTURE_SLOT = 2;
// The number of default material samplers, which include SAMPLER_NEAREST_CLAMP, SAMPLER_LINEAR_CLAMP, etc
static const uint32_t DEFAULT_MATERIAL_SAMPLER_COUNT = 12;
// The maximum number of dynamic samplers that can be used in a single batch
static const uint32_t BATCH_MAX_DYNAMIC_SAMPLERS = 3;
enum ShaderVariant {
SHADER_VARIANT_QUAD,
@ -94,6 +104,19 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
FLAGS_FLIP_V = (1 << 31),
};
enum {
BATCH_INDEX_COLOR_TEXTURE_MASK = 0x1F,
BATCH_INDEX_COLOR_TEXTURE_SHIFT = 0,
BATCH_INDEX_NORMAL_TEXTURE_MASK = 0x1F,
BATCH_INDEX_NORMAL_TEXTURE_SHIFT = 5,
BATCH_INDEX_SPECULAR_TEXTURE_MASK = 0x1F,
BATCH_INDEX_SPECULAR_TEXTURE_SHIFT = 10,
BATCH_INDEX_SAMPLER_MASK = 0x7,
BATCH_INDEX_SAMPLER_SHIFT = 15,
BATCH_INDEX_UNSET = 0x80,
};
enum {
LIGHT_FLAGS_TEXTURE_MASK = 0xFFFF,
LIGHT_FLAGS_BLEND_SHIFT = 16,
@ -338,7 +361,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
struct InstanceData {
float world[6];
uint32_t flags;
uint32_t specular_shininess;
uint32_t batch_indexes;
union {
//rect
struct {
@ -349,7 +372,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
};
float dst_rect[4];
float src_rect[4];
float pad[2];
float color_texture_pixel_size[2];
};
//primitive
struct {
@ -358,15 +381,20 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t colors[6]; // colors encoded as half
};
};
float color_texture_pixel_size[2];
uint32_t texture_data_index;
uint32_t pad;
uint32_t lights[4];
};
struct TextureData {
float color_texture_pixel_size[2];
uint32_t specular_shininess;
uint32_t pad;
};
struct PushConstant {
uint32_t base_instance_index;
uint32_t pad1;
uint32_t pad2;
uint32_t pad3;
uint32_t pad[3];
};
// TextureState is used to determine when a new batch is required due to a change of texture state.
@ -413,25 +441,59 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
return (other >> TEXTURE_IS_DATA_SHIFT) & TEXTURE_IS_DATA_MASK;
}
bool operator==(const TextureState &p_val) const {
_FORCE_INLINE_ bool operator==(const TextureState &p_val) const {
return (texture == p_val.texture) && (other == p_val.other);
}
bool operator!=(const TextureState &p_val) const {
_FORCE_INLINE_ bool operator!=(const TextureState &p_val) const {
return (texture != p_val.texture) || (other != p_val.other);
}
_FORCE_INLINE_ bool is_valid() const { return texture.is_valid(); }
_FORCE_INLINE_ bool is_null() const { return texture.is_null(); }
uint32_t hash() const {
uint32_t hash = hash_murmur3_one_64(texture.get_id());
return hash_murmur3_one_32(other, hash);
}
};
struct TextureInfo {
TextureState state;
uint32_t specular_shininess = 0;
uint32_t flags = 0;
Vector2 texpixel_size;
RID diffuse;
RID normal;
RID specular;
RID sampler;
uint32_t texture_data_index = 0;
};
HashMap<TextureState, TextureInfo, HashableHasher<TextureState>> texture_info_map;
union BatchIndexes {
struct {
uint8_t color;
uint8_t normal;
uint8_t specular;
uint8_t sampler;
};
/// Little-endian encoding of the indexes.
/// - bits 0-7 : color
/// - bits 8-15 : normal
/// - bits 16-23: specular
/// - bits 24-31: sampler
uint32_t batch_indexes;
BatchIndexes() :
batch_indexes(0x80808080) {}
_FORCE_INLINE_ bool found_textures() const {
return (batch_indexes & 0x00808080) == 0;
}
_FORCE_INLINE_ bool found_all() const {
return (batch_indexes & 0x80808080) == 0;
}
};
struct Batch {
@ -440,7 +502,16 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t instance_count = 0;
uint32_t instance_buffer_index = 0;
TextureInfo tex_info;
/// Contains all the textures used in the batch.
///
/// index 0 is the default_canvas_texture.diffuse
/// index 1 is the default_canvas_texture.normal
RID batch_textures[BATCH_MAX_TEXTURES];
/// A bitmask indicating which slots are used, allowing up to 64 textures
uint64_t batch_textures_used = 0;
RID batch_samplers[3];
/// A bitmask indicating which slots are used, allowing up to 64 samplers
uint64_t batch_samplers_used = 0;
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
@ -462,10 +533,26 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t mesh_instance_count;
};
bool has_blend = false;
_FORCE_INLINE_ void reset_batch_usage() {
batch_textures_used = 0;
batch_samplers_used = 0;
}
};
struct BufferSize {
RID buffer;
uint32_t size = 0;
BufferSize() {}
BufferSize(RID p_buffer, uint32_t p_size) :
buffer(p_buffer),
size(p_size) {}
operator RID() const { return buffer; }
};
struct DataBuffer {
LocalVector<RID> instance_buffers;
LocalVector<BufferSize> texture_data_buffers;
};
struct State {
@ -501,6 +588,14 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t max_instances_per_buffer = 16384;
uint32_t max_instance_buffer_size = 16384 * sizeof(InstanceData);
uint32_t next_texture_data_index = 0;
uint32_t current_texture_data_buffer_index = 0;
TextureData *texture_data_array = nullptr;
// Start with 128 unique textures in a single frame
// however, this can grow as needed, and should settle on a size
// that prevents further allocations
uint32_t texture_data_array_size = 128;
RID current_batch_uniform_set;
LightUniform *light_uniforms = nullptr;
@ -524,7 +619,10 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
Item *items[MAX_RENDER_ITEMS];
// this is assigned dynamically based on driver support
uint32_t batch_available_texture_slots = 0;
TextureInfo default_texture_info;
RID default_material_samplers[DEFAULT_MATERIAL_SAMPLER_COUNT];
bool using_directional_lights = false;
RID default_canvas_texture;
@ -554,11 +652,16 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
void _render_batch_items(RenderTarget p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _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);
BatchIndexes _find_slots(Batch *p_batch, TextureInfo &p_info);
BatchIndexes _set_first_slot(Batch *p_batch, TextureInfo &p_info);
void _render_batch(RD::DrawListID p_draw_list, PipelineVariants *p_pipeline_variants, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _prepare_batch_texture_info(Batch *p_current_batch, RID p_texture) const;
void _prepare_batch_texture_info(RID p_texture, TextureState &p_state, TextureInfo *p_info);
[[nodiscard]] Batch *_new_batch(bool &r_batch_broken);
[[nodiscard]] TextureData *_next_texture_data();
void _update_texture_data_buffer();
void _add_to_batch(uint32_t &r_index, bool &r_batch_broken, Batch *&r_current_batch);
void _allocate_instance_buffer();
void _allocate_texture_data_buffer();
_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
_FORCE_INLINE_ void _update_transform_2d_to_mat2x3(const Transform2D &p_transform, float *p_mat2x3);

View File

@ -28,7 +28,7 @@ layout(location = 11) in vec4 weight_attrib;
layout(location = 4) out flat uint instance_index_interp;
#endif // USE_ATTRIBUTES
#endif // !USE_ATTRIBUTES
layout(location = 0) out vec2 uv_interp;
layout(location = 1) out vec4 color_interp;
@ -324,11 +324,7 @@ vec4 light_compute(
#ifdef USE_NINEPATCH
float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) {
#ifdef USE_ATTRIBUTES
const InstanceData draw_data = instances.data[params.base_instance_index];
#else
const InstanceData draw_data = instances.data[instance_index];
#endif // USE_ATTRIBUTES
float tex_size = 1.0 / tex_pixel_size;
@ -476,6 +472,11 @@ void main() {
const InstanceData draw_data = instances.data[instance_index];
#endif // USE_ATTRIBUTES
const uint BATCH_COLOR_INDEX = bitfieldExtract(draw_data.batch_indexes, 0, 8);
const uint BATCH_NORMAL_INDEX = bitfieldExtract(draw_data.batch_indexes, 8, 8);
const uint BATCH_SPECULAR_INDEX = bitfieldExtract(draw_data.batch_indexes, 16, 8);
const uint BATCH_SAMPLER_INDEX = bitfieldExtract(draw_data.batch_indexes, 24, 8);
#if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
#ifdef USE_NINEPATCH
@ -572,7 +573,7 @@ void main() {
if (specular_shininess_used || (using_light && normal_used && bool(draw_data.flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
specular_shininess = texture(sampler2D(specular_texture, texture_sampler), uv);
specular_shininess *= unpackUnorm4x8(draw_data.specular_shininess);
specular_shininess *= unpackUnorm4x8(texture_data.data[draw_data.texture_data_index].specular_shininess);
specular_shininess_used = true;
} else {
specular_shininess = vec4(1.0);

View File

@ -37,7 +37,7 @@ struct InstanceData {
vec2 world_y;
vec2 world_ofs;
uint flags;
uint specular_shininess;
uint batch_indexes;
#ifdef USE_PRIMITIVE
vec2 points[3];
vec2 uvs[3];
@ -47,18 +47,17 @@ struct InstanceData {
vec4 ninepatch_margins;
vec4 dst_rect; //for built-in rect and UV
vec4 src_rect;
vec2 pad;
vec2 color_texture_pixel_size;
#endif
vec2 color_texture_pixel_size;
uint texture_data_index;
uint pad;
uint lights[4];
};
layout(push_constant, std430) uniform Params {
uint base_instance_index; // base index to instance data
uint pad1;
uint pad2;
uint pad3;
uint pad[3];
}
params;
@ -134,8 +133,6 @@ layout(set = 0, binding = 5) uniform sampler shadow_sampler;
layout(set = 0, binding = 6) uniform texture2D color_buffer;
layout(set = 0, binding = 7) uniform texture2D sdf_texture;
#include "samplers_inc.glsl"
layout(set = 0, binding = 9, std430) restrict readonly buffer GlobalShaderUniformData {
vec4 data[];
}
@ -154,12 +151,41 @@ transforms;
/* SET3: Texture */
layout(set = 3, binding = 0) uniform texture2D color_texture;
layout(set = 3, binding = 1) uniform texture2D normal_texture;
layout(set = 3, binding = 2) uniform texture2D specular_texture;
layout(set = 3, binding = 3) uniform sampler texture_sampler;
layout(set = 3, binding = 0) uniform texture2D texture_array[BATCH_MAX_TEXTURES];
layout(set = 3, binding = 4, std430) restrict readonly buffer DrawData {
#define SAMPLER_LINEAR_CLAMP material_samplers[1]
/// This shader uses a max of 16 samplers
///
/// - 1 shadow_sampler
/// - 12 base samplers, SAMPLER_NEAREST_CLAMP, SAMPLER_LINEAR_CLAMP, etc
/// - 3 dynamic samplers
layout(set = 3, binding = 1) uniform sampler material_samplers[15];
#define color_texture texture_array[BATCH_COLOR_INDEX]
#define normal_texture texture_array[BATCH_NORMAL_INDEX]
#define specular_texture texture_array[BATCH_SPECULAR_INDEX]
#define texture_sampler material_samplers[BATCH_SAMPLER_INDEX]
layout(set = 3, binding = 2, std430) restrict readonly buffer DrawData {
InstanceData data[];
}
instances;
#ifdef USE_NINEPATCH
// ninepatch has per-instance color_texture_pixel_size
#define _color_texture_pixel_size draw_data.color_texture_pixel_size
#else
#define _color_texture_pixel_size texture_data.data[draw_data.texture_data_index].color_texture_pixel_size
#endif
struct TextureData {
vec2 color_texture_pixel_size;
uint specular_shininess;
uint pad;
};
layout(set = 3, binding = 3, std430) restrict readonly buffer TextureDrawData {
TextureData data[];
}
texture_data;

View File

@ -1110,6 +1110,25 @@ Vector<RD::Uniform> MaterialStorage::Samplers::get_uniforms(int p_first_index) c
return uniforms;
}
Vector<RID> MaterialStorage::Samplers::get_rids() const {
Vector<RID> res;
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST][RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR][RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS][RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS][RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC][RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC][RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST][RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR][RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS][RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS][RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC][RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED]);
res.push_back(rids[RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC][RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED]);
return res;
}
bool MaterialStorage::Samplers::is_valid() const {
return rids[1][1].is_valid();
}

View File

@ -116,6 +116,7 @@ public:
}
Vector<RD::Uniform> get_uniforms(int p_first_index) const;
Vector<RID> get_rids() const;
bool is_valid() const;
bool is_null() const;
};

View File

@ -299,7 +299,10 @@ String ShaderCompiler::_get_sampler_name(ShaderLanguage::TextureFilter p_filter,
"SAMPLER_NEAREST_WITH_MIPMAPS_ANISOTROPIC_REPEAT",
"SAMPLER_LINEAR_WITH_MIPMAPS_ANISOTROPIC_REPEAT"
};
return String(name_mapping[p_filter + (p_repeat == ShaderLanguage::REPEAT_ENABLE ? ShaderLanguage::FILTER_DEFAULT : 0)]);
if (actions.sampler_array_name.is_empty()) {
return String(name_mapping[p_filter + (p_repeat == ShaderLanguage::REPEAT_ENABLE ? ShaderLanguage::FILTER_DEFAULT : 0)]);
}
return actions.sampler_array_name + "[" + itos(p_filter + (p_repeat == ShaderLanguage::REPEAT_ENABLE ? ShaderLanguage::FILTER_DEFAULT : 0)) + "]";
}
void ShaderCompiler::_dump_function_deps(const SL::ShaderNode *p_node, const StringName &p_for_func, const HashMap<StringName, String> &p_func_code, String &r_to_add, HashSet<StringName> &added) {

View File

@ -93,6 +93,8 @@ public:
HashMap<StringName, String> custom_samplers;
ShaderLanguage::TextureFilter default_filter = ShaderLanguage::TextureFilter::FILTER_NEAREST;
ShaderLanguage::TextureRepeat default_repeat = ShaderLanguage::TextureRepeat::REPEAT_DISABLE;
/// If this value is not an empty string, it will be used as the name of the array of samplers.
String sampler_array_name;
int base_texture_binding_index = 0;
int texture_layout_set = 0;
String base_uniform_string;