packages feed

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;
}