nano-ui-sdl-0.1.0.0: cbits/nano_ui_batch.c
#include "nano_ui_batch.h"
#include "nano_ui_simd.h"
#include <stdlib.h>
struct NanoUiBatch {
SDL_Renderer *renderer;
const uint8_t *verts;
int vert_count;
const uint8_t *indices;
SDL_Texture *pending_texture;
int pending_start;
int pending_n;
};
NanoUiBatch *nano_ui_batch_create(SDL_Renderer *renderer)
{
if (!renderer) {
return NULL;
}
NanoUiBatch *batch = (NanoUiBatch *)calloc(1, sizeof(NanoUiBatch));
if (!batch) {
return NULL;
}
batch->renderer = renderer;
return batch;
}
void nano_ui_batch_destroy(NanoUiBatch *batch)
{
if (batch) {
nano_ui_batch_flush(batch);
free(batch);
}
}
void nano_ui_batch_flush(NanoUiBatch *batch)
{
if (!batch || !batch->renderer || batch->pending_n < 3) {
if (batch) {
batch->pending_n = 0;
batch->pending_start = 0;
batch->pending_texture = NULL;
}
return;
}
const SDL_Vertex *sdl_verts = (const SDL_Vertex *)batch->verts;
const int *idx = (const int *)batch->indices + batch->pending_start;
SDL_RenderGeometry(batch->renderer, batch->pending_texture, sdl_verts, batch->vert_count, idx, batch->pending_n);
batch->pending_n = 0;
batch->pending_start = 0;
batch->pending_texture = NULL;
}
void nano_ui_batch_draw_range(
NanoUiBatch *batch,
const uint8_t *verts,
int vert_count,
const uint8_t *indices,
int index_start,
int index_n,
SDL_Texture *texture,
int has_damage,
float dmg_x,
float dmg_y,
float dmg_w,
float dmg_h)
{
if (!batch || !verts || !indices || vert_count <= 0 || index_n < 3) {
return;
}
if (index_start < 0) {
index_start = 0;
}
if (has_damage && dmg_w > 0.f && dmg_h > 0.f && index_n >= 6) {
const SDL_Vertex *sdl_verts = (const SDL_Vertex *)verts;
const int *idx = (const int *)indices + index_start;
float dx0 = dmg_x;
float dy0 = dmg_y;
float dx1 = dmg_x + dmg_w;
float dy1 = dmg_y + dmg_h;
bool any_visible = false;
int q = 0;
#if defined(NANO_UI_HAS_AVX2)
// 8 quads per iteration: the 8-wide AABB test amortizes the
// gather + compares, which matters on partial-redraw frames where
// long runs of quads fall entirely outside the damage rect.
{
const __m256 vdx0 = _mm256_set1_ps(dx0);
const __m256 vdy0 = _mm256_set1_ps(dy0);
const __m256 vdx1 = _mm256_set1_ps(dx1);
const __m256 vdy1 = _mm256_set1_ps(dy1);
float qx0[8], qy0[8], qx1[8], qy1[8];
for (; q + 48 <= index_n; q += 48) {
bool all_valid = true;
for (int k = 0; k < 8; k++) {
int i0 = idx[q + k * 6];
int i2 = idx[q + k * 6 + 2];
if (i0 < 0 || i0 >= vert_count || i2 < 0 || i2 >= vert_count) {
all_valid = false;
break;
}
float x0 = sdl_verts[i0].position.x;
float y0 = sdl_verts[i0].position.y;
float x1 = sdl_verts[i2].position.x;
float y1 = sdl_verts[i2].position.y;
qx0[k] = x0 < x1 ? x0 : x1;
qx1[k] = x0 > x1 ? x0 : x1;
qy0[k] = y0 < y1 ? y0 : y1;
qy1[k] = y0 > y1 ? y0 : y1;
}
if (!all_valid) {
any_visible = true;
break;
}
uint32_t mask = nano_ui_cull_8_quads_avx2(
_mm256_loadu_ps(qx0), _mm256_loadu_ps(qy0),
_mm256_loadu_ps(qx1), _mm256_loadu_ps(qy1),
vdx0, vdy0, vdx1, vdy1);
if (mask != 0) {
any_visible = true;
break;
}
}
}
#endif
for (; q + 6 <= index_n; q += 6) {
int i0 = idx[q];
int i2 = idx[q + 2];
if (i0 >= 0 && i0 < vert_count && i2 >= 0 && i2 < vert_count) {
float x0 = sdl_verts[i0].position.x;
float y0 = sdl_verts[i0].position.y;
float x1 = sdl_verts[i2].position.x;
float y1 = sdl_verts[i2].position.y;
float qx0 = x0 < x1 ? x0 : x1;
float qx1 = x0 > x1 ? x0 : x1;
float qy0 = y0 < y1 ? y0 : y1;
float qy1 = y0 > y1 ? y0 : y1;
if (nano_ui_aabb_intersects(qx0, qy0, qx1, qy1, dx0, dy0, dx1, dy1)) {
any_visible = true;
break;
}
} else {
any_visible = true;
break;
}
}
if (!any_visible && q > 0) {
return;
}
}
if (batch->pending_n > 0 &&
batch->verts == verts &&
batch->indices == indices &&
batch->pending_texture == texture &&
batch->pending_start + batch->pending_n == index_start)
{
batch->pending_n += index_n;
if (vert_count > batch->vert_count) {
batch->vert_count = vert_count;
}
return;
}
nano_ui_batch_flush(batch);
batch->verts = verts;
batch->vert_count = vert_count;
batch->indices = indices;
batch->pending_texture = texture;
batch->pending_start = index_start;
batch->pending_n = index_n;
}