/*
This file is part of reicast.
reicast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
reicast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with reicast. If not, see .
*/
#include
#include "sorter.h"
#include "sort_pairs.h"
/* (sorting: in memory kept from one frame to the next) */
static grow_buf_t sortPairs, sortPairsTmp;
struct IndexTrig
{
u32 id[3];
u16 pid;
f32 z;
};
/* (as std::min(): the first when neither is less) */
static INLINE float min2(float a, float b)
{
return b < a ? b : a;
}
static float minZ(const Vertex *v, const u32 *mod)
{
return min2(min2(v[mod[0]].z, v[mod[1]].z), v[mod[2]].z);
}
void SortPParams(int first, int count)
{
if (pvrrc.verts.used() == 0 || count <= 1)
return;
Vertex* vtx_base=pvrrc.verts.head();
u32* idx_base = pvrrc.idx.head();
PolyParam* pp = &pvrrc.global_param_tr.head()[first];
PolyParam* pp_end = pp + count;
while(pp!=pp_end)
{
if (pp->count<2)
{
pp->zvZ=0;
}
else
{
u32* idx = idx_base + pp->first;
Vertex* vtx=vtx_base+idx[0];
Vertex* vtx_end=vtx_base + idx[pp->count-1]+1;
u32 zv=0xFFFFFFFF;
while(vtx!=vtx_end)
{
if ((u32&)vtx->z < zv)
zv = (u32&)vtx->z;
vtx++;
}
pp->zvZ=(f32&)zv;
}
pp++;
}
/* sorted by zvZ, stably: the polygons gathered in that order, and put back */
PolyParam * const base = pvrrc.global_param_tr.head() + first;
sort_pair_t *pairs = (sort_pair_t *)grow_buf_reserve(&sortPairs, (size_t)count * sizeof(sort_pair_t));
static grow_buf_t gathered;
PolyParam *to = (PolyParam *)grow_buf_reserve(&gathered, (size_t)count * sizeof(PolyParam));
if (pairs == NULL || to == NULL)
return;
for (int i = 0; i < count; i++)
{
pairs[i].key = sort_key(base[i].zvZ);
pairs[i].index = (u32)i;
}
pairs = sort_pairs(pairs, (u32)count, &sortPairsTmp);
if (pairs == NULL)
return;
for (int i = 0; i < count; i++)
to[i] = base[pairs[i].index];
memcpy(base, to, (size_t)count * sizeof(PolyParam));
}
//are two poly params the same?
static bool PP_EQ(const PolyParam *pp0, const PolyParam *pp1)
{
return SortSameDraw(pp0, pp1);
}
static void fill_id(u32 *d, const Vertex *v0, const Vertex *v1, const Vertex *v2, const Vertex *vb)
{
d[0] = (u32)(v0 - vb);
d[1] = (u32)(v1 - vb);
d[2] = (u32)(v2 - vb);
}
u32 SortedTriangles(int first, int count)
{
const PolyParam *pp = pvrrc.global_param_tr.head() + first;
const PolyParam * const pp_end = pp + count;
u32 tris = 0;
for (; pp < pp_end; pp++)
if (pp->count > 2)
tris += pp->count - 2;
return tris;
}
SortList *SortPasses(grow_buf_t *lists, u32 passes)
{
const size_t had = lists->size / sizeof(SortList);
if (passes > had)
{
if (grow_buf_reserve_keep(lists, (size_t)passes * sizeof(SortList)) == NULL)
return NULL;
/* (the new ones empty: zeroed) */
memset((SortList *)lists->data + had, 0, lists->size - had * sizeof(SortList));
}
return (SortList *)lists->data;
}
void SortPassesFree(grow_buf_t *lists)
{
SortList *list = (SortList *)lists->data;
for (size_t i = 0; i < lists->size / sizeof(SortList); i++)
grow_buf_free(&list[i].buf);
grow_buf_free(lists);
}
u32 GenSortedTo(int first, int count, SortList *draws, u32 *out, bool firstProvoking)
{
draws->count = 0;
if (pvrrc.verts.used() == 0 || count == 0)
return 0;
const Vertex * const vtx_base = pvrrc.verts.head();
const u32 * const idx_base = pvrrc.idx.head();
const PolyParam * const pp_base = &pvrrc.global_param_tr.head()[first];
const PolyParam *pp = pp_base;
const PolyParam * const pp_end = pp + count;
while (pp->count == 0 && pp < pp_end)
pp++;
if (pp == pp_end)
return 0;
const int vtx_count = pvrrc.verts.used() - idx_base[pp->first];
if (vtx_count<=0)
return 0;
/* every triangle, with its polygon and its vertices; and a pair of its
* depth and its place, which is what is sorted. (Kept from one frame to
* the next: nothing allocated once they have grown.) */
static grow_buf_t lstBuf;
const u32 tris = SortedTriangles(first, count);
IndexTrig *lst = (IndexTrig *)grow_buf_reserve(&lstBuf, (size_t)(tris ? tris : 1) * sizeof(IndexTrig));
sort_pair_t *pairs = (sort_pair_t *)grow_buf_reserve(&sortPairs, (size_t)(tris ? tris : 1) * sizeof(sort_pair_t));
if (lst == NULL || pairs == NULL)
return 0;
u32 pfsti=0;
while(pp!=pp_end)
{
u32 ppid = (u32)(pp - pp_base);
if (pp->count>2)
{
const u32 *idx = idx_base + pp->first;
u32 flip = 0;
for (u32 i = 0; i < pp->count - 2; i++)
{
const Vertex *v0, *v1, *v2;
if (flip && firstProvoking)
{
// The same winding, starting with the vertex that holds
// this triangle's colour (strip vertex i, not i + 1).
v0 = vtx_base + idx[i];
v1 = vtx_base + idx[i + 2];
v2 = vtx_base + idx[i + 1];
}
else if (flip)
{
v0 = vtx_base + idx[i + 1];
v1 = vtx_base + idx[i];
v2 = vtx_base + idx[i + 2];
}
else
{
v0 = vtx_base + idx[i];
v1 = vtx_base + idx[i + 1];
v2 = vtx_base + idx[i + 2];
}
fill_id(lst[pfsti].id,v0,v1,v2,vtx_base);
lst[pfsti].pid= ppid ;
lst[pfsti].z = minZ(vtx_base,lst[pfsti].id);
pairs[pfsti].key = sort_key(lst[pfsti].z);
pairs[pfsti].index = pfsti;
pfsti++;
flip ^= 1;
}
}
pp++;
}
const u32 aused=pfsti;
//sort them: by depth, those at the same depth in the order sent
pairs = sort_pairs(pairs, aused, &sortPairsTmp);
if (pairs == NULL)
return 0;
//Merge pids/draw cmds if two different pids are actually equal
for (u32 k=1;kbuf, (size_t)(aused ? aused : 1) * sizeof(SortTrigDrawParam));
if (list == NULL)
return 0;
u32 n = 0;
int idx=-1;
for (u32 i=0; icount = n;
return aused * 3;
}
/* Whether a polygon that blends with one minus its alpha can have a pixel
* at alpha 1, as far as its vertices say: not if its alpha is theirs (or
* theirs times the texture's) and no vertex has 255 */
static bool PrepassVerticesOpaque(const PolyParam *pp, const Vertex *vtx_base, const u32 *idx_base)
{
if (!PrepassAlphaFromVertices(pp))
return true;
const u32 *idx = idx_base + pp->first;
for (u32 i = 0; i < pp->count; i++)
if (vtx_base[idx[i]].col[3] == 255)
return true;
return false;
}
/* The polygons that can hide, in their order (order), in runs of
* neighbours drawn alike (runs): a model's polygons come one after the
* other, so it is the runs that are sorted, few of them. */
struct PrepassRun
{
PrepassKey key;
u32 start, count; // in order
u32 tris; // the most triangles they make
};
/* @n runs sorted by key, stably (so those of a key keep the order they
* were made in, by start): merged from runs of one up, through @tmp; where
* they are, after */
static PrepassRun *sort_runs(PrepassRun *runs, PrepassRun *tmp, u32 n)
{
PrepassRun *from = runs, *to = tmp;
for (u32 width = 1; width < n; width *= 2)
{
for (u32 lo = 0; lo < n; lo += 2 * width)
{
const u32 mid = lo + width < n ? lo + width : n;
const u32 hi = lo + 2 * width < n ? lo + 2 * width : n;
u32 a = lo, b = mid, k = lo;
while (a < mid && b < hi)
to[k++] = from[b].key < from[a].key ? from[b++] : from[a++];
while (a < mid)
to[k++] = from[a++];
while (b < hi)
to[k++] = from[b++];
}
PrepassRun *t = from;
from = to;
to = t;
}
return from;
}
u32 GenPrepassTo(int first, int count, bool lastPass, SortList *draws, u32 *out, u32 base,
bool firstProvoking)
{
/* (kept from one frame to the next: no allocation once they have grown) */
static grow_buf_t orderBuf, runsBuf, runsTmp;
if (count <= 0 || pvrrc.verts.used() == 0 || !PrepassWanted(lastPass))
return 0;
const Vertex * const vtx_base = pvrrc.verts.head();
const u32 * const idx_base = pvrrc.idx.head();
const PolyParam * const pp_base = pvrrc.global_param_tr.head();
/* at most a run and an entry of order a polygon */
u32 *order = (u32 *)grow_buf_reserve(&orderBuf, (size_t)count * sizeof(u32));
PrepassRun *runs = (PrepassRun *)grow_buf_reserve(&runsBuf, (size_t)count * sizeof(PrepassRun));
PrepassRun *tmp = (PrepassRun *)grow_buf_reserve(&runsTmp, (size_t)count * sizeof(PrepassRun));
if (order == NULL || runs == NULL || tmp == NULL)
return 0;
u32 norder = 0, nruns = 0;
for (int n = first; n < first + count; n++)
{
const PolyParam *pp = pp_base + n;
if (!PrepassCandidate(pp, lastPass) || !PrepassVerticesOpaque(pp, vtx_base, idx_base))
continue;
const PrepassKey key = PrepassMakeKey(pp, pp->texid);
if (nruns != 0 && runs[nruns - 1].start + runs[nruns - 1].count == norder && runs[nruns - 1].key == key)
{
runs[nruns - 1].count++;
runs[nruns - 1].tris += pp->count - 2;
}
else
{
runs[nruns].key = key;
runs[nruns].start = norder;
runs[nruns].count = 1;
runs[nruns].tris = pp->count - 2;
nruns++;
}
order[norder++] = (u32)n;
}
if (nruns == 0)
return 0;
runs = sort_runs(runs, tmp, nruns);
/* the draws: at most one a run more than there are */
SortTrigDrawParam *list = (SortTrigDrawParam *)grow_buf_reserve_keep(&draws->buf,
((size_t)draws->count + nruns) * sizeof(SortTrigDrawParam));
if (list == NULL)
return 0;
u32 nd = draws->count;
const u32 had = nd;
// the triangles, as GenSortedTo() makes them of a strip, written in place
u32 *dst = out;
for (u32 k = 0; k < nruns; k++)
{
const PrepassRun& r = runs[k];
if (k == 0 || !(r.key == runs[k - 1].key))
{
// (a group before it all of whose triangles drew nothing is not kept)
if (k != 0 && list[nd - 1].count == 0)
nd--;
list[nd].ppid = pp_base + order[r.start];
list[nd].first = (u32)(base + (dst - out));
list[nd].count = 0;
nd++;
}
u32 * const runStart = dst;
for (u32 j = r.start; j < r.start + r.count; j++)
{
const PolyParam *pp = pp_base + order[j];
const u32 *idx = idx_base + pp->first;
u32 flip = 0;
for (u32 i = 0; i + 2 < pp->count; i++, flip ^= 1)
{
const u32 a = idx[i], b = idx[i + 1], c = idx[i + 2];
// (a triangle with two corners the same draws nothing)
if (a == b || b == c || a == c)
continue;
dst[0] = a;
if (flip && firstProvoking)
{
dst[1] = c;
dst[2] = b;
}
else if (flip)
{
dst[0] = b;
dst[1] = a;
dst[2] = c;
}
else
{
dst[1] = b;
dst[2] = c;
}
dst += 3;
}
}
list[nd - 1].count += (u32)(dst - runStart);
}
if (nd > had && list[nd - 1].count == 0)
nd--;
draws->count = nd;
return (u32)(dst - out);
}
u32 SortPassesTo(grow_buf_t *sortedLists, grow_buf_t *prepassLists, u32 *out, bool prepasses, bool firstProvoking)
{
const u32 passes = (u32)pvrrc.render_passes.used();
SortList * const sorted = SortPasses(sortedLists, passes);
SortList * const prepass = SortPasses(prepassLists, passes);
if (sorted == NULL || prepass == NULL)
{
/* (no memory: nothing drawn sorted, rather than the last frame's) */
for (u32 i = 0; sorted != NULL && i < passes; i++)
sorted[i].count = 0;
for (u32 i = 0; prepass != NULL && i < passes; i++)
prepass[i].count = 0;
return 0;
}
u32 total = 0;
int previous = 0;
for (u32 pass = 0; pass < passes; pass++)
{
const RenderPass& current = pvrrc.render_passes.head()[pass];
sorted[pass].count = 0;
prepass[pass].count = 0;
if (current.autosort)
{
const int count = current.tr_count - previous;
u32 n = GenSortedTo(previous, count, &sorted[pass], out, firstProvoking);
if (prepasses)
n += GenPrepassTo(previous, count, pass == passes - 1, &prepass[pass], out + n, n, firstProvoking);
SortTrigDrawParam *draw = SortDraws(&sorted[pass]);
for (u32 i = 0; i < sorted[pass].count; i++)
draw[i].first += total;
draw = SortDraws(&prepass[pass]);
for (u32 i = 0; i < prepass[pass].count; i++)
draw[i].first += total;
total += n;
out += n;
}
previous = current.tr_count;
}
return total;
}
u32 SortPassesRoom(void)
{
u32 tris = 0;
int previous = 0;
for (int pass = 0; pass < pvrrc.render_passes.used(); pass++)
{
const RenderPass& current = pvrrc.render_passes.head()[pass];
if (current.autosort)
tris += SortedTriangles(previous, current.tr_count - previous);
previous = current.tr_count;
}
return tris * 6;
}