From bc87a9a59de1004632565bb16d6325c7a7cb876d Mon Sep 17 00:00:00 2001 From: libretroadmin Date: Mon, 2 Nov 2026 15:55:13 +0100 Subject: [PATCH] Vulkan, OpenGL: modifier volumes and sorted indices from where they are parsed The parse's arena has a part for each list the card draws from: the vertices, the indices (with the sorted passes' room after them), and the modifier volumes' triangles and those cut (rend/modvol_clip.h); a frame's lists are moved into a slice together (parse_arena_attach()). The volumes' triangles a frame draws are drawn from there with Vulkan and OpenGL, not copied into the main buffer or uploaded each frame. With OpenGL the per-triangle sorted passes' indices are written after the frame's, in its index buffer, not uploaded to a buffer of their own; the per-pixel renderer's volume array is set again when its buffer changes. The heavy disc has a shadow volume a billion pixels off the screen, first in its list, over a polygon that takes shadows: the volumes' cut triangles, and those after it, drawn from their part (vk_parse_arena.sh, gl_parse_arena.sh), which also take the disc with volumes of each cull mode now. --- core/hw/pvr/ta_ctx.h | 28 +++- core/rend/gl4/gl4.h | 3 +- core/rend/gl4/gldraw.cpp | 17 ++- core/rend/gl4/gles.cpp | 6 +- core/rend/gles/gldraw.cpp | 36 ++++-- core/rend/gles/gles.cpp | 42 ++++-- core/rend/gles/gles.h | 6 + core/rend/gles/parse_arena_gl.c | 55 ++++---- core/rend/gles/parse_arena_gl.h | 25 ++-- core/rend/parse_arena.c | 78 +++++------ core/rend/parse_arena.h | 88 +++++++------ core/rend/vulkan/drawer.cpp | 4 +- core/rend/vulkan/drawer.h | 4 + core/rend/vulkan/oit/oit_drawer.cpp | 2 +- core/rend/vulkan/parse_arena_vk.c | 11 +- core/rend/vulkan/parse_arena_vk.h | 7 +- core/rend/vulkan/vulkan_renderer.h | 20 ++- tools/gl/gl_parse_arena.sh | 8 +- tools/rend/parse_arena_test.c | 193 +++++++++++++++++----------- tools/threads/live_disc.py | 144 +++++++++++---------- tools/threads/live_prog.c | 16 ++- tools/vulkan/vk_parse_arena.sh | 7 +- 22 files changed, 480 insertions(+), 320 deletions(-) diff --git a/core/hw/pvr/ta_ctx.h b/core/hw/pvr/ta_ctx.h index 9f68f4c0..c0bc957e 100644 --- a/core/hw/pvr/ta_ctx.h +++ b/core/hw/pvr/ta_ctx.h @@ -3,6 +3,7 @@ #include "pvr_regs.h" #include "helper_classes.h" #include "ta_index.h" +#include "../../rend/parse_arena.h" // helper for 32 byte aligned memory allocation void* OS_aligned_malloc(size_t align, size_t size); @@ -338,6 +339,31 @@ struct TA_context { return (size_t)120 * 1024 * (ta_data_size() / TA_DATA_SIZE) * sizeof(u32); // (indices have the strips' joins) } + // the modifier volumes' triangles', and those cut's (a third of them as fans of three more) + static size_t ModTrigRoom() + { + return (size_t)16384 * (ta_data_size() / TA_DATA_SIZE) * sizeof(ModTriangle); + } + static size_t MvClipRoom() + { + return ModTrigRoom() + ModTrigRoom() / sizeof(ModTriangle) / 2 * sizeof(ModTriangle); + } + /* The lists a frame's parse can write where the card reads them + * (rend/parse_arena.h's parts), their rooms and the lists themselves */ + static void ArenaRooms(size_t *room) + { + room[PARSE_ARENA_VERTS] = VertRoom(); + room[PARSE_ARENA_IDX] = IdxRoom(); + room[PARSE_ARENA_MODTRIG] = ModTrigRoom(); + room[PARSE_ARENA_MV_CLIP] = MvClipRoom(); + } + void ArenaLists(ta_list_t **lists) + { + lists[PARSE_ARENA_VERTS] = &rend.verts.l; + lists[PARSE_ARENA_IDX] = &rend.idx.l; + lists[PARSE_ARENA_MODTRIG] = &rend.modtrig.l; + lists[PARSE_ARENA_MV_CLIP] = &rend.mv_clip.l; + } /* false: there was not the memory for it, and it holds nothing (a * NAOMI 2's frame takes about 60 megabytes). @data: the data goes * there, a slice of ta_data_size() bytes of the GPU pool's (@slot), @@ -346,7 +372,7 @@ struct TA_context { data_size = ta_data_size(); const unsigned scale = data_size / TA_DATA_SIZE; - unsigned modtrig_size = 16384 * scale; + const unsigned modtrig_size = (unsigned)(ModTrigRoom() / sizeof(ModTriangle)); pool = data != NULL ? slot : -1; gpu_serial = 0; tad.Reset(data != NULL ? data : (u8*)OS_aligned_malloc(32, data_size)); diff --git a/core/rend/gl4/gl4.h b/core/rend/gl4/gl4.h index 38c0a625..37359dca 100644 --- a/core/rend/gl4/gl4.h +++ b/core/rend/gl4/gl4.h @@ -328,7 +328,8 @@ bool isTwoVolumes(const PolyParam pp) \n\ // (the vertex and index buffers: 0, the renderer's own) void gl4SetupMainVBO(GLuint geometry = 0, GLuint idxs = 0); -void gl4SetupModvolVBO(); +// (the volumes' triangles' buffer: 0, the renderer's own) +void gl4SetupModvolVBO(GLuint modvols = 0); /* A frame decoded on the GPU (pvrrc.gpu, rend/gles/ta_decoder_gl.h): its * vertices, indices, modifier volume triangles and translucent polygons' * words bound in their places (the vertex arrays' and binding 1), and each diff --git a/core/rend/gl4/gldraw.cpp b/core/rend/gl4/gldraw.cpp index 004cd426..6f26422c 100644 --- a/core/rend/gl4/gldraw.cpp +++ b/core/rend/gl4/gldraw.cpp @@ -539,10 +539,11 @@ void gl4SetupMainVBO(GLuint geometry, GLuint idxs) glVertexAttribPointer(VERTEX_UV1_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, u1)); glCheck(); } -/* What the vertex arrays and storage binding 1 have: -1 not known, -2 the - * GPU decode's, or the vertex buffer of the renderer's own or of the - * frame's they have */ +/* What the vertex arrays and storage binding 1 have: frame_buffers -1 not + * known, -2 the GPU decode's, or the vertex buffer of the renderer's own + * or of the frame's they have; and the volumes' triangles' buffer */ static long frame_buffers = -1; +static GLuint frame_modvols; void gl4ForgetFrameBuffers() { @@ -552,17 +553,19 @@ void gl4ForgetFrameBuffers() void gl4BindFrameBuffers(bool gpu, bool own) { const GLuint geometry = own ? gl4.vbo.geometry : gl_frame_vertex_buffer(gl4.vbo.geometry); + const GLuint modvols = own ? gl4.vbo.modvols : gl_frame_modvol_buffer(gl4.vbo.modvols); const long want = gpu ? -2 : (long)geometry; - if (frame_buffers == want) + if (frame_buffers == want && (gpu || frame_modvols == modvols)) return; frame_buffers = want; + frame_modvols = modvols; if (!gpu) { gl4SetupMainVBO(geometry, own ? gl4.vbo.idxs : gl_frame_index_buffer(gl4.vbo.idxs)); glDisableVertexAttribArray(VERTEX_PP_ARRAY); // (an attribute without its array: the number is 0) gl4.vertexAttribI4ui(VERTEX_PP_ARRAY, 0, 0, 0, 0); - gl4SetupModvolVBO(); + gl4SetupModvolVBO(modvols); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, gl4.vbo.tr_poly_params); glBindVertexArray(gl4.vbo.main_vao); return; @@ -589,11 +592,11 @@ void gl4BindFrameBuffers(bool gpu, bool own) glBindVertexArray(gl4.vbo.main_vao); } -void gl4SetupModvolVBO() +void gl4SetupModvolVBO(GLuint modvols) { glBindVertexArray(gl4.vbo.modvol_vao); - glBindBuffer(GL_ARRAY_BUFFER, gl4.vbo.modvols); + glBindBuffer(GL_ARRAY_BUFFER, modvols != 0 ? modvols : gl4.vbo.modvols); //setup vertex buffers attrib pointers glEnableVertexAttribArray(VERTEX_POS_ARRAY); glCheck(); diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index 2cea5c05..397e6c09 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -882,8 +882,8 @@ static bool RenderFrame() glBufferData(GL_ELEMENT_ARRAY_BUFFER,pvrrc.idx.bytes(),pvrrc.idx.head(),GL_STREAM_DRAW); glCheck(); } - //Modvol VBO - if (pvrrc.modtrig.used() && !pvrrc.mv_clip.used()) + //Modvol VBO (but where they were parsed into the buffers the card reads) + if (pvrrc.modtrig.used() && !pvrrc.mv_clip.used() && gl_frame_modvol_buffer(0) == 0) { glBindVertexArray(gl4.vbo.modvol_vao); glBindBuffer(GL_ARRAY_BUFFER, gl4.vbo.modvols); glCheck(); @@ -916,7 +916,7 @@ static bool RenderFrame() /* The volumes' triangles with those far off the screen cut * (rend/modvol_clip.h), however the frame was decoded: the volumes' * array then draws them, and is set again for the next frame */ - if (!pvrrc.isRenderFramebuffer && pvrrc.mv_clip.used()) + if (!pvrrc.isRenderFramebuffer && pvrrc.mv_clip.used() && gl_frame_modvol_buffer(0) == 0) { gl4SetupModvolVBO(); glBufferData(GL_ARRAY_BUFFER, pvrrc.mv_clip.bytes(), pvrrc.mv_clip.head(), GL_STREAM_DRAW); glCheck(); diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index 9e433df8..e826bbcc 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -339,8 +339,11 @@ static bool PrepassSupported() } /* The sorted triangles of the render pass's translucent list, when they - * are drawn by triangle, and its depth prepass's: both uploaded */ -static void SortTriangles(int first, int count, bool sortTriangles, bool lastPass) + * are drawn by triangle, and its depth prepass's: written after the + * frame's indices where the frame was parsed into the buffers the card + * reads, and bound, or uploaded to a buffer of their own. Where they start + * in the buffer bound, in bytes. */ +static uintptr_t SortTriangles(int first, int count, bool sortTriangles, bool lastPass) { /* (kept from one frame to the next: grown, never made again) */ static grow_buf_t indices; @@ -348,18 +351,26 @@ static void SortTriangles(int first, int count, bool sortTriangles, bool lastPas prepassDraws.count = 0; const bool prepass = PrepassSupported(); if (!sortTriangles && !prepass) - return; + return 0; /* room for both, made u16 in place if the indices are */ - u32 *out = (u32 *)grow_buf_reserve(&indices, (size_t)(6 * SortedTriangles(first, count) + 1) * sizeof(u32)); + const size_t room = (size_t)(6 * SortedTriangles(first, count) + 1) * sizeof(u32); + size_t at = 0; + u32 *here = gl_frame_sorted(room, &at); + u32 *out = here != nullptr ? here : (u32 *)grow_buf_reserve(&indices, room); if (out == nullptr) - return; + return 0; u32 n = 0; if (sortTriangles) n = GenSortedTo(first, count, &sortedDraws, out, false); if (prepass) n += GenPrepassTo(first, count, lastPass, &prepassDraws, out + n, n, false); if (n == 0) - return; + return 0; + if (here != nullptr) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_frame_index_buffer(gl.vbo.idxs)); glCheck(); + return at; + } //Bind and upload sorted index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl.vbo.idxs2); glCheck(); @@ -373,6 +384,7 @@ static void SortTriangles(int first, int count, bool sortTriangles, bool lastPas } else glBufferData(GL_ELEMENT_ARRAY_BUFFER, n * sizeof(u32), out, GL_STREAM_DRAW); + return 0; } /* The depth prepass of a sorted list: rend/sorter.h (with the sorted @@ -599,7 +611,7 @@ static void SetupModvolVBO(bool gpu) if (gpu) glTaDecoder.BindModVols(); else - glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.modvols); + glBindBuffer(GL_ARRAY_BUFFER, gl_frame_modvol_buffer(gl.vbo.modvols)); //setup vertex buffers attrib pointers glEnableVertexAttribArray(VERTEX_POS_ARRAY); @@ -867,19 +879,19 @@ void DrawStrips() } else if (settings.pvr.Emulation.AlphaSortMode == 0) { - SortTriangles(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count, true, lastPass); - DrawPrepass(SortDraws(&prepassDraws), prepassDraws.count, 0); - DrawSorted(SortDraws(&sortedDraws), sortedDraws.count, !lastPass, 0); + const uintptr_t base = SortTriangles(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count, true, lastPass); + DrawPrepass(SortDraws(&prepassDraws), prepassDraws.count, base); + DrawSorted(SortDraws(&sortedDraws), sortedDraws.count, !lastPass, base); // (the main indices again, after sorted triangles or a prepass) if (sortedDraws.count != 0 || prepassDraws.count != 0) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_frame_index_buffer(gl.vbo.idxs)); } else { - SortTriangles(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count, false, lastPass); + const uintptr_t base = SortTriangles(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count, false, lastPass); if (prepassDraws.count != 0) { - DrawPrepass(SortDraws(&prepassDraws), prepassDraws.count, 0); + DrawPrepass(SortDraws(&prepassDraws), prepassDraws.count, base); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_frame_index_buffer(gl.vbo.idxs)); } SortPParams(previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index ae732c9d..c06a44e2 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1563,15 +1563,15 @@ static bool RenderFrame(void) upload_vertex_indices(); } - //Modvol VBO - if (pvrrc.modtrig.used() && !pvrrc.mv_clip.used()) + //Modvol VBO (but where they were parsed into the buffers the card reads) + if (pvrrc.modtrig.used() && !pvrrc.mv_clip.used() && gl_frame_modvol_buffer(0) == 0) { glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.modvols); glCheck(); glBufferData(GL_ARRAY_BUFFER,pvrrc.modtrig.bytes(),pvrrc.modtrig.head(),GL_STREAM_DRAW); glCheck(); } } // the volumes' triangles with those far off the screen cut (rend/modvol_clip.h), however the frame was decoded - if (pvrrc.mv_clip.used()) + if (pvrrc.mv_clip.used() && gl_frame_modvol_buffer(0) == 0) { glBindBuffer(GL_ARRAY_BUFFER, gl.vbo.modvols); glCheck(); glBufferData(GL_ARRAY_BUFFER, pvrrc.mv_clip.bytes(), pvrrc.mv_clip.head(), GL_STREAM_DRAW); glCheck(); @@ -1698,24 +1698,39 @@ static parse_arena_gl_t parse_arena; static bool parse_arena_tried; #endif -GLuint gl_frame_vertex_buffer(GLuint own) +// (the buffer of part @part of the arena's the frame was parsed into, or @own) +static GLuint frame_buffer(unsigned part, GLuint own) { #ifdef HAVE_GL4 - const GLuint b = parse_arena_gl_vertex_buffer(&parse_arena); + const GLuint b = parse_arena_gl_buffer(&parse_arena, part); if (b != 0) return b; #endif return own; } +GLuint gl_frame_vertex_buffer(GLuint own) +{ + return frame_buffer(PARSE_ARENA_VERTS, own); +} + GLuint gl_frame_index_buffer(GLuint own) +{ + return frame_buffer(PARSE_ARENA_IDX, own); +} + +GLuint gl_frame_modvol_buffer(GLuint own) +{ + return frame_buffer(pvrrc.mv_clip.used() != 0 ? PARSE_ARENA_MV_CLIP : PARSE_ARENA_MODTRIG, own); +} + +u32 *gl_frame_sorted(size_t bytes, size_t *offset) { #ifdef HAVE_GL4 - const GLuint b = parse_arena_gl_index_buffer(&parse_arena); - if (b != 0) - return b; + return (u32 *)parse_arena_gl_sorted(&parse_arena, bytes, offset); +#else + return nullptr; #endif - return own; } void gl_frame_drawn() @@ -1743,15 +1758,18 @@ static void parse_where_drawn(TA_context* ctx) if (!parse_arena_tried) { parse_arena_tried = true; - if (parse_arena_gl_init(&parse_arena, TA_context::VertRoom(), TA_context::IdxRoom(), 3)) + size_t room[PARSE_ARENA_PARTS]; + TA_context::ArenaRooms(room); + if (parse_arena_gl_init(&parse_arena, room, 3)) NOTICE_LOG(RENDERER, "OpenGL: renders parsed where the card reads them, not copied there"); else INFO_LOG(RENDERER, "OpenGL: renders copied to the card (the buffers for them could not be had, or read slowly)"); } /* (four vertices at the head the parse keeps: the background's, FillBGP()'s) */ static bool copied; - if (!parse_arena_gl_attach(&parse_arena, &ctx->rend.verts.l, 4 * sizeof(Vertex), &ctx->rend.idx.l) - && parse_arena.ready && !copied) + ta_list_t *lists[PARSE_ARENA_PARTS]; + ctx->ArenaLists(lists); + if (!parse_arena_gl_attach(&parse_arena, lists, 4 * sizeof(Vertex)) && parse_arena.ready && !copied) { copied = true; NOTICE_LOG(RENDERER, "OpenGL: a render copied to the card, no slot of the parse's free"); diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index a22236b4..fdfd68f3 100644 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -223,6 +223,12 @@ bool ProcessFrame(TA_context* ctx, u32 taFlags = 0); * @own, which the lists are copied into */ GLuint gl_frame_vertex_buffer(GLuint own); GLuint gl_frame_index_buffer(GLuint own); +// ...the volumes' triangles drawn (those cut where there are: rend/modvol_clip.h) +GLuint gl_frame_modvol_buffer(GLuint own); +/* Room for @bytes of a sorted pass's indices after the frame's in its index + * buffer (gl_frame_index_buffer()), and their @offset there; nullptr: none, + * and they go to a buffer of their own */ +u32 *gl_frame_sorted(size_t bytes, size_t *offset); // ...and the frame's draws issued: the buffers the card's until they are done void gl_frame_drawn(); // (the context going) diff --git a/core/rend/gles/parse_arena_gl.c b/core/rend/gles/parse_arena_gl.c index 08120a14..b700c952 100644 --- a/core/rend/gles/parse_arena_gl.c +++ b/core/rend/gles/parse_arena_gl.c @@ -71,26 +71,27 @@ static int reads_fast(unsigned char *map, size_t bytes) return (t2 - t1) <= 4 * (t1 - t0) + 200; } -int parse_arena_gl_init(parse_arena_gl_t *a, size_t vert_room, size_t idx_room, unsigned slots) +int parse_arena_gl_init(parse_arena_gl_t *a, const size_t *room, unsigned slots) { - unsigned i; + const size_t probe = room[PARSE_ARENA_VERTS] < ((size_t)1 << 20) ? room[PARSE_ARENA_VERTS] : (size_t)1 << 20; + unsigned i, p; memset(a, 0, sizeof(*a)); - if (parse_arena_layout(&a->arena, vert_room, idx_room, slots, 1) == 0) + if (parse_arena_layout(&a->arena, room, slots, 1) == 0) return 0; for (i = 0; i < slots; i++) - { - void *v = NULL, *x = NULL; - a->verts[i] = make_buffer(vert_room, &v); - a->idx[i] = make_buffer(idx_room, &x); - if (v == NULL || x == NULL) + for (p = 0; p < PARSE_ARENA_PARTS; p++) { - parse_arena_gl_term(a); - return 0; + void *m = NULL; + a->buffer[i][p] = make_buffer(a->arena.space[p], &m); + if (m == NULL) + { + parse_arena_gl_term(a); + return 0; + } + parse_arena_bind_slot(&a->arena, i, p, m); } - parse_arena_bind_slot(&a->arena, i, v, x); - } - if (!reads_fast(a->arena.slot_verts[0], vert_room < ((size_t)1 << 20) ? vert_room : (size_t)1 << 20)) + if (!reads_fast(a->arena.part_mem[0][PARSE_ARENA_VERTS], probe)) { parse_arena_gl_term(a); return 0; @@ -101,7 +102,7 @@ int parse_arena_gl_init(parse_arena_gl_t *a, size_t vert_room, size_t idx_room, void parse_arena_gl_term(parse_arena_gl_t *a) { - unsigned i; + unsigned i, p; if (a->ready) parse_arena_detach(&a->arena); @@ -110,10 +111,9 @@ void parse_arena_gl_term(parse_arena_gl_t *a) if (a->fence[i] != NULL) glDeleteSync(a->fence[i]); /* (deleting a buffer unmaps it) */ - if (a->verts[i] != 0) - glDeleteBuffers(1, &a->verts[i]); - if (a->idx[i] != 0) - glDeleteBuffers(1, &a->idx[i]); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + if (a->buffer[i][p] != 0) + glDeleteBuffers(1, &a->buffer[i][p]); } memset(a, 0, sizeof(*a)); } @@ -134,7 +134,7 @@ static int slot_free(parse_arena_gl_t *a, unsigned s) return 1; } -int parse_arena_gl_attach(parse_arena_gl_t *a, ta_list_t *verts, size_t vert_head, ta_list_t *idx) +int parse_arena_gl_attach(parse_arena_gl_t *a, ta_list_t *const *lists, size_t vert_head) { unsigned i; @@ -145,9 +145,10 @@ int parse_arena_gl_attach(parse_arena_gl_t *a, ta_list_t *verts, size_t vert_hea const unsigned s = (a->next + i) % a->arena.slices; if (!slot_free(a, s)) continue; - if (!parse_arena_attach(&a->arena, s, verts, vert_head, idx)) + if (!parse_arena_attach(&a->arena, s, lists, vert_head)) return 0; a->next = (s + 1) % a->arena.slices; + a->sorted = 0; return 1; } return 0; @@ -169,12 +170,18 @@ void parse_arena_gl_cancel(parse_arena_gl_t *a) parse_arena_detach(&a->arena); } -GLuint parse_arena_gl_vertex_buffer(const parse_arena_gl_t *a) +GLuint parse_arena_gl_buffer(const parse_arena_gl_t *a, unsigned part) { - return a->ready && a->arena.slot >= 0 ? a->verts[a->arena.slot] : 0; + return a->ready && a->arena.slot >= 0 && part < PARSE_ARENA_PARTS ? a->buffer[a->arena.slot][part] : 0; } -GLuint parse_arena_gl_index_buffer(const parse_arena_gl_t *a) +void *parse_arena_gl_sorted(parse_arena_gl_t *a, size_t bytes, size_t *offset) { - return a->ready && a->arena.slot >= 0 ? a->idx[a->arena.slot] : 0; + const size_t at = a->arena.room[PARSE_ARENA_IDX] + a->sorted; + + if (!a->ready || a->arena.slot < 0 || bytes > a->arena.sort_room - a->sorted) + return NULL; + a->sorted += bytes; + *offset = at; + return a->arena.part_mem[a->arena.slot][PARSE_ARENA_IDX] + at; } diff --git a/core/rend/gles/parse_arena_gl.h b/core/rend/gles/parse_arena_gl.h index ab45e018..889aaa6a 100644 --- a/core/rend/gles/parse_arena_gl.h +++ b/core/rend/gles/parse_arena_gl.h @@ -1,7 +1,9 @@ #ifndef PARSE_ARENA_GL_H #define PARSE_ARENA_GL_H -/* The parse's arena (rend/parse_arena.h) with OpenGL: a vertex and an - * index buffer for each of a few frames in flight, of immutable storage +/* The parse's arena (rend/parse_arena.h) with OpenGL: a buffer for each of + * its parts (the vertices, the indices with the sorted passes' after them, + * the volumes' triangles and those cut) for each of a few frames in + * flight, of immutable storage * mapped persistently and coherently in the client's memory (OpenGL 4.4 * or ARB_buffer_storage), where the frame is parsed and then drawn from: * no copy into a buffer for the card. @@ -21,30 +23,33 @@ extern "C" { typedef struct parse_arena_gl { parse_arena_t arena; - GLuint verts[PARSE_ARENA_SLOTS]; - GLuint idx[PARSE_ARENA_SLOTS]; + GLuint buffer[PARSE_ARENA_SLOTS][PARSE_ARENA_PARTS]; GLsync fence[PARSE_ARENA_SLOTS]; + size_t sorted; /* the sorted passes' bytes so far this frame */ unsigned next; /* the slot looked at first */ int ready; /* (all zero, as a static one is before it is made: none) */ } parse_arena_gl_t; -/* Made for @slots frames of lists of @vert_room and @idx_room bytes, in +/* Made for @slots frames of lists of the rooms @room (by PARSE_ARENA_*), in * the context that is current; false, and nothing kept, where the buffers * cannot be made and mapped, or read back slowly */ -int parse_arena_gl_init(parse_arena_gl_t *a, size_t vert_room, size_t idx_room, unsigned slots); +int parse_arena_gl_init(parse_arena_gl_t *a, const size_t *room, unsigned slots); void parse_arena_gl_term(parse_arena_gl_t *a); /* The frame whose lists these are parsed into a free slot, from now till * parse_arena_gl_done() (its draws issued) or parse_arena_gl_cancel() (it * is not drawn); false where no slot is free, or the lists are not of the * arena's size (parse_arena_attach(), @vert_head as there) */ -int parse_arena_gl_attach(parse_arena_gl_t *a, ta_list_t *verts, size_t vert_head, ta_list_t *idx); +int parse_arena_gl_attach(parse_arena_gl_t *a, ta_list_t *const *lists, size_t vert_head); void parse_arena_gl_done(parse_arena_gl_t *a); void parse_arena_gl_cancel(parse_arena_gl_t *a); -/* The frame's buffers while it is attached, 0 otherwise */ -GLuint parse_arena_gl_vertex_buffer(const parse_arena_gl_t *a); -GLuint parse_arena_gl_index_buffer(const parse_arena_gl_t *a); +/* The frame's buffer of part @part while it is attached, 0 otherwise */ +GLuint parse_arena_gl_buffer(const parse_arena_gl_t *a, unsigned part); +/* Room for @bytes of a sorted pass's indices after the frame's, in its + * index buffer: where to write them, and (@offset) where they are in the + * buffer; NULL where the frame is not attached or the room is used up */ +void *parse_arena_gl_sorted(parse_arena_gl_t *a, size_t bytes, size_t *offset); #ifdef __cplusplus } diff --git a/core/rend/parse_arena.c b/core/rend/parse_arena.c index 74727ac1..fe9c6e42 100644 --- a/core/rend/parse_arena.c +++ b/core/rend/parse_arena.c @@ -7,44 +7,46 @@ static size_t round_up(size_t n, size_t align) return (n + align - 1) / align * align; } -size_t parse_arena_layout(parse_arena_t *a, size_t vert_room, size_t idx_room, unsigned slices, size_t align) +size_t parse_arena_layout(parse_arena_t *a, const size_t *room, unsigned slices, size_t align) { + size_t at = 0; + unsigned p; + memset(a, 0, sizeof(*a)); a->slot = -1; if (slices > PARSE_ARENA_SLOTS) return 0; if (align == 0) align = 1; - a->vert_room = vert_room; - a->idx_room = idx_room; /* (the sorted passes': as much again as the indices, which is room for * more translucent triangles than a frame has; more, and that frame is * drawn from a copy) */ - a->sort_room = idx_room; - a->idx_at = round_up(vert_room, align); - a->slice = round_up(a->idx_at + idx_room + a->sort_room, align); + a->sort_room = room[PARSE_ARENA_IDX]; + for (p = 0; p < PARSE_ARENA_PARTS; p++) + { + a->room[p] = room[p]; + a->space[p] = room[p] + (p == PARSE_ARENA_IDX ? a->sort_room : 0); + a->at[p] = at; + at = round_up(at + a->space[p], align); + } + a->slice = at; a->slices = slices; - a->slot = -1; return a->slice * slices; } void parse_arena_bind(parse_arena_t *a, void *mem) { - unsigned i; + unsigned i, p; a->mem = (unsigned char *)mem; for (i = 0; i < a->slices; i++) - { - a->slot_verts[i] = mem != NULL ? a->mem + a->slice * i : NULL; - a->slot_idx[i] = mem != NULL ? a->mem + a->slice * i + a->idx_at : NULL; - } + for (p = 0; p < PARSE_ARENA_PARTS; p++) + a->part_mem[i][p] = mem != NULL ? a->mem + a->slice * i + a->at[p] : NULL; } -void parse_arena_bind_slot(parse_arena_t *a, unsigned slot, void *verts, void *idx) +void parse_arena_bind_slot(parse_arena_t *a, unsigned slot, unsigned part, void *mem) { - if (slot >= a->slices) - return; - a->slot_verts[slot] = (unsigned char *)verts; - a->slot_idx[slot] = (unsigned char *)idx; + if (slot < a->slices && part < PARSE_ARENA_PARTS) + a->part_mem[slot][part] = (unsigned char *)mem; } /* @l pointed at @room bytes at @at, with what it holds - and at least its @@ -58,38 +60,40 @@ static void move_into(ta_list_t *l, unsigned char *at, size_t room, size_t head) l->end = at + room; } -int parse_arena_attach(parse_arena_t *a, unsigned slot, ta_list_t *verts, size_t vert_head, ta_list_t *idx) +int parse_arena_attach(parse_arena_t *a, unsigned slot, ta_list_t *const *lists, size_t vert_head) { - if (slot >= a->slices || a->slot_verts[slot] == NULL || a->slot_idx[slot] == NULL || a->slot >= 0 - || (size_t)(verts->end - verts->base) != a->vert_room || (size_t)(idx->end - idx->base) != a->idx_room - || vert_head > a->vert_room) + unsigned p; + + if (slot >= a->slices || a->slot >= 0 || vert_head > a->room[PARSE_ARENA_VERTS]) return 0; - a->own_verts = *verts; - a->own_idx = *idx; - a->verts = verts; - a->idx = idx; + for (p = 0; p < PARSE_ARENA_PARTS; p++) + if (a->part_mem[slot][p] == NULL || (size_t)(lists[p]->end - lists[p]->base) != a->room[p]) + return 0; a->slot = (int)slot; - move_into(verts, a->slot_verts[slot], a->vert_room, vert_head); - move_into(idx, a->slot_idx[slot], a->idx_room, 0); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + { + a->own[p] = *lists[p]; + a->lists[p] = lists[p]; + move_into(lists[p], a->part_mem[slot][p], a->room[p], p == PARSE_ARENA_VERTS ? vert_head : 0); + } return 1; } void parse_arena_detach(parse_arena_t *a) { + unsigned p; + if (a->slot < 0) return; - *a->verts = a->own_verts; - *a->idx = a->own_idx; - a->verts = a->idx = NULL; + for (p = 0; p < PARSE_ARENA_PARTS; p++) + { + *a->lists[p] = a->own[p]; + a->lists[p] = NULL; + } a->slot = -1; } -size_t parse_arena_vert_offset(const parse_arena_t *a, unsigned slot) +size_t parse_arena_offset(const parse_arena_t *a, unsigned slot, unsigned part) { - return a->slice * slot; -} - -size_t parse_arena_idx_offset(const parse_arena_t *a, unsigned slot) -{ - return a->slice * slot + a->idx_at; + return a->slice * slot + a->at[part]; } diff --git a/core/rend/parse_arena.h b/core/rend/parse_arena.h index c479ef98..ae9ac60b 100644 --- a/core/rend/parse_arena.h +++ b/core/rend/parse_arena.h @@ -1,17 +1,20 @@ #ifndef PARSE_ARENA_H #define PARSE_ARENA_H -/* Where a frame's parse writes its vertices and indices when the card - * draws them from there: memory of the renderer's, which the card reads - * and the processor has in its cache (a device whose memory is the - * host's), in slices, one for each of the renderer's frames in flight. +/* Where a frame's parse writes what the card draws when the card draws it + * from there: memory of the renderer's, which the card reads and the + * processor has in its cache (a device whose memory is the host's, or the + * client's memory a buffer is mapped in), in slices, one for each of the + * renderer's frames in flight. * * A render's frame is parsed into the slice of its frame in flight (the - * renderer's command pool's slot, which is free when the frame before in - * that slot is done on the card: nothing here waits), and drawn from there. - * The vertices are not copied into a buffer for the card: they are in one. - * A slice is the room a frame's lists have (ta_ctx.h), so a frame never - * runs out of it here where it would not have in its own; and the sorted - * passes' indices go after the frame's, in room of their own. + * renderer's slot, which is free when the frame before in that slot is + * done on the card: nothing here waits), and drawn from there. Its lists - + * the vertices, the indices, the modifier volumes' triangles and those of + * them cut (rend/modvol_clip.h) - are not copied into a buffer for the + * card: they are in one. A slice has the room a frame's lists have + * (ta_ctx.h), so a frame never runs out of it here where it would not have + * in its own; and the sorted passes' indices go after the frame's, in room + * of their own. * * The frame's own lists are put back when it has been drawn: they are the * frame's, and go with it. C89. */ @@ -25,50 +28,57 @@ extern "C" { /* (most slices an arena has) */ #define PARSE_ARENA_SLOTS 8 +/* A slice's parts: a list of the frame's each */ +enum +{ + PARSE_ARENA_VERTS = 0, + PARSE_ARENA_IDX, + PARSE_ARENA_MODTRIG, + PARSE_ARENA_MV_CLIP, + PARSE_ARENA_PARTS +}; + typedef struct parse_arena { - unsigned char *mem; /* mapped: the slices one after another */ - /* ...or a slice's vertices and indices each where a buffer of their - * own is mapped (parse_arena_bind_slot()) */ - unsigned char *slot_verts[PARSE_ARENA_SLOTS]; - unsigned char *slot_idx[PARSE_ARENA_SLOTS]; - size_t vert_room; /* a slice's vertices' bytes */ - size_t idx_room; /* ...its indices' */ - size_t sort_room; /* ...and the sorted passes', after them */ - size_t idx_at; /* where in a slice the indices start */ - size_t slice; /* a slice's bytes */ + unsigned char *mem; /* mapped: the slices one after another */ + /* each part of each slice, where it is: in mem, or in a buffer of its + * own mapped (parse_arena_bind_slot()) */ + unsigned char *part_mem[PARSE_ARENA_SLOTS][PARSE_ARENA_PARTS]; + size_t room[PARSE_ARENA_PARTS]; /* a list's bytes */ + size_t space[PARSE_ARENA_PARTS]; /* ...and a part's: the indices' with the sorted passes' after */ + size_t at[PARSE_ARENA_PARTS]; /* where in a slice a part starts */ + size_t sort_room; /* (the sorted passes' bytes) */ + size_t slice; /* a slice's bytes */ unsigned slices; /* while a frame's lists are in a slice: which, and the lists as they * were, to be put back */ int slot; - ta_list_t *verts, *idx; - ta_list_t own_verts, own_idx; + ta_list_t *lists[PARSE_ARENA_PARTS]; + ta_list_t own[PARSE_ARENA_PARTS]; } parse_arena_t; -/* The layout for @slices (PARSE_ARENA_SLOTS at most) of rooms of - * @vert_room and @idx_room bytes (a frame's lists'), each part of a slice +/* The layout for @slices (PARSE_ARENA_SLOTS at most) of the rooms @room + * (a frame's lists', in bytes, by PARSE_ARENA_*), each part of a slice * @align bytes aligned: the bytes it takes, 0 for too many slices. No * memory yet (parse_arena_bind()). */ -size_t parse_arena_layout(parse_arena_t *a, size_t vert_room, size_t idx_room, unsigned slices, size_t align); +size_t parse_arena_layout(parse_arena_t *a, const size_t *room, unsigned slices, size_t align); void parse_arena_bind(parse_arena_t *a, void *mem); -/* ...or slice @slot's vertices at @verts (vert_room bytes) and its indices - * at @idx (idx_room bytes, and sort_room after them where the sorted - * passes go there), apart */ -void parse_arena_bind_slot(parse_arena_t *a, unsigned slot, void *verts, void *idx); +/* ...or part @part of slice @slot at @mem (space[part] bytes), apart */ +void parse_arena_bind_slot(parse_arena_t *a, unsigned slot, unsigned part, void *mem); -/* The frame whose lists these are parsed into slice @slot: what they hold - * already copied there - and of the vertices, at least the first - * @vert_head bytes, which the parse keeps though the list is empty (the - * background polygon's, FillBGP()'s) - and they point there, till - * parse_arena_detach(). False, and nothing done, where they are not of - * the room the slices are (a machine whose frames are another size). */ -int parse_arena_attach(parse_arena_t *a, unsigned slot, ta_list_t *verts, size_t vert_head, ta_list_t *idx); +/* The frame whose lists these are (@lists, by PARSE_ARENA_*) parsed into + * slice @slot: what they hold already copied there - and of the vertices, + * at least the first @vert_head bytes, which the parse keeps though the + * list is empty (the background polygon's, FillBGP()'s) - and they point + * there, till parse_arena_detach(). False, and nothing done, where they + * are not of the rooms the parts are (a machine whose frames are another + * size). */ +int parse_arena_attach(parse_arena_t *a, unsigned slot, ta_list_t *const *lists, size_t vert_head); /* The frame's own lists back. What was parsed stays in the slice. */ void parse_arena_detach(parse_arena_t *a); -/* Where in the memory slice @slot's vertices and indices are */ -size_t parse_arena_vert_offset(const parse_arena_t *a, unsigned slot); -size_t parse_arena_idx_offset(const parse_arena_t *a, unsigned slot); +/* Where in the memory part @part of slice @slot is */ +size_t parse_arena_offset(const parse_arena_t *a, unsigned slot, unsigned part); #ifdef __cplusplus } diff --git a/core/rend/vulkan/drawer.cpp b/core/rend/vulkan/drawer.cpp index 215ce89c..5acdcc25 100644 --- a/core/rend/vulkan/drawer.cpp +++ b/core/rend/vulkan/drawer.cpp @@ -429,7 +429,7 @@ BufferData *Drawer::SortIntoMainBuffer() const u32 sortRoom = pvrrc.gpu ? 0 : SortPassesRoom() * sizeof(u32); const bool here = UseParsed(sortRoom); const u32 vertexBytes = pvrrc.gpu || here ? 0 : pvrrc.verts.bytes(); - const u32 modVolBytes = ModVolsOnCpu() ? ModVolList().bytes() : 0; + const u32 modVolBytes = ModVolsOnCpu() && !here ? ModVolList().bytes() : 0; const u32 indexBytes = pvrrc.gpu || here ? 0 : pvrrc.idx.bytes(); const u32 sortedBytes = here ? 0 : sortRoom; @@ -462,7 +462,7 @@ void Drawer::UploadMainBuffer(BufferData *buffer, const FragmentShaderUniforms& buffer->upload(pvrrc.verts.bytes(), pvrrc.verts.head(), 0); buffer->upload(pvrrc.idx.bytes(), pvrrc.idx.head(), offsets.indexOffset); } - if (ModVolsOnCpu()) + if (ModVolsOnCpu() && !geometryParsed) buffer->upload(ModVolList().bytes(), ModVolList().head(), offsets.modVolOffset); buffer->upload(sizeof(fragmentUniforms), &fragmentUniforms, offsets.fragmentUniformOffset); } diff --git a/core/rend/vulkan/drawer.h b/core/rend/vulkan/drawer.h index b5bbae01..df73cf91 100644 --- a/core/rend/vulkan/drawer.h +++ b/core/rend/vulkan/drawer.h @@ -62,6 +62,7 @@ public: { vk::Buffer buffer; vk::DeviceSize vertices = 0, indices = 0; + vk::DeviceSize modVols = 0; // the volumes' triangles drawn (ModVolList()'s) u32 sortRoom = 0; // bytes }; /* This render's frame was parsed there; none (a null buffer): it is @@ -163,6 +164,9 @@ protected: const vk::DeviceSize zero = 0; if (!ModVolsOnCpu()) cmdBuffer.bindVertexBuffers(0, 1, &gpuDraw->modtrig, &zero); + // (where the parse wrote them, with the frame's geometry: geometryParsed) + else if (geometryParsed) + cmdBuffer.bindVertexBuffers(0, 1, &parsed.buffer, &parsed.modVols); else cmdBuffer.bindVertexBuffers(0, 1, &mainBuffer, &modVolOffset); } diff --git a/core/rend/vulkan/oit/oit_drawer.cpp b/core/rend/vulkan/oit/oit_drawer.cpp index 93a6d70a..bacf29f1 100644 --- a/core/rend/vulkan/oit/oit_drawer.cpp +++ b/core/rend/vulkan/oit/oit_drawer.cpp @@ -305,7 +305,7 @@ void OITDrawer::UploadMainBuffer(const OITDescriptorSets::FragmentShaderUniforms * geometry there) */ const bool here = UseParsed(0); const u32 vertexBytes = pvrrc.gpu || here ? 0 : pvrrc.verts.bytes(); - const u32 modVolBytes = ModVolsOnCpu() ? ModVolList().bytes() : 0; + const u32 modVolBytes = ModVolsOnCpu() && !here ? ModVolList().bytes() : 0; const u32 indexBytes = pvrrc.gpu || here ? 0 : pvrrc.idx.bytes(); // Vertex diff --git a/core/rend/vulkan/parse_arena_vk.c b/core/rend/vulkan/parse_arena_vk.c index c2a2538a..22b13466 100644 --- a/core/rend/vulkan/parse_arena_vk.c +++ b/core/rend/vulkan/parse_arena_vk.c @@ -9,18 +9,17 @@ #define PARSE_MEMORY (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT \ | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT) -int parse_arena_vk_init(parse_arena_vk_t *a, VkDevice device, vk_heap_t *heap, size_t vert_room, size_t idx_room, - unsigned slices) +int parse_arena_vk_init(parse_arena_vk_t *a, VkDevice device, vk_heap_t *heap, const size_t *room, unsigned slices) { VkBufferCreateInfo info; VkMemoryRequirements req; size_t bytes; memset(a, 0, sizeof(*a)); - /* (a slice's indices 256 bytes aligned: more than an index buffer's - * offset needs, and the sorted passes' after them are as aligned as - * the frame's indices' end, 4 bytes) */ - bytes = parse_arena_layout(&a->arena, vert_room, idx_room, slices, 256); + /* (a slice's parts 256 bytes aligned: more than a vertex or index + * buffer's offset needs, and the sorted passes' after the indices are + * as aligned as the frame's indices' end, 4 bytes) */ + bytes = parse_arena_layout(&a->arena, room, slices, 256); if (bytes == 0) return 0; memset(&info, 0, sizeof(info)); diff --git a/core/rend/vulkan/parse_arena_vk.h b/core/rend/vulkan/parse_arena_vk.h index 3ee51542..32a2c7a2 100644 --- a/core/rend/vulkan/parse_arena_vk.h +++ b/core/rend/vulkan/parse_arena_vk.h @@ -23,10 +23,9 @@ typedef struct parse_arena_vk vk_heap_alloc_t mem; } parse_arena_vk_t; -/* Made for @slices frames in flight with lists of @vert_room and - * @idx_room bytes; false (and nothing kept) without the memory for it */ -int parse_arena_vk_init(parse_arena_vk_t *a, VkDevice device, vk_heap_t *heap, size_t vert_room, size_t idx_room, - unsigned slices); +/* Made for @slices frames in flight with lists of the rooms @room (by + * PARSE_ARENA_*); false (and nothing kept) without the memory for it */ +int parse_arena_vk_init(parse_arena_vk_t *a, VkDevice device, vk_heap_t *heap, const size_t *room, unsigned slices); /* (the frames that used it done on the card) */ void parse_arena_vk_term(parse_arena_vk_t *a); diff --git a/core/rend/vulkan/vulkan_renderer.h b/core/rend/vulkan/vulkan_renderer.h index a8ab7a0d..74ebed39 100644 --- a/core/rend/vulkan/vulkan_renderer.h +++ b/core/rend/vulkan/vulkan_renderer.h @@ -174,8 +174,10 @@ public: * there is one: the draws read it there, uncopied (with the * background polygon's four vertices FillBGP() wrote, at the * head of the list the parse empties and takes them back in) */ + ta_list_t *lists[PARSE_ARENA_PARTS]; + ctx->ArenaLists(lists); if (parseArenaOk && parse_arena_attach(&parseArena.arena, (unsigned)texCommandPool.GetIndex(), - &ctx->rend.verts.l, 4 * sizeof(Vertex), &ctx->rend.idx.l)) + lists, 4 * sizeof(Vertex))) parsedSlot = texCommandPool.GetIndex(); result = ta_parse_vdrc(ctx); if (!result) @@ -222,9 +224,12 @@ protected: { const parse_arena_t& a = parseArena.arena; parsed.buffer = vk::Buffer(parseArena.buffer); - parsed.vertices = parse_arena_vert_offset(&a, (unsigned)parsedSlot); - parsed.indices = parse_arena_idx_offset(&a, (unsigned)parsedSlot); - parsed.sortRoom = (u32)(a.idx_room + a.sort_room - pvrrc.idx.bytes()); + parsed.vertices = parse_arena_offset(&a, (unsigned)parsedSlot, PARSE_ARENA_VERTS); + parsed.indices = parse_arena_offset(&a, (unsigned)parsedSlot, PARSE_ARENA_IDX); + parsed.sortRoom = (u32)(a.space[PARSE_ARENA_IDX] - pvrrc.idx.bytes()); + // (the volumes' triangles drawn: those cut where there are, rend/modvol_clip.h) + parsed.modVols = parse_arena_offset(&a, (unsigned)parsedSlot, + pvrrc.mv_clip.used() != 0 ? PARSE_ARENA_MV_CLIP : PARSE_ARENA_MODTRIG); if (parsedHere++ == 0) NOTICE_LOG(RENDERER, "Vulkan: renders parsed where the card reads them, not copied there"); } @@ -251,8 +256,10 @@ protected: * (Made once: a change of size comes back here, the frames on the * card done, and keeps it while it has as many slices as there are * slots, of the machine's lists' room.) */ + size_t room[PARSE_ARENA_PARTS]; + TA_context::ArenaRooms(room); if (parseArenaOk && (parseArena.arena.slices != (unsigned)texCommandPool.GetSize() - || parseArena.arena.vert_room != TA_context::VertRoom() || parseArena.arena.idx_room != TA_context::IdxRoom())) + || memcmp(parseArena.arena.room, room, sizeof(room)) != 0)) { parse_arena_vk_term(&parseArena); parseArenaOk = false; @@ -260,8 +267,7 @@ protected: if (!parseArenaOk) { parseArenaOk = parse_arena_vk_init(&parseArena, (VkDevice)GetContext()->GetDevice(), - GetContext()->GetHeap().Raw(), TA_context::VertRoom(), TA_context::IdxRoom(), - (unsigned)texCommandPool.GetSize()) != 0; + GetContext()->GetHeap().Raw(), room, (unsigned)texCommandPool.GetSize()) != 0; parsedHere = parsedCopied = 0; } DetachParse(); diff --git a/tools/gl/gl_parse_arena.sh b/tools/gl/gl_parse_arena.sh index 9f4b921b..a525b7db 100644 --- a/tools/gl/gl_parse_arena.sh +++ b/tools/gl/gl_parse_arena.sh @@ -6,7 +6,9 @@ # drawn from where it was parsed, with no OpenGL error (MESA_DEBUG), and # every frame is the one of the lists copied, as an OpenGL without # ARB_buffer_storage has it (GLF_NO_STORAGE) - the test discs' plain one, -# heavy one and DMA one, with each renderer, threaded rendering on and off, +# heavy one, DMA one and the one with modifier volumes (--cull: their +# triangles drawn from there too), the sorted passes' indices written after +# the frame's, with each renderer, threaded rendering on and off, # a VMU's screen drawn over them. # And with OpenGL ES, which has no such buffers, the frames are drawn as # ever. @@ -22,7 +24,7 @@ mkdir -p "$WORK" [ -n "${KEEP:-}" ] || trap 'rm -rf "$WORK"' EXIT ${CC:-cc} -O1 -g -Wall -o "$WORK/gl_frames" tools/gl/gl_frames.c -lEGL -lGL -ldl # (a directory a disc: their tracks' names are the same) -for disc in plain heavy dma; do +for disc in plain heavy dma cull; do mkdir -p "$WORK/$disc" case $disc in plain) python3 tools/threads/live_disc.py "$WORK/$disc/test.gdi" ;; @@ -46,7 +48,7 @@ run() { fi } -for disc in plain heavy dma; do +for disc in plain heavy dma cull; do for r in per-triangle per-pixel; do case $r in per-triangle) set -- "reicast_alpha_sorting=per-triangle (normal)" ;; diff --git a/tools/rend/parse_arena_test.c b/tools/rend/parse_arena_test.c index fbfba2c9..5504373d 100644 --- a/tools/rend/parse_arena_test.c +++ b/tools/rend/parse_arena_test.c @@ -1,9 +1,11 @@ -/* The parse's arena (core/rend/parse_arena.c): the slices laid out apart - * and aligned; a frame's lists moved into a slice with what they hold - - * and the head the parse keeps though the list is empty (the background +/* The parse's arena (core/rend/parse_arena.c): the slices and their parts + * laid out apart and aligned, the indices' part with the sorted passes' + * room after; a frame's lists moved into a slice with what they hold - and + * the head the parse keeps though the list is empty (the background * polygon's vertices: FillBGP()) - their own back after; lists of another - * size, a second attach, a slot out of range refused; a frame filling its - * slice's room overruns into its own slice, not the next. C89. + * size, a second attach, a slot out of range or with a part unbound + * refused; a frame filling its slice's room overruns into its own slice, + * not the next; parts bound apart (a buffer each, as OpenGL's). C89. * * sh tools/rend/parse_arena_test.sh */ #include @@ -15,123 +17,164 @@ static int fails; #define CHECK(c) do { if (!(c)) { printf("FAIL line %d: %s\n", __LINE__, #c); fails++; } } while (0) -#define VERT_ROOM 4096 -#define IDX_ROOM 1024 #define SLICES 3 #define ALIGN 256 #define HEAD 64 /* "four vertices" */ +static const size_t room[PARSE_ARENA_PARTS] = { 4096, 1024, 360, 540 }; +static const size_t elem[PARSE_ARENA_PARTS] = { 16, 4, 36, 36 }; + +/* the lists, each with its own memory, and how many bytes each holds */ +static int make_lists(ta_list_t *l, ta_list_t **lp, unsigned char *overrun) +{ + unsigned p; + for (p = 0; p < PARSE_ARENA_PARTS; p++) + { + if (!ta_list_init(&l[p], room[p], elem[p], overrun, "list")) + return 0; + lp[p] = &l[p]; + } + return 1; +} + int main(void) { parse_arena_t a; - ta_list_t verts, idx, other; + ta_list_t l[PARSE_ARENA_PARTS], other; + ta_list_t *lp[PARSE_ARENA_PARTS]; + unsigned char *own[PARSE_ARENA_PARTS]; unsigned char overrun = 0; unsigned char *mem; - unsigned char *own_verts, *own_idx; size_t bytes, i; - unsigned slot; + unsigned slot, p; - bytes = parse_arena_layout(&a, VERT_ROOM, IDX_ROOM, SLICES, ALIGN); + bytes = parse_arena_layout(&a, room, SLICES, ALIGN); CHECK(bytes == a.slice * SLICES); - CHECK(a.slice % ALIGN == 0 && a.idx_at % ALIGN == 0); - CHECK(a.idx_at >= VERT_ROOM && a.slice >= a.idx_at + IDX_ROOM + a.sort_room); - CHECK(a.sort_room >= IDX_ROOM); + CHECK(a.slice % ALIGN == 0); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + { + CHECK(a.at[p] % ALIGN == 0); + CHECK(a.room[p] == room[p]); + CHECK(a.space[p] == room[p] + (p == PARSE_ARENA_IDX ? a.sort_room : 0)); + if (p > 0) + CHECK(a.at[p] >= a.at[p - 1] + a.space[p - 1]); + } + CHECK(a.slice >= a.at[PARSE_ARENA_PARTS - 1] + a.space[PARSE_ARENA_PARTS - 1]); + CHECK(a.sort_room >= room[PARSE_ARENA_IDX]); mem = (unsigned char *)malloc(bytes); CHECK(mem != NULL); - if (mem == NULL) + if (mem == NULL || !make_lists(l, lp, &overrun)) return 1; memset(mem, 0xEE, bytes); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + own[p] = l[p].base; /* nothing to attach to before there is memory */ - CHECK(ta_list_init(&verts, VERT_ROOM, 16, &overrun, "verts")); - CHECK(ta_list_init(&idx, IDX_ROOM, 4, &overrun, "idx")); - CHECK(!parse_arena_attach(&a, 0, &verts, HEAD, &idx)); + CHECK(!parse_arena_attach(&a, 0, lp, HEAD)); parse_arena_bind(&a, mem); - own_verts = verts.base; - own_idx = idx.base; for (slot = 0; slot < SLICES; slot++) { - /* the head written, the list emptied after (as the TA's is) */ + /* the vertices' head written, the list emptied after (as the TA's + * is); the other lists with one element each */ for (i = 0; i < HEAD; i++) - own_verts[i] = (unsigned char)(slot * 16 + i); - verts.cur = verts.base; - /* one index already there */ - own_idx[0] = (unsigned char)(0x40 + slot); - own_idx[1] = own_idx[2] = own_idx[3] = 0; - idx.cur = idx.base + 4; + own[PARSE_ARENA_VERTS][i] = (unsigned char)(slot * 16 + i); + l[PARSE_ARENA_VERTS].cur = l[PARSE_ARENA_VERTS].base; + for (p = 1; p < PARSE_ARENA_PARTS; p++) + { + memset(own[p], (int)(0x40 + slot * 4 + p), elem[p]); + l[p].cur = l[p].base + elem[p]; + } - CHECK(parse_arena_attach(&a, slot, &verts, HEAD, &idx)); - CHECK(verts.base == mem + parse_arena_vert_offset(&a, slot)); - CHECK(idx.base == mem + parse_arena_idx_offset(&a, slot)); - CHECK(verts.cur == verts.base && (size_t)(verts.end - verts.base) == VERT_ROOM); - CHECK(idx.cur == idx.base + 4 && (size_t)(idx.end - idx.base) == IDX_ROOM); + CHECK(parse_arena_attach(&a, slot, lp, HEAD)); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + { + CHECK(l[p].base == mem + parse_arena_offset(&a, slot, p)); + CHECK((size_t)(l[p].end - l[p].base) == room[p]); + } + CHECK(l[PARSE_ARENA_VERTS].cur == l[PARSE_ARENA_VERTS].base); for (i = 0; i < HEAD; i++) - CHECK(verts.base[i] == (unsigned char)(slot * 16 + i)); - CHECK(idx.base[0] == 0x40 + slot); + CHECK(l[PARSE_ARENA_VERTS].base[i] == (unsigned char)(slot * 16 + i)); + for (p = 1; p < PARSE_ARENA_PARTS; p++) + { + CHECK(l[p].cur == l[p].base + elem[p]); + CHECK(l[p].base[0] == (unsigned char)(0x40 + slot * 4 + p) && l[p].base[elem[p] - 1] == l[p].base[0]); + } /* (one frame at a time) */ - CHECK(!parse_arena_attach(&a, slot, &verts, HEAD, &idx)); + CHECK(!parse_arena_attach(&a, slot, lp, HEAD)); - /* the parse fills its room: its slice's only, the others untouched */ - memset(verts.base, 0x11 * (slot + 1), VERT_ROOM); - memset(idx.base, 0x22 * (slot + 1), IDX_ROOM + a.sort_room); + /* the parse fills its parts' space: its slice's only, the others untouched */ + for (p = 0; p < PARSE_ARENA_PARTS; p++) + memset(l[p].base, (int)(0x11 * (slot + 1) + p), a.space[p]); parse_arena_detach(&a); - CHECK(verts.base == own_verts && idx.base == own_idx); - CHECK((size_t)(verts.end - verts.base) == VERT_ROOM); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + { + CHECK(l[p].base == own[p]); + CHECK((size_t)(l[p].end - l[p].base) == room[p]); + } parse_arena_detach(&a); /* (twice: nothing) */ - CHECK(verts.base == own_verts); + CHECK(l[PARSE_ARENA_VERTS].base == own[PARSE_ARENA_VERTS]); } for (slot = 0; slot < SLICES; slot++) - { - const unsigned char *v = mem + parse_arena_vert_offset(&a, slot); - const unsigned char *x = mem + parse_arena_idx_offset(&a, slot); - CHECK(v[0] == 0x11 * (slot + 1) && v[VERT_ROOM - 1] == 0x11 * (slot + 1)); - CHECK(x[0] == 0x22 * (slot + 1) && x[IDX_ROOM + a.sort_room - 1] == 0x22 * (slot + 1)); - } + for (p = 0; p < PARSE_ARENA_PARTS; p++) + { + const unsigned char *m = mem + parse_arena_offset(&a, slot, p); + CHECK(m[0] == (unsigned char)(0x11 * (slot + 1) + p) && m[a.space[p] - 1] == m[0]); + } - /* refused: a slot out of range, lists of another room, a head past it */ - CHECK(!parse_arena_attach(&a, SLICES, &verts, HEAD, &idx)); - CHECK(ta_list_init(&other, VERT_ROOM / 2, 16, &overrun, "verts")); - CHECK(!parse_arena_attach(&a, 0, &other, HEAD, &idx)); - CHECK(!parse_arena_attach(&a, 0, &verts, VERT_ROOM + 1, &idx)); - CHECK(verts.base == own_verts && idx.base == own_idx && other.base != NULL); + /* refused: a slot out of range, a list of another room, a head past it */ + CHECK(!parse_arena_attach(&a, SLICES, lp, HEAD)); + CHECK(ta_list_init(&other, room[PARSE_ARENA_MV_CLIP] / 2, 36, &overrun, "other")); + lp[PARSE_ARENA_MV_CLIP] = &other; + CHECK(!parse_arena_attach(&a, 0, lp, HEAD)); + lp[PARSE_ARENA_MV_CLIP] = &l[PARSE_ARENA_MV_CLIP]; + CHECK(!parse_arena_attach(&a, 0, lp, room[PARSE_ARENA_VERTS] + 1)); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + CHECK(l[p].base == own[p]); ta_list_free(&other); /* an overrun in the slice: the list's own flag, and its own slice */ - CHECK(parse_arena_attach(&a, 1, &verts, 0, &idx)); + CHECK(parse_arena_attach(&a, 1, lp, 0)); overrun = 0; - verts.cur = verts.end; - CHECK(ta_list_append(&verts, 16) == mem + parse_arena_vert_offset(&a, 1)); + l[PARSE_ARENA_VERTS].cur = l[PARSE_ARENA_VERTS].end; + CHECK(ta_list_append(&l[PARSE_ARENA_VERTS], 16) == mem + parse_arena_offset(&a, 1, PARSE_ARENA_VERTS)); CHECK(overrun == 1); parse_arena_detach(&a); - /* slices bound apart (a buffer of their own each, as OpenGL's): their - * memory, wherever it is */ + /* parts bound apart: their memory, wherever it is; a part unbound, + * refused */ { - static unsigned char vmem[2][VERT_ROOM], imem[2][IDX_ROOM]; + static unsigned char part[2][PARSE_ARENA_PARTS][8192]; parse_arena_t b; - CHECK(parse_arena_layout(&b, VERT_ROOM, IDX_ROOM, 2, 1) != 0); - CHECK(!parse_arena_attach(&b, 0, &verts, HEAD, &idx)); - parse_arena_bind_slot(&b, 0, vmem[0], imem[0]); - parse_arena_bind_slot(&b, 1, vmem[1], imem[1]); - parse_arena_bind_slot(&b, 2, vmem[0], imem[0]); /* (no slot 2: nothing) */ + CHECK(parse_arena_layout(&b, room, 2, 1) != 0); + CHECK(!parse_arena_attach(&b, 0, lp, HEAD)); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + { + parse_arena_bind_slot(&b, 0, p, part[0][p]); + if (p != PARSE_ARENA_MV_CLIP) + parse_arena_bind_slot(&b, 1, p, part[1][p]); + } + parse_arena_bind_slot(&b, 2, 0, part[0][0]); /* (no slot 2: nothing) */ + parse_arena_bind_slot(&b, 0, PARSE_ARENA_PARTS, part[0][0]); /* (no such part: nothing) */ + CHECK(!parse_arena_attach(&b, 1, lp, HEAD)); for (i = 0; i < HEAD; i++) - own_verts[i] = (unsigned char)(0x80 + i); - verts.cur = verts.base; - idx.cur = idx.base; - CHECK(parse_arena_attach(&b, 1, &verts, HEAD, &idx)); - CHECK(verts.base == vmem[1] && idx.base == imem[1]); - CHECK(memcmp(vmem[1], own_verts, HEAD) == 0); + own[PARSE_ARENA_VERTS][i] = (unsigned char)(0x80 + i); + l[PARSE_ARENA_VERTS].cur = l[PARSE_ARENA_VERTS].base; + CHECK(parse_arena_attach(&b, 0, lp, HEAD)); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + CHECK(l[p].base == part[0][p]); + CHECK(memcmp(part[0][PARSE_ARENA_VERTS], own[PARSE_ARENA_VERTS], HEAD) == 0); parse_arena_detach(&b); - CHECK(verts.base == own_verts && idx.base == own_idx); - CHECK(!parse_arena_attach(&b, 2, &verts, HEAD, &idx)); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + CHECK(l[p].base == own[p]); + CHECK(!parse_arena_attach(&b, 2, lp, HEAD)); /* more slices than there is room for: none */ - CHECK(parse_arena_layout(&b, VERT_ROOM, IDX_ROOM, PARSE_ARENA_SLOTS + 1, 1) == 0); + CHECK(parse_arena_layout(&b, room, PARSE_ARENA_SLOTS + 1, 1) == 0); CHECK(b.slot == -1); } - ta_list_free(&verts); - ta_list_free(&idx); + for (p = 0; p < PARSE_ARENA_PARTS; p++) + ta_list_free(&l[p]); free(mem); if (fails) { diff --git a/tools/threads/live_disc.py b/tools/threads/live_disc.py index 86c65ab0..6d942f15 100755 --- a/tools/threads/live_disc.py +++ b/tools/threads/live_disc.py @@ -3768,7 +3768,7 @@ f38f027127d12892222127d227d1292127d33b2113662367e07726d518e35227 # The program built with -DLIVE_HEAVY (live_prog.c): a frame of many # vertices, as a game's are, for the parse's speed -# (tools/pvr/ta_parse_bench.sh). +# (tools/pvr/ta_parse_bench.sh), and a shadow volume far off the screen. PROGRAM_HEAVY = bytes.fromhex(""" 01df02d02b40090000f0008cac0f018c020005d119206b200e40543402004227 01170b0009000900fefcffff020005d119205b200e4044340200422601160b00 @@ -3847,8 +3847,8 @@ f3620c7225840c63284326840c6118613b2127840c600b21b9931c33b8922633 f1af7362f06001882b8bf36001840288298b1fd22a21f362fa840c6213631943 3032228ff3622b840c621c611032ffe00a600840707f264ff66bf66af6690b00 f668f7af0900f5af0900f3af0900f1af0900efaf0900edaf0900ebaf0900e9af -09e0e7af04e0e5af04e0e3af04e0a8fd2b01800021100900e432018c2807018c -1480018cd832018ccc32018c00800000ffff00000ad142210471522101e109d2 +09e0e7af04e0e5af04e0e3af04e0a8fd2b018000211009002433018c2807018c +1480018c1833018c0c33018c00800000ffff00000ad142210471522101e109d2 1222087212220472122207d1226001c8018d1041fa8b05d1126001c80b002900 04745fa00c745fa000093d0018745fa0626113671947014725922821ffe22a62 436300432903136001402444136019407a2001c8108943605c302a2306342907 @@ -3907,7 +3907,7 @@ c228b220b225722612238302f66ef66df66cf66bf66af6690b00f66800000080 22217bd27bd3ac9132221041fc8f047279d17ad77ad310e27221a3961c363226 1042f98f047177d177d2222104719a92222176d276d310e132221041fc8f0472 1fe174d2122274d2122274d100e002ea00e973d807e41fe58697869317e6d62f -019d230df66dd0201043058d017726657035f989f8af63616ad227536ad22033 +019d230df66d10211043058d017726657035f989f8af63616ad227536ad22033 648d48e001e1160f10e168d212220472122267d267d30ce132221041fc8f0472 00e163d212220472122263d163d2222163d164d2222164d12e4164d1ffe700e3 08e2722131111042fb8f08715fd360d1122360d260d1292148977b2111135fd1 @@ -3917,14 +3917,14 @@ c228b220b225722612238302f66ef66df66cf66bf66af6690b00f66800000080 092454dcc36dc51fbda200ee52d2205335d22033978996af01e1007ce003ff7f 8001800000101f7ce00700f836010048846c5fa0f46c5fa0c0785fa00400d8ff 0800d8ff44805fa0000080003f7d1700000020000401150000007f020000df01 -77df27000000100020805fa028815fa08c805fa0000000030c0010a55c33018c +77df27000000100020805fa028815fa08c805fa0000000030c0010a59c33018c abaaaaaa88805fa017b7d138000010a500000002000080200000003004905fa0 001000a40303030374805fa000ff000000825fa080800000002006a405050505 0606060614905fa01ffc0000000006a4070707071c985fa01c9c5fa0004006a4 ffff0000c040018c11111111380000ff000018a500000080180018a54c0e00a0 2c805fa000001800a800018c0040018c000000100050018c00f0ff1f44332211 4040018c000000ff100000ff680a018c000023ac0000ffff0000feff0000fdff -5eb00000a806018c6600018cff7fffff6c00018c2433018c0041018cbd9562d4 +5eb00000a806018c6600018cff7fffff6c00018c6433018c0041018cbd9562d4 62d00b4009000820028fe61f42af09005fd460d00b4005e50820018ba3a002ee 5dd8ab9b59da5dd9b3650b4a83640820018b9aa002eea1900c389038f58fb365 53d454d00b4008e50820018b8fa003ee959553d44dd00b4009000820028f10e1 @@ -3938,7 +3938,7 @@ ffff0000c040018c11111111380000ff000018a500000080180018a54c0e00a0 26d110611c6120315a8f07ee24d110611c612031568b23d110611c612031538b 00ee97aee61f95aee61f93aee61f91aee61f8faee61f8daee61f002800040040 00180010ee0009000000200cd409018c5eb00000a806018c0000210c0028210c -0000220c4c33018c000020ac000021ac0000ffff000022ac0000feff04745fa0 +0000220c8c33018c000020ac000021ac0000ffff000022ac0000feff04745fa0 0000230c0c745fa0801a060018745fa0000023ac0000fdff001023ac002820ac 002821ac004022ac54aee61f52aee61f50aee61f4eaee61f4caee61f4aaee61f 48aee61f46aee61f44aee61f017993600888038f936800a1017e936807e00d48 @@ -3975,8 +3975,8 @@ fc322b51b031028d903ec7a006eee38b41d1181241d1191241d93dd80b489364 e51f2cd00b400aee0188278fe51f2ad483600b400bee20d428d00b40090028d1 10301b8fe51f27d424d00b4009000188148fe51f24d140e0fc30181000e11a10 f3656875f36421d10b41607440e3fc333a52385110320cee028de51f42ae0900 -a36435a0836080004c33018c1c33018c4800018c0833018c6600018c5000018c -f2830070f38300708400018cf032018c7200018c7a00018c0000803f00000040 +a36435a0836080008c33018c5c33018c4800018c4833018c6600018c5000018c +f2830070f38300708400018c3033018c7200018c7a00018c0000803f00000040 010004008005018c010024008605018c8a00018c9800018c01000c006805018c 000080bf01000080785634127605018c0b40090044d800e1122844d10b410900 82627e9110320dee088fe51ff36440d00b4068740788138f00eee51ff2ad0900 @@ -3989,7 +3989,7 @@ f0ade51feeade51fecade51feaade51fe8ade51fe6ade51fe4ade51fe2ade51f 10e3d6af081fd4af081fd2af081f1043248924666c6604677c677036f789f6af 871f8001115409004440018c2c05018c4005018cc8805fa00c06018c00695fa0 80841e003c06018c4000a0ff9040018c7040018c018200001000a0ff6ed22062 -282203890192230209001a166bd2226210711032228df75ee82e018f03eee71f +282203890192230209005a166bd2226210711032228df75ee82e018f03eee71f 67d268d112220171111267d100e332213111321133113411351164d732275fd3 2223136504731223047304e66223a893322710715bd004e813a004e35cd11261 5031d98ff75e5bd112611821d48ff75e56d1126002c8d38fe82eceaf09001043 @@ -4009,7 +4009,7 @@ e61f314420010002000383001f0fff0077772c01a10f09008040018c1000a0ff 1a0013670837d59eec3771230172027361714632ec8f85315123017202736032 f78f617100e16cd21222047212220472c193322204721fe332220472bc933222 04721222047212220472122263d1b4922221047160e2222161d100e0ae9500e6 -ad97ad935fd25630d62f019d230df66de413612101707030f58f027100e15ad2 +ad97ad935fd25630d62f019d230df66d2414612101707030f58f027100e15ad2 12220472122204729993322204721fe332220472909332220472122204721222 0472122204728c933222047248e3322288924ed3222304732223047322230473 2223047322234ad212220472122249d17992222148d600e7769048d475987593 @@ -4017,14 +4017,14 @@ ad97ad935fd25630d62f019d230df66de413612101707030f58f027100e15ad2 87021a022831629eec31112601771043e68f027600e13ad21222047212220472 4d93322204721fe3322204724493322204724d933222047212220472122231d1 3b922221047160e22221d87141922221f65007e11d401fcb40e2fc32061200ee -eb1fee1fed1fffeeec1f00eee91f3ea700e8188d5a884d8f648823d12a922221 -0190230083606c1220d10b4102e4ffa109001ed10b4103e4faa109001bd10b41 -04e4f5a109001ad115922221019023008360401230f800011f3c1f0fff070010 +eb1fee1fed1fffeeec1f00eee91f4ca700e8188d5a884d8f648823d12a922221 +019023008360ac1220d10b4102e4ffa109001ed10b4103e4faa109001bd10b41 +04e4f5a109001ad115922221019023008360801230f800011f3c1f0fff070010 70170f0f00190242ff0fcf07011019fc00780342e003e07f040070a0240070a0 000082a090e80000840070a0ac0070a0c00070a0800070a0000083a06b6f4606 040170a0240170a01c985fa0e405018c10905fa002890c911038138b06d107d2 222183606488178b05d10392222113a00900c8001f7c0900000070a001c20000 -1c9c5fa0e791103803890190230083608c1173d173d2222173d174dc122c74d1 +1c9c5fa0e79110380389019023008360cc1173d173d2222173d174dc122c74d1 126340e2fc32331272dee2644412c265551201e2222120e1122e6fd1122c6fd1 6fd612266fd712276fd170d0122070d1141f131f00e9921f911f6ed2222f6edd ba9700e66dd50b4d00e46ddbb41fb31f921f911f6bd1122f6bd700e667d50b4d @@ -4050,10 +4050,10 @@ c41f61d1131fb21f60d1111fa22f60d760d5b7940b4900e6c41f5fd1131fb21f ffe700e65ad50b4900e45ad1141f5ad1131f5ad1121f5ad1111fa22fffe759d6 59d50b4908e459d159d2222159d25ad332220472e222047200e33222047257d3 3222047232220472322204723222830100e048dd52dc13634edb52da52d953d4 -00e253d563a104e11638018b31a7836050d151d2222151d1ffe2222150d10b41 -090050d110600fc90688018be0a409004dd1126118211f894cd1126118211b8f -0820028d09e191a501e240e2fc321e121f1247d1126004e700e600e5f3640b40 -787478e0fe011821028d01e241d1222141d2226101711222bfa483603fd12cae +00e253d563a104e11638018b51a7836050d151d2222151d1ffe2222150d10b41 +090050d110600fc90688018beea409004dd1126118211f894cd1126118211b8f +0820028d09e1b1a501e240e6fc361e161f1647d1126004e700e600e5f3640b40 +787478e0fe011821028d01e241d1222141d2226101711222cda483603fd12cae 09003fd122213fd2222c32d132d22221b41fb31f921f911f2bd1122f3ad700e6 3ad50b4d00e4922a22d292220472922204729222047292220472922204729222 04729222830a25d1ffe2222187ae09002fd111611d612fd00031f7892ed101e2 @@ -4093,67 +4093,69 @@ b22a622d322be22c53d69226fc701220fc7422240c769226830ab22a5ad6622d 38d79227830a00e300e2c36b636affe9736421a004e5236740d141dc236641dd db2725dd722d26de022e27d0c2201ad7722b29dc122c29d76227922a2224830d 122d622e9220222b222c2227222a2224830d10456c8d017302e1336002c8028d -3921aea401c801c801896ba403882ed01821d08f13672adcd1af13662ad027d1 +3921cea401c801c801898ba403882ed01821d08f13672adcd1af13662ad027d1 2adcccaf1366800000006b430000f04100005743000020410000003f00c00034 00208020d40e018c00005c4200000c4200c0003600008020000000e008000080 040000e0000000c0080000e0002480200c0000e000c80088100000e0140000e0 180000e01c0000e00000f0420000e041000050420000004300002043080000f0 0004802000002443000040430000604300008042c8000080000200300000803f -00005643c80000e00000484300006c43f391122f30e77ad67ad57bde0b4e00e4 -d36a00e9922d047d922df87c922c77d0922077d1922177d2922277d3922377d4 -9224830a76d577dbb11f77d1122f77de77d753660b4e00e4b11f76d1122f76d7 -76d677d50b4e00e476d1122a922d922c66d5922502e266d6222606e165d71227 -65d0222065d21222830ab11f6cd36ed76ed66fd56fd40b4e322f922a922d922c -5ad492245ad592255ad692265ad792275ade922e830a68d0041f68d1131f68d1 -121f68d2211f68dee22f68db68d669d569d40b4bffe760d3341f68d1131f60d4 -421f67d1111fe22f66d662d562d40b4bffe7922a922d922c44d5922544d69226 -44d7922744d0922044d19221830a5ed2241f54d3331f5dd4421f5dd5511f5dd6 -622f5dd75dd55ed40b4b00e656d7741f5cd0031f55d1121f5bd2211f5bd3322f -5bd756d556d40b4b00e65794422f59d652d553d42cd00b4010e7922a922d922c -2ad192212ad292222ad392232ad492242ad59225830a50d150d2222150d1141f -50d1131f50d1121f50d1111f50d1122f50d700e61bd50b4b00e428d6641f4ed1 -131f40d7721f4dd1111fe22f4cd74dd524940b4b00e6922a922d922c13de922e -13d0922013d1922113d2922213d39223830a45d4422f45de45d746d646d547d4 -0b4e090040d5522f45d746d646d547d40b4e090011d68ba0641f000180000900 -302010ff00008020f00b018c0c0000e0100000e0140000e0180000e01c0000e0 -0000a0420000403f0000c842a40d018c0000704300006f430000744300005243 -00005c43000000200000004300004443000080420000020000005c4200006843 -0000f041000048430000003fd40e018c00c40030000090200800000400008d43 -00007a4300c6003000002a430000024300000c439a99193fff00008000009094 -00000002000034430000f0429a99993e0000ff801020308060815fa000000080 -0000d84200008c420000be42000020423333333fffff00ff0000964300008043 -ffffff80000090806666663f840e018c0000d442000082420000c24200003442 -00004d4300009143000039430000874369d7731f69dee21f69d0011f69d1122f -69d76ad56ad40b4b00e66ad2241fe31fe21f69d3311f69d4422f69d763d564d4 -0b4b00e6922a922d922c66d5922566d6922666d7922766de922e66d09220830a -65d1ffe22221a4911038018946ab090062d263d1226001c8018d1041fa8b61d1 -61d2222161d162d22221047161d22221047161d2222104714dd2222104715fd2 -22215fd35fd220e1222301721041fb8f04735dd15dd22221147101e222211362 -4fd1226001c8018d1041fa8b19ab090057d222610171122283603c88028f4288 -f6a00900048ffb5ee82e018bf5a0090050d10b41090050d10b4110e44fd11262 -83639c7356911633028ff951f3a009002831fc5e163e008b1c1ffd5e123e0089 -1d1ffe5e1c3eee1f291f00e148e0fe022822038d182101894fa10900028d3fe1 -3fa1fd521638018befa08360017883603c88028f3ce1d1a809001638018bb8a8 -46881e88028f2d88bea80900028f0f88bfa809000189e1a1836031d10b4101e4 -836208422c3208428c32184209428c321191192283603fc9036108410c311841 -28d33c3128d321211043fc8f0271a5aa090096009500ff7f0000484300003443 -00000c439a99193fff0000800000909400000002000052430000f0429a99993e -0000ff800c0000e0100000e0140000e0180000e01c0000e014805fa0186c5fa0 +00005643c80000e00000484300006c43f291122f30e779d679d57ade0b4e00e4 +79d1141f79d0031f79d1121f79d1111f79d1122f79dbffe771d5de940b4b00e6 +d36a00e9922d047d922df87c922c74d2922274d3922374d4922474d5922574d6 +9226830a73dee11f73d7722f73d774d674d575d00b4000e4e11f6fd1122f73d7 +6fd6636570d20b4200e4e11f70d1122f70d771d671d56cd30b4300e470d1122a +922d922c5ed4922402e25ed5222506e15dd612265dd722275dd01220830ae11f +66d168d768d669d569d45fd20b42122f922a922d922c52d3922352d4922452d5 +922552d6922652d79227830a61dee41f61d1131f61dee21f61d0011f46d1122f +60d661d561d40b4bffe75ad2241f60d1131fe21f4bd3311f3fd4422f5dd65ad5 +5ad40b4bffe7922a922d922c3cd592253cd692263cd792273cde922e3cd09220 +830a55dee41f4ed1131f54d2221f54d3311f54d4422f54d754d555d40b4b00e6 +e41f54dee31f4dd5521f53d6611f53d7722f53d74dd54ed40b4b00e63d90022f +50d64ad54ad41fd10b4110e7922a922d922c23d2922223d3922323d4922423d5 +922523d69226830a47d148d2222148d1141f48d1131f48d1121f48d1111f48d1 +122f48d700e60ed50b4b00e422d7741f45d1131fe21f45d1111f0fd0022f44d7 +44d50a940b4b00e6922a922d922c0cd192210cd280a0090000018000302010ff +00008020f00b018c0000c042000091430000a842000066430000003fd40e018c +0c0000e0100000e0140000e0180000e01c0000e00000403f0000c842286b6e4e +0000a04200007a43a40d018c0000704300006f43000074430000524300005c43 +000000200000004300004443000080420000020000005c42000068430000f041 +0000484300c40030000090200800000400008d4300c6003000002a4300000243 +00000c439a99193fff0000800000909400000002000034430000f0429a99993e +0000ff801020308060815fa0000000800000d84200008c420000be4200002042 +3333333fffff00ff0000964300008043ffffff8000009080922276d3922376d4 +922476d59225830a75d6622f75d776d676d577d477d00b40090071d176d777d6 +77d578d473d20b42122f77d3341f77d4431fe21f76d5511f76d6622f76d777d5 +77d40b4b00e670d7741fe31fe21f75dee11f75d0022f75d770d571d40b4b00e6 +922a922d922c72d1922172d2922259d3922359d4922459d59225830a6ed1ffe2 +2221a4911038018938ab09006bd26cd1226001c8018d1041fa8b6ad16ad22221 +6ad16bd2222104716ad2222104716ad2222104715ad22221047168d2222168d3 +68d220e1222301721041fb8f047366d166d22221147101e22221136258d12260 +01c8018d1041fa8b0bab090060d222610171122283603c88028f428808a10900 +048ffb5ee82e018b07a1090059d10b41090059d10b4110e458d1126283639c73 +56911633028ff95105a109002831fc5e163e008b1c1ffd5e123e00891d1ffe5e +1c3eee1f291f00e148e0fe022822038d1821018961a10900028d3fe151a1fd52 +1638018b01a18360017883603c88028f3ce1c3a809001638018baaa846881e88 +028f2d88b0a80900028f0f88b1a809000189f3a183603ad10b4101e483620842 +2c3208428c32184209428c321191192283603fc9036108410c31184131d33c31 +31d321211043fc8f027197aa090096009500ff7f140000e0180000e01c0000e0 +6666663f0000d442000082420000c24200003442840e018c00004d4300009143 +0000394300008743000052430000484300000c439a99193fff00008000009094 +000000020000f0429a99993e0000ff800c0000e0100000e014805fa0186c5fa0 80841e0000e100ac7856341200e000ac2200008000e1000c0c01002200000065 14e000ac0001dec0046c5fa000e0000c1080018c0c06018c4c06018c0c00d8ff e405018c000000a500a0000083d1222100e183d210220c720ae3302204721022 80d1a0e2202180d280d1206008c8018f1041fa8b7cd1106008c8238d13e26871 212100e221210ae3312121212121212175d276d1206008c8018f1041fa8b72d1 106008c811896871116211601162116211616cd220620fc918401c610b216cd2 -0aaf12226bd1faaf09006ad1f7af090069d00b4000e404a00b1f67d00b4001e4 -0b1f66d10b41090065d10b4110e465d112621aaf291faa9110381f8ff55ee82e +f8ae12226bd1faaf09006ad1f7af090069d00b4000e404a00b1f67d00b4001e4 +0b1f66d10b41090065d10b4110e465d1126208af291faa9110381f8ff55ee82e 1c8961d1a493322160d1ec31291f60d2122260d112611821078d836022625ed1 -1032038f01c8d7a00ae201c8018bf6a009005ad15ad2122209af017887911038 +1032038f01c8d7a00ae201c8018bf6a009005ad15ad21222f7ae017887911038 d78fce71fd53fc5ee8331633268dfe5e54d3ec337d911633268d44e0fe000820 278df65e48d175933221291ffe5e4dd2ec326e9116325e8d44e0fe0008205d8f f65ee82e5d8f48e0fe0008205c8fff5ee82e278df85044d1b9afec313ad15a93 322142d1b3af291f37d155933221ddaf291fe82e0e8d58e033d1fe000221291f -fd52fc5ee8324491163238d19f8dfe5ecdaf0900291fa9ae01e12bd13d922221 +fd52fc5ee8324491163238d19f8dfe5ecdaf0900291f97ae01e12bd13d922221 efaffd520820038df75e31d18faf0c31e82e02892fd18aafec31fa5ee82e0289 2dd184afec31fb5ee82e038d40e02bd17dafec3101e1fe021732038f40e028d1 75af2c31fe00018802891bd16faf090024d16caf090024d169af090023d166af @@ -4164,15 +4166,15 @@ efaffd520820038df75e31d18faf0c31e82e02892fd18aafec31fa5ee82e0289 0007d0ba4d4d0d600300d0ba0400d0ba0500d0ba0600d0ba5bd11261c9712631 12895ad112611821138959d11262a9911032028957d108a0090057d112611821 0a8956d101a0090055d156d212220caf836055d1f9af090054d1f6af090054d1 -08af090029071ce61821028d6d4795ab090050d050dc51d661ab090003c90388 -0189d1a83fe13fe11638028d836018ae836201c8018b01a9090049d1019c230c -090058ee47d222621071203102ee068fe71f45d11261503102ee048de71f0192 -23020900dce90191230109001aea3360038829071ce11d473cd038d136dc2eab -1366f48f182139d053ab09004121017002715630058f20c8d62f019d230df66d -12ecf38f2364f1af336401a093628362212101700271a630f78f0388f78d4630 +08af090029071ce61821028d6d4775ab090050d050dc51d641ab090003c90388 +0189b1a83fe13fe11638028d836006ae836201c8018be1a8090049d1019c230c +090018ee47d222621071203102ee068fe71f45d11261503102ee048de71f0192 +230209009ce9019123010900dae93360038829071ce11d473cd038d136dc0eab +1366f48f182139d033ab09004121017002715630058f20c8d62f019d230df66d +d2ebf38f2364f1af336401a093628362212101700271a630f78f0388f78d4630 2c8f66302c8b312101705888f38f027127d12892222127d227d1292127d33b21 13662367e07726d518e352271043fc8f047724d30ce77223fc7310e7722322d7 -08e3722101771043fb8f047183061ed700e101e60195230508e3d4dec8af5362 +08e3722101771043fb8f047183061ed700e101e60195230508e394dec8af5362 c6af7362280680000c80018c0880018c0480018c030cd0ba0080018c040cd0ba 010cd0ba4840018c020cd0ba9d5a0d60000020000000484300006c430000803f 000040001000a0ff1400a0ff000070431c815fa0e040018ce0ffff03000000e0 diff --git a/tools/threads/live_prog.c b/tools/threads/live_prog.c index df44dcbd..f1899275 100644 --- a/tools/threads/live_prog.c +++ b/tools/threads/live_prog.c @@ -738,8 +738,9 @@ static u32 keep_done, keep_age; #ifdef LIVE_HEAVY /* With -DLIVE_HEAVY: a frame of many vertices, as a game's are, for the * parse's speed (tools/pvr/ta_parse_bench.sh): Gouraud strips, tiny, in - * the bottom left corner. A whole number's float, made without the FPU - * the program has no code for. */ + * the bottom left corner; and a shadow volume reaching far off the screen + * (below). A whole number's float, made without the FPU the program has + * no code for. */ static u32 int_float(u32 n) { u32 e = 0; @@ -894,9 +895,20 @@ static void ta_scene(void) #endif #ifdef LIVE_HEAVY ta_heavy(0, TSP_PLAIN, 0xFF102030, 48, 256); + /* a white polygon that takes shadows, half under the volume far off the + * screen (after the list) */ + ta_quad(TA_SHADOW, TSP_PLAIN, 0, 0xFFFFFFFF, F(0.5f), F(230.0f), F(84.0f), F(290.0f), F(96.0f)); #endif ta_send(0, 0, 0, 0, 0, 0, 0, 0); /* end of the opaque list */ +#ifdef LIVE_HEAVY + /* A volume reaching a billion pixels off the screen to the right, as a + * game's shadow near the eye does: its triangles are cut to a square + * about the screen before they are drawn (core/rend/modvol_clip.h). The + * first, so that the triangles of the volumes after it are elsewhere in + * the list of those cut than in the volumes' own. */ + ta_volume_part(0, F(250.0f), F(80.0f), F(1.0e9f), F(100.0f), F(0.75f)); +#endif #ifdef LIVE_CULL ta_volume_culled(2, F(4.0f), F(146.0f), F(44.0f), F(174.0f), F(0.75f)); ta_volume_culled(3, F(4.0f), F(174.0f), F(44.0f), F(200.0f), F(0.75f)); diff --git a/tools/vulkan/vk_parse_arena.sh b/tools/vulkan/vk_parse_arena.sh index 151dda20..be0859d8 100644 --- a/tools/vulkan/vk_parse_arena.sh +++ b/tools/vulkan/vk_parse_arena.sh @@ -8,7 +8,8 @@ # copied; told that memory is slow to read (VKH_UNCACHED) the core copies # them, as ever - and the pictures of the two are the same, byte for byte: # the test discs' plain one, heavy one (more strips than the copy's first -# guess holds: tools/threads/live_disc.py --heavy) and DMA one, with each +# guess holds: tools/threads/live_disc.py --heavy), DMA one and the one with +# modifier volumes (--cull, whose triangles are drawn from there too), with each # renderer, threaded rendering on and off, and with a temporal upscaler # on the frontend (its motion vectors drawn from the same geometry). # @@ -30,7 +31,7 @@ ${CC:-cc} -O1 -g -Wall -I./core/libretro-common/include -I./core/deps/khronos \ core/rend/hdr/hdr_output.c core/rend/hdr/hdr_transform.c core/rend/hdr/hdr_aces2.c \ core/rend/hdr/hdr_libretro.c core/rend/hdr/hdr_display.c -ldl -lm -lpthread # (a directory a disc: their tracks' names are the same) -for disc in plain heavy dma; do +for disc in plain heavy dma cull; do mkdir -p "$WORK/$disc" case $disc in plain) python3 tools/threads/live_disc.py "$WORK/$disc/test.gdi" ;; @@ -56,7 +57,7 @@ run() { fi } -for disc in plain heavy dma; do +for disc in plain heavy dma cull; do for r in per-triangle per-pixel per-triangle-upscaler per-pixel-upscaler; do unset VKH_UPSCALER case $r in