per-triangle sorting of translucent and punchthrough lists

This commit is contained in:
Anthony Pesch 2017-10-21 12:48:10 -04:00
parent be3cdea37c
commit 3243219bcf
4 changed files with 172 additions and 92 deletions

View File

@ -17,7 +17,6 @@ struct tr {
tr_find_texture_cb find_texture;
/* current global state */
const union poly_param *last_poly;
const union vert_param *last_vertex;
int list_type;
int vert_type;
@ -183,7 +182,7 @@ static struct ta_surface *tr_reserve_surf(struct tr *tr, struct tr_context *rc,
memset(surf, 0, sizeof(*surf));
}
surf->first_vert = rc->num_indices;
surf->first_vert = rc->num_verts;
surf->num_verts = 0;
return surf;
@ -191,81 +190,65 @@ static struct ta_surface *tr_reserve_surf(struct tr *tr, struct tr_context *rc,
static struct ta_vertex *tr_reserve_vert(struct tr *tr, struct tr_context *rc) {
struct ta_surface *curr_surf = &rc->surfs[rc->num_surfs];
int curr_surf_vert = curr_surf->num_verts / 3;
int vert_index = rc->num_verts + curr_surf_vert;
int vert_index = rc->num_verts + curr_surf->num_verts;
CHECK_LT(vert_index, ARRAY_SIZE(rc->verts));
struct ta_vertex *vert = &rc->verts[vert_index];
int index = rc->num_indices + curr_surf->num_verts;
CHECK_LT(index + 2, ARRAY_SIZE(rc->indices));
uint16_t *indices = &rc->indices[index];
memset(vert, 0, sizeof(*vert));
/* polygons are fed to the TA as triangle strips, with the vertices being fed
in a CW order, so a given quad looks like:
1----3----5
|\ |\ |
| \ | \ |
| \ | \ |
| \| \|
0----2----4
convert from these triangle strips to triangles to make merging surfaces
easy in tr_commit_surf, and convert to CCW to match OpenGL defaults */
if (curr_surf_vert & 1) {
indices[0] = vert_index;
indices[1] = vert_index + 1;
indices[2] = vert_index + 2;
} else {
indices[0] = vert_index;
indices[1] = vert_index + 2;
indices[2] = vert_index + 1;
}
curr_surf->num_verts += 3;
curr_surf->num_verts++;
return vert;
}
static inline int tr_can_merge_surfs(struct ta_surface *a,
struct ta_surface *b) {
return a->params.full == b->params.full;
}
static void tr_commit_surf(struct tr *tr, struct tr_context *rc) {
struct tr_list *list = &rc->lists[tr->list_type];
struct ta_surface *new_surf = &rc->surfs[rc->num_surfs];
/* tr_reserve_vert preemptively adds indices for the next two vertices when
converting the incoming triangle strips to triangles. this results in the
first 2 vertices adding 6 extra indices that don't exist */
new_surf->num_verts -= 6;
/* track original number of surfaces, before sorting, merging, etc. */
list->num_orig_surfs++;
/* check to see if this surface can be merged with the previous surface */
struct ta_surface *prev_surf = NULL;
/* for translucent lists, commit a surf for each tri to make sorting easier */
if (tr->list_type == TA_LIST_TRANSLUCENT ||
tr->list_type == TA_LIST_PUNCH_THROUGH) {
/* ignore the last two verts as polygons are fed to the TA as tristrips */
int num_verts = new_surf->num_verts;
if (rc->num_surfs) {
prev_surf = &rc->surfs[rc->num_surfs - 1];
for (int i = 0; i < num_verts - 2; i++) {
struct ta_surface *surf = NULL;
if (i == 0) {
surf = new_surf;
} else {
surf = tr_reserve_surf(tr, rc, 1);
}
/* track triangle strip offset so winding order can be consistent when
generating indices */
surf->strip_offset = i;
surf->first_vert = rc->num_verts;
surf->num_verts = 3;
/* default sort the new surface */
list->surfs[list->num_surfs++] = rc->num_surfs;
/* commit the new surface */
rc->num_verts += 1;
rc->num_surfs++;
}
/* commit the last two verts */
rc->num_verts += 2;
}
if (prev_surf && tr_can_merge_surfs(prev_surf, new_surf)) {
/* merge the new verts into the prev surface */
prev_surf->num_verts += new_surf->num_verts;
} else {
/* for opaque lists, commit surface as is */
else {
/* default sort the new surface */
struct tr_list *list = &rc->lists[tr->list_type];
list->surfs[list->num_surfs] = rc->num_surfs;
list->num_surfs++;
list->surfs[list->num_surfs++] = rc->num_surfs;
/* commit the new surface */
rc->num_verts += new_surf->num_verts;
rc->num_surfs += 1;
}
/* commit the new verts and indices */
rc->num_verts += (new_surf->num_verts + 6) / 3;
rc->num_indices += new_surf->num_verts;
}
/*
@ -426,7 +409,6 @@ static void tr_parse_poly_param(struct tr *tr, const struct ta_context *ctx,
const union poly_param *param = (const union poly_param *)data;
/* reset state */
tr->last_poly = param;
tr->last_vertex = NULL;
tr->vert_type = ta_vert_type(param->type0.pcw);
@ -652,7 +634,83 @@ static void tr_parse_vert_param(struct tr *tr, const struct ta_context *ctx,
}
}
/* scratch buffers used by surface merge sort */
static void tr_parse_eol(struct tr *tr, const struct ta_context *ctx,
struct tr_context *rc, const uint8_t *data) {
tr->last_vertex = NULL;
tr->list_type = TA_NUM_LISTS;
tr->vert_type = TA_NUM_VERTS;
}
static inline int tr_can_merge_surfs(struct ta_surface *a,
struct ta_surface *b) {
return a->params.full == b->params.full;
}
static void tr_generate_indices(struct tr *tr, struct tr_context *rc,
int list_type) {
/* polygons are fed to the TA as triangle strips, with the vertices being fed
in a CW order, so a given quad looks like:
1----3----5
|\ |\ |
| \ | \ |
| \ | \ |
| \| \|
0----2----4
convert from these triangle strips to triangles, and convert to CCW to
match OpenGL defaults */
struct tr_list *list = &rc->lists[list_type];
int num_merged = 0;
for (int i = 0, j = 0; i < list->num_surfs; i = j) {
struct ta_surface *root = &rc->surfs[list->surfs[i]];
int first_index = rc->num_indices;
/* merge adjacent surfaces at this time */
for (j = i; j < list->num_surfs; j++) {
struct ta_surface *surf = &rc->surfs[list->surfs[j]];
if (surf != root) {
if (!tr_can_merge_surfs(root, surf)) {
break;
}
num_merged++;
}
int num_indices = (surf->num_verts - 2) * 3;
CHECK_LT(rc->num_indices + num_indices, ARRAY_SIZE(rc->indices));
for (int j = 0; j < surf->num_verts - 2; j++) {
int strip_offset = surf->strip_offset + j;
int vertex_offset = surf->first_vert + j;
/* be careful to maintain a CCW winding order */
if (strip_offset & 1) {
rc->indices[rc->num_indices++] = vertex_offset + 0;
rc->indices[rc->num_indices++] = vertex_offset + 1;
rc->indices[rc->num_indices++] = vertex_offset + 2;
} else {
rc->indices[rc->num_indices++] = vertex_offset + 0;
rc->indices[rc->num_indices++] = vertex_offset + 2;
rc->indices[rc->num_indices++] = vertex_offset + 1;
}
}
}
/* update to point at triangle indices instead of the raw tristrip verts */
root->first_vert = first_index;
root->num_verts = rc->num_indices - first_index;
/* shift the list to account for merges */
list->surfs[j - num_merged - 1] = list->surfs[i];
}
list->num_surfs -= num_merged;
}
static int sort_tmp[TR_MAX_SURFS];
static float sort_minz[TR_MAX_SURFS];
@ -662,42 +720,29 @@ static int tr_compare_surf(const void *a, const void *b) {
return sort_minz[i] <= sort_minz[j];
}
static void tr_sort_render_list(struct tr *tr, struct tr_context *rc,
int list_type) {
/* sort each surface from back to front based on its minz */
static void tr_sort_surfaces(struct tr *tr, struct tr_context *rc,
int list_type) {
struct tr_list *list = &rc->lists[list_type];
/* sort each surface from back to front based on its minz */
for (int i = 0; i < list->num_surfs; i++) {
int surf_index = list->surfs[i];
struct ta_surface *surf = &rc->surfs[surf_index];
float *minz = &sort_minz[surf_index];
/* the surf coordinates have 1/w for z, so smaller values are
further away from the camera */
*minz = FLT_MAX;
struct ta_vertex *verts = &rc->verts[surf->first_vert];
CHECK_EQ(surf->num_verts, 3);
for (int j = 0; j < surf->num_verts; j++) {
int vert_index = rc->indices[surf->first_vert + j];
struct ta_vertex *vert = &rc->verts[vert_index];
*minz = MIN(*minz, vert->xyz[2]);
}
*minz = MIN(verts[0].xyz[2], verts[1].xyz[2]);
*minz = MIN(*minz, verts[2].xyz[2]);
}
msort_noalloc(list->surfs, sort_tmp, list->num_surfs, sizeof(int),
&tr_compare_surf);
}
static void tr_parse_eol(struct tr *tr, const struct ta_context *ctx,
struct tr_context *rc, const uint8_t *data) {
tr->last_poly = NULL;
tr->last_vertex = NULL;
tr->list_type = TA_NUM_LISTS;
tr->vert_type = TA_NUM_VERTS;
}
static void tr_reset(struct tr *tr, struct tr_context *rc) {
/* reset global state */
tr->last_poly = NULL;
tr->last_vertex = NULL;
tr->list_type = TA_NUM_LISTS;
tr->vert_type = TA_NUM_VERTS;
@ -714,6 +759,7 @@ static void tr_reset(struct tr *tr, struct tr_context *rc) {
for (int i = 0; i < TA_NUM_LISTS; i++) {
struct tr_list *list = &rc->lists[i];
list->num_surfs = 0;
list->num_orig_surfs = 0;
}
}
@ -821,14 +867,13 @@ void tr_convert_context(struct render_backend *r, void *userdata,
data += ta_param_size(pcw, tr.vert_type);
}
/* sort blended surface lists if requested */
/* sort surfaces if requested */
if (ctx->autosort) {
tr_sort_render_list(&tr, rc, TA_LIST_TRANSLUCENT);
tr_sort_render_list(&tr, rc, TA_LIST_PUNCH_THROUGH);
tr_sort_surfaces(&tr, rc, TA_LIST_TRANSLUCENT);
tr_sort_surfaces(&tr, rc, TA_LIST_PUNCH_THROUGH);
}
#if 0
LOG_INFO("tr_convert_convext merged %d / %d surfaces", tr.merged_surfs,
tr.merged_surfs + rc->num_surfs);
#endif
for (int i = 0; i < TA_NUM_LISTS; i++) {
tr_generate_indices(&tr, rc, i);
}
}

View File

@ -6,8 +6,7 @@
struct tr;
#define TR_MAX_SURFS (1024 * 16)
#define TR_MAX_VERTS (1024 * 64)
#define TR_MAX_SURFS (1024 * 64)
typedef uint64_t tr_texture_key_t;
@ -49,6 +48,9 @@ struct tr_param {
struct tr_list {
int surfs[TR_MAX_SURFS];
int num_surfs;
/* debug info */
int num_orig_surfs;
};
struct tr_context {
@ -60,10 +62,10 @@ struct tr_context {
struct ta_surface surfs[TR_MAX_SURFS];
int num_surfs;
struct ta_vertex verts[TR_MAX_VERTS];
struct ta_vertex verts[TR_MAX_SURFS];
int num_verts;
uint16_t indices[TR_MAX_VERTS * 3];
uint16_t indices[TR_MAX_SURFS * 3];
int num_indices;
/* sorted list of surfaces corresponding to each of the ta's polygon lists */

View File

@ -105,6 +105,10 @@ struct ta_surface {
int first_vert;
int num_verts;
/* first vertex's offset from start of original tristrip, used to control
winding order when generating indices */
int strip_offset;
};
struct ui_vertex {

View File

@ -585,7 +585,7 @@ static void tracer_render_side_menu(struct tracer *tracer) {
/* texture window */
if (igBegin("textures", NULL, 0)) {
struct ImVec2 size = {220.0f, io->DisplaySize.y * 0.85f};
struct ImVec2 size = {220.0f, io->DisplaySize.y * 0.85f * 0.5f};
struct ImVec2 pos = {io->DisplaySize.x - 220.0f, io->DisplaySize.y * 0.05f};
igSetWindowSize(size, ImGuiCond_Once);
igSetWindowPos(pos, ImGuiCond_Once);
@ -640,6 +640,34 @@ static void tracer_render_side_menu(struct tracer *tracer) {
igEnd();
}
if (igBegin("debug info", NULL, 0)) {
struct ImVec2 size = {220.0f, io->DisplaySize.y * 0.85f * 0.5f};
struct ImVec2 pos = {io->DisplaySize.x - 220.0f,
io->DisplaySize.y * 0.05f + size.y};
igSetWindowSize(size, ImGuiCond_Once);
igSetWindowPos(pos, ImGuiCond_Once);
int total_orig_surfs = 0;
int total_surfs = 0;
for (int i = 0; i < TA_NUM_LISTS; i++) {
struct tr_list *list = &tracer->rc.lists[i];
igText(list_names[i]);
igText("%d original surfaces", list->num_orig_surfs);
igText("%d draw surfaces", list->num_surfs);
igSeparator();
total_orig_surfs += list->num_orig_surfs;
total_surfs += list->num_surfs;
}
igText("%d total original surfaces", total_orig_surfs);
igText("%d total draw surfaces", total_surfs);
igText("%.2f kb index buffer", (tracer->rc.num_indices * 2.0f) / 1024.0f);
igEnd();
}
}
void tracer_render_frame(struct tracer *tracer) {
@ -659,13 +687,14 @@ void tracer_render_frame(struct tracer *tracer) {
end_surf = rp->last_surf;
}
tr_convert_context(tracer->r, tracer, &tracer_find_texture, &tracer->ctx,
&tracer->rc);
for (int i = 0; i < rc->num_surfs; i++) {
struct ta_surface *surf = &rc->surfs[i];
surf->params.debug_depth = tracer->debug_depth;
}
tr_convert_context(tracer->r, tracer, &tracer_find_texture, &tracer->ctx,
&tracer->rc);
tr_render_context_until(tracer->r, rc, end_surf);
}