1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-16 14:00:40 +00:00

2D: Switch to VBOs for instance data

- Add support for vertex bindings and UMA vertex buffers in D3D12.
- Simplify 2D instance params and move more into per-batch data to save
  bandwidth

Co-authored-by: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com>
Co-authored-by: Clay John <claynjohn@gmail.com>
Co-authored-by: A Thousand Ships <96648715+athousandships@users.noreply.github.com>
This commit is contained in:
Stuart Carnie
2025-11-07 05:50:02 +11:00
parent bad1287b62
commit 90c0e6acca
25 changed files with 893 additions and 256 deletions

View File

@@ -67,8 +67,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
INSTANCE_FLAGS_CLIP_RECT_UV = (1 << 4),
INSTANCE_FLAGS_TRANSPOSE_RECT = (1 << 5),
INSTANCE_FLAGS_USE_MSDF = (1 << 6),
INSTANCE_FLAGS_USE_LCD = (1 << 7),
INSTANCE_FLAGS_NINEPACH_DRAW_CENTER = (1 << 8),
INSTANCE_FLAGS_NINEPATCH_H_MODE_SHIFT = 9,
@@ -120,6 +118,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
struct {
uint32_t use_lighting : 1;
uint32_t use_msdf : 1;
uint32_t use_lcd : 1;
};
};
};
@@ -186,6 +186,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
RID default_version_rd_shader;
RID quad_index_buffer;
RID quad_index_array;
RD::VertexFormatID quad_vertex_format_id;
RD::VertexFormatID primitive_vertex_format_id;
ShaderCompiler compiler;
uint32_t pipeline_compilations[RS::PIPELINE_SOURCE_MAX] = {};
Mutex mutex;
@@ -352,16 +354,12 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
struct InstanceData {
float world[6];
uint32_t flags;
uint32_t instance_uniforms_ofs;
float ninepatch_pixel_size[2];
union {
//rect
struct {
float modulation[4];
union {
float msdf[4];
float ninepatch_margins[4];
};
float ninepatch_margins[4];
float dst_rect[4];
float src_rect[4];
float pad[2];
@@ -373,15 +371,35 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t colors[6]; // colors encoded as half
};
};
float color_texture_pixel_size[2];
uint32_t flags;
uint32_t instance_uniforms_ofs;
uint32_t lights[4];
};
static_assert(sizeof(InstanceData) == 128, "2D instance data struct size must be 128 bytes");
struct PushConstant {
uint32_t base_instance_index;
ShaderSpecialization shader_specialization;
uint32_t specular_shininess;
uint32_t batch_flags;
uint32_t pad0;
float msdf[2];
float color_texture_pixel_size[2];
};
struct PushConstantAttributes {
PushConstant base;
float world[6];
uint32_t flags;
uint32_t instance_uniforms_ofs;
float modulation[4];
uint32_t lights[4];
operator PushConstant &() {
return base;
}
};
// TextureState is used to determine when a new batch is required due to a change of texture state.
@@ -459,18 +477,16 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
/// A key used to uniquely identify a distinct BATCH_UNIFORM_SET
struct RIDSetKey {
TextureState state;
RID instance_data;
RIDSetKey() {
}
RIDSetKey(TextureState p_state, RID p_instance_data) :
state(p_state),
instance_data(p_instance_data) {
RIDSetKey(TextureState p_state) :
state(p_state) {
}
_ALWAYS_INLINE_ bool operator==(const RIDSetKey &p_val) const {
return state == p_val.state && instance_data == p_val.instance_data;
return state == p_val.state;
}
_ALWAYS_INLINE_ bool operator!=(const RIDSetKey &p_val) const {
@@ -478,9 +494,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
}
_ALWAYS_INLINE_ uint32_t hash() const {
uint32_t h = state.hash();
h = hash_murmur3_one_64(instance_data.get_id(), h);
return hash_fmix32(h);
return state.hash();
}
};
@@ -495,6 +509,9 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
/// diffuse texture.
HashMap<RID, TightLocalVector<RID>> canvas_texture_to_uniform_set;
static constexpr uint32_t PUSH_DATA_INSTANCE_COUNT = 0x8000'0000; // Use high bit to indicate instance data comes from push_data.
static constexpr uint32_t INSTANCE_COUNT_MASK = 0x7fff'ffff;
struct Batch {
/// First instance index into the instance buffer for this batch.
uint32_t start = 0;
@@ -502,10 +519,14 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t instance_count = 0;
/// Resource ID of the instance buffer for this batch.
RID instance_buffer; // UMA
/// Push-constant payload for non-VAO draws.
InstanceData push_data = {};
TextureInfo *tex_info;
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
float msdf_pix_range = 0.0;
float msdf_outline = 0.0;
Item *clip = nullptr;
@@ -517,6 +538,9 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
ShaderVariant shader_variant = SHADER_VARIANT_QUAD;
RD::RenderPrimitive render_primitive = RD::RENDER_PRIMITIVE_TRIANGLES;
bool use_lighting = false;
bool use_msdf = false;
bool use_lcd = false;
bool has_blend = false;
// batch-specific data
union {
@@ -525,8 +549,31 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
// TYPE_PARTICLES
uint32_t mesh_instance_count;
};
bool has_blend = false;
uint32_t flags = 0;
_FORCE_INLINE_ PushConstant push_constant() const {
PushConstant pc;
pc.specular_shininess = tex_info->specular_shininess;
pc.batch_flags = tex_info->flags | flags;
pc.pad0 = 0;
pc.msdf[0] = msdf_pix_range;
pc.msdf[1] = msdf_outline;
pc.color_texture_pixel_size[0] = tex_info->texpixel_size.x;
pc.color_texture_pixel_size[1] = tex_info->texpixel_size.y;
return pc;
}
_FORCE_INLINE_ PushConstantAttributes push_constant_attributes() const {
PushConstantAttributes pc;
pc.base = push_constant();
memcpy(pc.world, push_data.world, sizeof(pc.world));
memcpy(pc.modulation, push_data.modulation, sizeof(pc.modulation));
memcpy(pc.lights, push_data.lights, sizeof(pc.lights));
pc.flags = push_data.flags;
pc.instance_uniforms_ofs = push_data.instance_uniforms_ofs;
return pc;
}
};
HashMap<TextureState, TextureInfo, HashMapHasherDefault, HashMapComparatorDefault<TextureState>, PagedAllocator<HashMapElement<TextureState, TextureInfo>>> texture_info_map;
@@ -564,6 +611,9 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
InstanceData *instance_data = nullptr;
/// The index of the next instance to be added to <c>instance_data</c>.
uint32_t instance_data_index = 0;
/// Save the previous instance data to allow us to append .
InstanceData *prev_instance_data = nullptr;
uint32_t prev_instance_data_index = 0;
uint32_t max_instances_per_buffer = 16384;
uint32_t max_instance_buffer_size = 16384 * sizeof(InstanceData);
@@ -626,7 +676,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
void _prepare_batch_texture_info(RID p_texture, TextureState &p_state, TextureInfo *p_info);
// non-UMA
InstanceData *new_instance_data(Batch &p_current_batch, const InstanceData &template_instance);
InstanceData *new_instance_data(Batch &p_current_batch, const InstanceData &template_instance, bool p_use_push_data = false);
[[nodiscard]] Batch *_new_batch(bool &r_batch_broken);
void _add_to_batch(bool &r_batch_broken, Batch *&r_current_batch);
void _allocate_instance_buffer();