[vulkan] Implement thread-safe rendering for the world model

Sub-models and instance models need an instance data buffer, but this
gets the basics working (and the proof of concept). Using arrays like
this actually simplified a lot of the code, and will make it easy to get
transparency without turbulence (just another queue).
This commit is contained in:
Bill Currie 2022-05-23 01:28:43 +09:00
parent c6f520b743
commit 3a2560e4c1
3 changed files with 269 additions and 405 deletions

View file

@ -39,30 +39,81 @@
#include "QF/simd/types.h"
typedef struct bsp_face_s {
uint32_t first_index;
uint32_t index_count;
uint32_t tex_id;
uint32_t flags;
} bsp_face_t;
typedef struct bsp_packet_s {
int first_index;
int index_count;
int transform_id;
int color_id;
} bsp_packet_t;
typedef struct bsp_packetset_s
DARRAY_TYPE (bsp_packet_t) bsp_packetset_t;
typedef struct bsp_indexset_s
DARRAY_TYPE (uint32_t) bsp_indexset_t;
typedef struct bsp_tex_s {
VkDescriptorSet descriptors;
bsp_packetset_t packets;
bsp_indexset_t indices;
} bsp_tex_t;
typedef struct vulktex_s {
struct texture_s *texture;
struct instsurf_s *tex_chain; // for gl_texsort drawing
struct instsurf_s **tex_chain_tail;
struct elechain_s *elechain;
struct elechain_s **elechain_tail;
struct qfv_tex_s *tex;
VkDescriptorSet descriptor;
int tex_id;
} vulktex_t;
typedef struct regtexset_s
DARRAY_TYPE (vulktex_t *) regtexset_t;
typedef struct bsp_texset_s
DARRAY_TYPE (bsp_tex_t) bsp_texset_t;
typedef struct bsp_draw_s {
uint32_t tex_id;
uint32_t index_count;
uint32_t instance_count;
uint32_t first_index;
uint32_t first_instance;
} bsp_draw_t;
typedef struct bsp_drawset_s
DARRAY_TYPE (bsp_draw_t) bsp_drawset_t;
typedef struct instface_s {
uint32_t inst_id;
uint32_t face;
} instface_t;
typedef struct bsp_instfaceset_s
DARRAY_TYPE (instface_t) bsp_instfaceset_t;
typedef struct bsp_pass_s {
uint32_t *indices; // points into index buffer
uint32_t index_count; // number of indices written to buffer
int vis_frame;
int *face_frames;
int *leaf_frames;
int *node_frames;
bsp_instfaceset_t *face_queue;
regtexset_t *textures;
int num_queues;
bsp_drawset_t *draw_queues;
} bsp_pass_t;
typedef struct bspvert_s {
quat_t vertex;
quat_t tlst;
} bspvert_t;
typedef struct elechain_s {
struct elechain_s *_next;
struct elechain_s *next;
uint32_t first_index;
uint32_t index_count;
vec_t *transform;
float *color;
} elechain_t;
typedef enum {
qfv_bsp_texture,
qfv_bsp_glowmap,
@ -90,33 +141,13 @@ typedef struct bspframe_s {
typedef struct bspframeset_s
DARRAY_TYPE (bspframe_t) bspframeset_t;
typedef struct texchainset_s
DARRAY_TYPE (vulktex_t *) texchainset_t;
typedef struct bspctx_s {
struct entity_s *entity;
vec_t *transform;
float *color;
uint32_t inst_id;
instsurf_t *waterchain;
instsurf_t **waterchain_tail;
instsurf_t *sky_chain;
instsurf_t **sky_chain_tail;
texchainset_t texture_chains;
// for world and non-instance models
instsurf_t *static_instsurfs;
instsurf_t **static_instsurfs_tail;
instsurf_t *free_static_instsurfs;
// for instance models
elechain_t *elechains;
elechain_t **elechains_tail;
elechain_t *free_elechains;
instsurf_t *instsurfs;
instsurf_t **instsurfs_tail;
instsurf_t *free_instsurfs;
regtexset_t registered_textures;
struct qfv_tex_s *default_skysheet;
struct qfv_tex_s *skysheet_tex;
@ -131,7 +162,10 @@ typedef struct bspctx_s {
struct scrap_s *light_scrap;
struct qfv_stagebuf_s *light_stage;
struct bsppoly_s *polys;
bsp_face_t *faces;
uint32_t *poly_indices;
bsp_pass_t main_pass; // camera view depth, gbuffer, etc
VkSampler sampler;
VkPipelineLayout layout;

View file

@ -307,7 +307,6 @@ Vulkan_Mod_ProcessTexture (model_t *mod, texture_t *tx, vulkan_ctx_t *ctx)
}
vulktex_t *tex = tx->render;
tex->texture = tx;
tex->tex = (qfv_tex_t *) (tex + 1);
VkExtent3D extent = { tx->width, tx->height, 1 };

View file

@ -105,67 +105,6 @@ static vulktex_t vulkan_notexture = { };
#define ALLOC_CHUNK 64
typedef struct bsppoly_s {
uint32_t count;
uint32_t indices[1];
} bsppoly_t;
#define CHAIN_SURF_F2B(surf,chain) \
({ \
instsurf_t *inst = (surf)->instsurf; \
if (__builtin_expect(!inst, 1)) \
inst = get_instsurf (bctx); \
inst->surface = (surf); \
*(chain##_tail) = inst; \
(chain##_tail) = &inst->tex_chain; \
*(chain##_tail) = 0; \
inst; \
})
#define CHAIN_SURF_B2F(surf,chain) \
({ \
instsurf_t *inst = (surf)->instsurf; \
if (__builtin_expect(!inst, 1)) \
inst = get_instsurf (bctx); \
inst->surface = (surf); \
inst->tex_chain = (chain); \
(chain) = inst; \
inst; \
})
#define GET_RELEASE(type,name) \
static inline type * \
get_##name (bspctx_t *bctx) \
{ \
type *ele; \
if (!bctx->free_##name##s) { \
int i; \
bctx->free_##name##s = calloc (ALLOC_CHUNK, sizeof (type)); \
for (i = 0; i < ALLOC_CHUNK - 1; i++) \
bctx->free_##name##s[i]._next = &bctx->free_##name##s[i + 1]; \
} \
ele = bctx->free_##name##s; \
bctx->free_##name##s = ele->_next; \
ele->_next = 0; \
*bctx->name##s_tail = ele; \
bctx->name##s_tail = &ele->_next; \
return ele; \
} \
static inline void \
release_##name##s (bspctx_t *bctx) \
{ \
if (bctx->name##s) { \
*bctx->name##s_tail = bctx->free_##name##s; \
bctx->free_##name##s = bctx->name##s; \
bctx->name##s = 0; \
bctx->name##s_tail = &bctx->name##s; \
} \
}
GET_RELEASE (elechain_t, elechain)
GET_RELEASE (instsurf_t, static_instsurf)
GET_RELEASE (instsurf_t, instsurf)
static void
add_texture (texture_t *tx, vulkan_ctx_t *ctx)
{
@ -173,83 +112,25 @@ add_texture (texture_t *tx, vulkan_ctx_t *ctx)
vulktex_t *tex = tx->render;
if (tex->tex) {
DARRAY_APPEND (&bctx->texture_chains, tex);
tex->tex_id = bctx->registered_textures.size;
DARRAY_APPEND (&bctx->registered_textures, tex);
tex->descriptor = Vulkan_CreateTextureDescriptor (ctx, tex->tex,
bctx->sampler);
}
tex->tex_chain = 0;
tex->tex_chain_tail = &tex->tex_chain;
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
static void
init_surface_chains (mod_brush_t *brush, vulkan_ctx_t *ctx)
{
bspctx_t *bctx = ctx->bsp_context;
release_static_instsurfs (bctx);
release_instsurfs (bctx);
for (unsigned i = 0; i < brush->nummodelsurfaces; i++) {
brush->surfaces[i].instsurf = get_static_instsurf (bctx);
brush->surfaces[i].instsurf->surface = &brush->surfaces[i];
}
}
static inline void
clear_tex_chain (vulktex_t *tex)
{
tex->tex_chain = 0;
tex->tex_chain_tail = &tex->tex_chain;
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
static void
clear_texture_chains (bspctx_t *bctx)
{
for (size_t i = 0; i < bctx->texture_chains.size; i++) {
if (!bctx->texture_chains.a[i])
continue;
clear_tex_chain (bctx->texture_chains.a[i]);
}
clear_tex_chain (r_notexture_mip->render);
release_elechains (bctx);
release_instsurfs (bctx);
}
void
Vulkan_ClearElements (vulkan_ctx_t *ctx)
{
bspctx_t *bctx = ctx->bsp_context;
release_elechains (bctx);
// bspctx_t *bctx = ctx->bsp_context;
}
static inline void
chain_surface (msurface_t *surf, vulkan_ctx_t *ctx)
chain_surface (const bsp_face_t *face, bsp_pass_t *pass, bspctx_t *bctx)
{
bspctx_t *bctx = ctx->bsp_context;
instsurf_t *is;
if (surf->flags & SURF_DRAWSKY) {
is = CHAIN_SURF_F2B (surf, bctx->sky_chain);
} else if ((surf->flags & SURF_DRAWTURB)
|| (bctx->color && bctx->color[3] < 1.0)) {
is = CHAIN_SURF_B2F (surf, bctx->waterchain);
} else {
texture_t *tx;
vulktex_t *tex;
if (!surf->texinfo->texture->anim_total)
tx = surf->texinfo->texture;
else
tx = R_TextureAnimation (bctx->entity, surf);
tex = tx->render;
is = CHAIN_SURF_F2B (surf, tex->tex_chain);
}
is->transform = bctx->transform;
is->color = bctx->color;
DARRAY_APPEND (&pass->face_queue[face->tex_id],
((instface_t) { bctx->inst_id, face - bctx->faces }));
}
static void
@ -269,7 +150,16 @@ static void
clear_textures (vulkan_ctx_t *ctx)
{
bspctx_t *bctx = ctx->bsp_context;
bctx->texture_chains.size = 0;
if (bctx->main_pass.face_queue) {
for (size_t i = 0; i < bctx->registered_textures.size; i++) {
DARRAY_CLEAR (&bctx->main_pass.face_queue[i]);
}
free (bctx->main_pass.face_queue);
bctx->main_pass.face_queue = 0;
}
bctx->registered_textures.size = 0;
}
void
@ -280,7 +170,6 @@ Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx)
mod_brush_t *brush = &r_refdef.worldmodel->brush;
clear_textures (ctx);
init_surface_chains (brush, ctx);
add_texture (r_notexture_mip, ctx);
register_textures (brush, ctx);
for (i = 0; i < num_models; i++) {
@ -297,60 +186,67 @@ Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx)
brush->numsubmodels = 1; // no support for submodels in non-world model
register_textures (brush, ctx);
}
bspctx_t *bctx = ctx->bsp_context;
int num_tex = bctx->registered_textures.size;
bctx->main_pass.face_queue = malloc (num_tex * sizeof (bsp_instfaceset_t));
for (int i = 0; i < num_tex; i++) {
bctx->main_pass.face_queue[i]
= (bsp_instfaceset_t) DARRAY_STATIC_INIT (128);
}
}
static elechain_t *
add_elechain (vulktex_t *tex, bspctx_t *bctx)
{
elechain_t *ec;
typedef struct {
msurface_t *face;
model_t *model;
int model_face_base;
} faceref_t;
ec = get_elechain (bctx);
ec->first_index = 0;
ec->index_count = 0;
ec->transform = 0;
ec->color = 0;
*tex->elechain_tail = ec;
tex->elechain_tail = &ec->next;
return ec;
}
typedef struct DARRAY_TYPE (faceref_t) facerefset_t;
static void
count_verts_inds (model_t **models, msurface_t *surf,
uint32_t *verts, uint32_t *inds)
count_verts_inds (faceref_t *faceref, uint32_t *verts, uint32_t *inds)
{
msurface_t *surf = faceref->face;
*verts = surf->numedges;
*inds = surf->numedges + 1;
}
static bsppoly_t *
build_surf_displist (model_t **models, msurface_t *surf, int base,
bspvert_t **vert_list)
typedef struct {
bsp_face_t *faces;
uint32_t *indices;
bspvert_t *vertices;
uint32_t index_base;
uint32_t vertex_base;
int tex_id;
} buildctx_t;
static void
build_surf_displist (faceref_t *faceref, buildctx_t *build)
{
mod_brush_t *brush;
if (surf->model_index < 0) {
// instance model
brush = &models[~surf->model_index]->brush;
} else {
// main or sub model
brush = &r_refdef.worldmodel->brush;
msurface_t *surf = faceref->face;
mod_brush_t *brush = &faceref->model->brush;;
int facenum = surf - brush->surfaces;
bsp_face_t *face = &build->faces[facenum + faceref->model_face_base];
// create a triangle fan
int numverts = surf->numedges;
face->first_index = build->index_base;
face->index_count = numverts + 1; // +1 for primitive restart
face->tex_id = build->tex_id;
face->flags = surf->flags;
build->index_base += face->index_count;
for (int i = 0; i < numverts; i++) {
build->indices[face->first_index + i] = build->vertex_base + i;
}
build->indices[face->first_index + numverts] = -1; // primitive restart
bspvert_t *verts = build->vertices + build->vertex_base;
build->vertex_base += numverts;
mtexinfo_t *texinfo = surf->texinfo;
mvertex_t *vertices = brush->vertexes;
medge_t *edges = brush->edges;
int *surfedges = brush->surfedges;
// surf->polys is set to the next slot before the call
bsppoly_t *poly = (bsppoly_t *) surf->polys;
// create a triangle fan
int numverts = surf->numedges;
poly->count = numverts + 1; // +1 for primitive restart
for (int i = 0; i < numverts; i++) {
poly->indices[i] = base + i;
}
poly->indices[numverts] = -1; // primitive restart
surf->polys = (glpoly_t *) poly;
bspvert_t *verts = *vert_list;
mtexinfo_t *texinfo = surf->texinfo;
for (int i = 0; i < numverts; i++) {
vec_t *vec;
int index = surfedges[surf->firstedge + i];
@ -388,8 +284,6 @@ build_surf_displist (model_t **models, msurface_t *surf, int base,
verts[i].tlst[3] = 0;
}
}
*vert_list += numverts;
return (bsppoly_t *) &poly->indices[poly->count];
}
void
@ -403,9 +297,16 @@ Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx)
return;
}
facerefset_t *face_sets = alloca (bctx->registered_textures.size
* sizeof (facerefset_t));
for (size_t i = 0; i < bctx->registered_textures.size; i++) {
face_sets[i] = (facerefset_t) DARRAY_STATIC_INIT (1024);
}
// run through all surfaces, chaining them to their textures, thus
// effectively sorting the surfaces by texture (without worrying about
// surface order on the same texture chain).
int face_base = 0;
for (int i = 0; i < num_models; i++) {
model_t *m = models[i];
// sub-models are done as part of the main model
@ -421,30 +322,32 @@ Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx)
if (dm == brush->submodels + brush->numsubmodels) {
// limit the surfaces
// probably never hit
Sys_Printf ("R_BuildDisplayLists: too many surfaces\n");
Sys_Printf ("Vulkan_BuildDisplayLists: too many faces\n");
brush->numsurfaces = j;
break;
}
}
msurface_t *surf = brush->surfaces + j;
surf->model_index = dm - brush->submodels;
if (!surf->model_index && m != r_refdef.worldmodel) {
surf->model_index = -1 - i; // instanced model
}
// append surf to the texture chain
vulktex_t *tex = surf->texinfo->texture->render;
CHAIN_SURF_F2B (surf, tex->tex_chain);
DARRAY_APPEND (&face_sets[tex->tex_id],
((faceref_t) { surf, m, face_base }));
}
face_base += brush->numsurfaces;
}
// All vertices from all brush models go into one giant vbo.
uint32_t vertex_count = 0;
uint32_t index_count = 0;
uint32_t poly_count = 0;
for (size_t i = 0; i < bctx->texture_chains.size; i++) {
vulktex_t *tex = bctx->texture_chains.a[i];
for (instsurf_t *is = tex->tex_chain; is; is = is->tex_chain) {
// This is not optimal as counted vertices are not shared between faces,
// however this greatly simplifies display list creation as no care needs
// to be taken when it comes to UVs, and every vertex needs a unique light
// map UV anyway (when lightmaps are used).
for (size_t i = 0; i < bctx->registered_textures.size; i++) {
for (size_t j = 0; j < face_sets[i].size; j++) {
faceref_t *faceref = &face_sets[i].a[j];
uint32_t verts, inds;
count_verts_inds (models, is->surface, &verts, &inds);
count_verts_inds (faceref, &verts, &inds);
vertex_count += verts;
index_count += inds;
poly_count++;
@ -464,44 +367,39 @@ Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx)
qfv_packet_t *packet = QFV_PacketAcquire (stage);
bspvert_t *vertices = QFV_PacketExtend (packet, vertex_buffer_size);
// holds all the polygon definitions: vertex indices + poly_count
// primitive restart markers + poly_count index counts. The primitive
// restart markers are included in index_count, so poly_count below is
// for the per-polygon index count.
// primitive restart markers. The primitive restart markers are included
// in index_count.
// so each polygon within the list:
// count includes the end of primitive marker
// index count-1 indices
// index
// ...
// "end of primitive" (~0u)
free (bctx->polys);
bctx->polys = malloc ((index_count + poly_count) * sizeof (uint32_t));
free (bctx->faces);
free (bctx->poly_indices);
bctx->faces = malloc (face_base * sizeof (bsp_face_t));
bctx->poly_indices = malloc (index_count * sizeof (uint32_t));
// All usable surfaces have been chained to the (base) texture they use.
// Run through the textures, using their chains to build display lists.
// For animated textures, if a surface is on one texture of the group, it
// will effectively be on all (just one at a time).
int count = 0;
int vertex_index_base = 0;
bsppoly_t *poly = bctx->polys;
for (size_t i = 0; i < bctx->texture_chains.size; i++) {
vulktex_t *tex = bctx->texture_chains.a[i];
for (instsurf_t *is = tex->tex_chain; is; is = is->tex_chain) {
msurface_t *surf = is->surface;
surf->polys = (glpoly_t *) poly;
poly = build_surf_displist (models, surf, vertex_index_base,
&vertices);
vertex_index_base += surf->numedges;
count++;
buildctx_t build = {
.faces = bctx->faces,
.indices = bctx->poly_indices,
.vertices = vertices,
.index_base = 0,
.vertex_base = 0,
};
for (size_t i = 0; i < bctx->registered_textures.size; i++) {
build.tex_id = i;
for (size_t j = 0; j < face_sets[i].size; j++) {
faceref_t *faceref = &face_sets[i].a[j];
build_surf_displist (faceref, &build);
}
}
clear_texture_chains (bctx);
Sys_MaskPrintf (SYS_vulkan,
"R_BuildDisplayLists: verts:%u, inds:%u, "
"polys:%u (%d) %zd\n",
vertex_count, index_count, poly_count, count,
((size_t) poly - (size_t) bctx->polys) / sizeof(uint32_t));
"R_BuildDisplayLists: verts:%u, inds:%u, polys:%u\n",
vertex_count, index_count, poly_count);
if (index_buffer_size > bctx->index_buffer_size) {
if (bctx->index_buffer) {
dfunc->vkUnmapMemory (device->dev, bctx->index_memory);
@ -571,6 +469,10 @@ Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx)
0, 0, 0, 1, &bb.barrier, 0, 0);
QFV_PacketSubmit (packet);
QFV_DestroyStagingBuffer (stage);
for (size_t i = 0; i < bctx->registered_textures.size; i++) {
DARRAY_CLEAR (&face_sets[i]);
}
}
static void
@ -629,7 +531,7 @@ R_DrawBrushModel (entity_t *e, vulkan_ctx_t *ctx)
// enqueue the polygon
if (((surf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON))
|| (!(surf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) {
chain_surface (surf, ctx);
//chain_surface (surf, ctx);
}
}
}
@ -656,8 +558,9 @@ get_side (mnode_t *node)
static inline void
visit_node (mod_brush_t *brush, mnode_t *node, int side, vulkan_ctx_t *ctx)
{
bspctx_t *bctx = ctx->bsp_context;
bsp_pass_t *pass = &ctx->bsp_context->main_pass;
int c;
msurface_t *surf;
// sneaky hack for side = side ? SURF_PLANEBACK : 0;
// seems to be microscopically faster even on modern hardware
@ -665,19 +568,19 @@ visit_node (mod_brush_t *brush, mnode_t *node, int side, vulkan_ctx_t *ctx)
// chain any visible surfaces on the node that face the camera.
// not all nodes have any surfaces to draw (purely a split plane)
if ((c = node->numsurfaces)) {
int surf_id = node->firstsurface;
surf = brush->surfaces + surf_id;
for (; c; c--, surf++, surf_id++) {
if (r_face_visframes[surf_id] != r_visframecount)
const bsp_face_t *face = bctx->faces + node->firstsurface;
const int *frame = pass->face_frames + node->firstsurface;
for (; c; c--, face++, frame++) {
if (*frame != r_visframecount)
continue;
// side is either 0 or SURF_PLANEBACK
// if side and the surface facing differ, then the camera is
// on backside of the surface
if (side ^ (surf->flags & SURF_PLANEBACK))
if (side ^ (face->flags & SURF_PLANEBACK))
continue; // wrong side
chain_surface (surf, ctx);
chain_surface (face, pass, bctx);
}
}
}
@ -799,32 +702,6 @@ push_fragconst (bsp_push_constants_t *constants, VkPipelineLayout layout,
QFV_PushConstants (device, cmd, layout, 3, push_constants);
}
static void
draw_elechain (elechain_t *ec, VkPipelineLayout layout, qfv_device_t *device,
VkCommandBuffer cmd)
{
qfv_devfuncs_t *dfunc = device->funcs;
if (ec->transform) {
push_transform (ec->transform, layout, device, cmd);
} else {
//FIXME should cache current transform
push_transform (identity, layout, device, cmd);
}
if (ec->index_count) {
dfunc->vkCmdDrawIndexed (cmd, ec->index_count, 1, ec->first_index,
0, 0);
}
}
static void
reset_elechain (elechain_t *ec)
{
ec->first_index = 0;
ec->index_count = 0;
}
static void
bsp_begin_subpass (QFV_BspSubpass subpass, VkPipeline pipeline,
VkPipelineLayout layout, qfv_renderframe_t *rFrame)
@ -979,40 +856,32 @@ sky_end (vulkan_ctx_t *ctx)
bsp_end_subpass (bframe->cmdSet.a[QFV_bspSky], ctx);
}
static inline void
add_surf_elements (vulktex_t *tex, instsurf_t *is, elechain_t **ec,
bspctx_t *bctx, bspframe_t *bframe)
static void
clear_queues (bspctx_t *bctx, bsp_pass_t *pass)
{
bsppoly_t *poly = (bsppoly_t *) is->surface->polys;
if (!tex->elechain) {
(*ec) = add_elechain (tex, bctx);
(*ec)->transform = is->transform;
(*ec)->color = is->color;
(*ec)->first_index = bframe->index_count;
for (size_t i = 0; i < bctx->registered_textures.size; i++) {
DARRAY_RESIZE (&pass->face_queue[i], 0);
}
if (is->transform != (*ec)->transform || is->color != (*ec)->color) {
(*ec) = add_elechain (tex, bctx);
(*ec)->transform = is->transform;
(*ec)->color = is->color;
(*ec)->first_index = bframe->index_count;
for (int j = 0; j < pass->num_queues; j++) {
DARRAY_RESIZE (&pass->draw_queues[j], 0);
}
memcpy (bframe->index_data + bframe->index_count,
poly->indices, poly->count * sizeof (poly->indices[0]));
(*ec)->index_count += poly->count;
bframe->index_count += poly->count;
pass->index_count = 0;
}
static void
build_tex_elechain (vulktex_t *tex, bspctx_t *bctx, bspframe_t *bframe)
draw_queue (bsp_pass_t *pass, int queue, VkPipelineLayout layout,
qfv_device_t *device, VkCommandBuffer cmd)
{
instsurf_t *is;
elechain_t *ec = 0;
qfv_devfuncs_t *dfunc = device->funcs;
for (is = tex->tex_chain; is; is = is->tex_chain) {
// emit the polygon indices for the surface to the texture's
// element chain
add_surf_elements (tex, is, &ec, bctx, bframe);
for (size_t i = 0; i < pass->draw_queues[queue].size; i++) {
__auto_type d = pass->draw_queues[queue].a[i];
if (pass->textures) {
vulktex_t *tex = pass->textures->a[d.tex_id];
bind_texture (tex, 1, layout, dfunc, cmd);
}
dfunc->vkCmdDrawIndexed (cmd, d.index_count, d.instance_count,
d.first_index, 0, d.first_instance);
}
}
@ -1021,13 +890,17 @@ Vulkan_DrawWorld (qfv_renderframe_t *rFrame)
{
vulkan_ctx_t *ctx = rFrame->vulkan_ctx;
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
//qfv_devfuncs_t *dfunc = device->funcs;
bspctx_t *bctx = ctx->bsp_context;
bspframe_t *bframe = &bctx->frames.a[ctx->curFrame];
entity_t worldent;
mod_brush_t *brush;
clear_texture_chains (bctx); // do this first for water and skys
bctx->main_pass.face_frames = r_face_visframes;
bctx->main_pass.leaf_frames = r_leaf_visframes;
bctx->main_pass.node_frames = r_node_visframes;
clear_queues (bctx, &bctx->main_pass); // do this first for water and skys
bframe->index_count = 0;
memset (&worldent, 0, sizeof (worldent));
@ -1058,27 +931,42 @@ Vulkan_DrawWorld (qfv_renderframe_t *rFrame)
bsp_push_constants_t frag_constants = { .time = vr_data.realtime };
push_fragconst (&frag_constants, bctx->layout, device,
bframe->cmdSet.a[QFV_bspGBuffer]);
for (size_t i = 0; i < bctx->texture_chains.size; i++) {
vulktex_t *tex;
elechain_t *ec = 0;
tex = bctx->texture_chains.a[i];
build_tex_elechain (tex, bctx, bframe);
bind_texture (tex, 1, bctx->layout, dfunc,
bframe->cmdSet.a[QFV_bspGBuffer]);
for (ec = tex->elechain; ec; ec = ec->next) {
draw_elechain (ec, bctx->layout, device,
bframe->cmdSet.a[QFV_bspDepth]);
draw_elechain (ec, bctx->layout, device,
bframe->cmdSet.a[QFV_bspGBuffer]);
reset_elechain (ec);
__auto_type pass = &bctx->main_pass;
pass->indices = bframe->index_data;
for (size_t i = 0; i < bctx->registered_textures.size; i++) {
__auto_type queue = &pass->face_queue[i];
if (!queue->size) {
continue;
}
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
//FIXME implement draw_queue selection correctly (per face)
int dq = 0;
if (bctx->faces[queue->a[0].face].flags & SURF_DRAWSKY) {
dq = 1;
}
if (bctx->faces[queue->a[0].face].flags & SURF_DRAWTURB) {
dq = 2;
}
bsp_draw_t draw = {
.tex_id = i,
.instance_count = 1,
.first_index = pass->index_count,
};
for (size_t j = 0; j < queue->size; j++) {
__auto_type is = queue->a[j];
__auto_type f = bctx->faces[is.face];
memcpy (pass->indices + pass->index_count,
bctx->poly_indices + f.first_index,
f.index_count * sizeof (uint32_t));
draw.index_count += f.index_count;
pass->index_count += f.index_count;
}
DARRAY_APPEND (&pass->draw_queues[dq], draw);
}
VkPipelineLayout layout = bctx->layout;
pass->textures = 0;
draw_queue (pass, 0, layout, device, bframe->cmdSet.a[QFV_bspDepth]);
pass->textures = &bctx->registered_textures;
draw_queue (pass, 0, layout, device, bframe->cmdSet.a[QFV_bspGBuffer]);
bsp_end (ctx);
}
@ -1112,14 +1000,11 @@ Vulkan_DrawWaterSurfaces (qfv_renderframe_t *rFrame)
{
vulkan_ctx_t *ctx = rFrame->vulkan_ctx;
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
//qfv_devfuncs_t *dfunc = device->funcs;
bspctx_t *bctx = ctx->bsp_context;
bspframe_t *bframe = &bctx->frames.a[ctx->curFrame];
instsurf_t *is;
vulktex_t *tex = 0;
elechain_t *ec = 0;
if (!bctx->waterchain)
if (!bctx->main_pass.draw_queues[2].size)
return;
turb_begin (rFrame);
@ -1131,41 +1016,13 @@ Vulkan_DrawWaterSurfaces (qfv_renderframe_t *rFrame)
};
push_fragconst (&frag_constants, bctx->layout, device,
bframe->cmdSet.a[QFV_bspTurb]);
for (is = bctx->waterchain; is; is = is->tex_chain) {
msurface_t *surf = is->surface;
if (tex != surf->texinfo->texture->render) {
if (tex) {
bind_texture (tex, 1, bctx->layout, dfunc,
bframe->cmdSet.a[QFV_bspTurb]);
for (ec = tex->elechain; ec; ec = ec->next) {
draw_elechain (ec, bctx->layout, device,
bframe->cmdSet.a[QFV_bspTurb]);
reset_elechain (ec);
}
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
tex = surf->texinfo->texture->render;
}
// emit the polygon indices for the surface to the texture's
// element chain
add_surf_elements (tex, is, &ec, bctx, bframe);
}
if (tex) {
bind_texture (tex, 1, bctx->layout, dfunc,
bframe->cmdSet.a[QFV_bspTurb]);
for (ec = tex->elechain; ec; ec = ec->next) {
draw_elechain (ec, bctx->layout, device,
bframe->cmdSet.a[QFV_bspTurb]);
reset_elechain (ec);
}
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
turb_end (ctx);
bctx->waterchain = 0;
bctx->waterchain_tail = &bctx->waterchain;
VkPipelineLayout layout = bctx->layout;
__auto_type pass = &bctx->main_pass;
pass->textures = &bctx->registered_textures;
draw_queue (pass, 2, layout, device, bframe->cmdSet.a[QFV_bspTurb]);
turb_end (ctx);
}
void
@ -1176,11 +1033,8 @@ Vulkan_DrawSky (qfv_renderframe_t *rFrame)
qfv_devfuncs_t *dfunc = device->funcs;
bspctx_t *bctx = ctx->bsp_context;
bspframe_t *bframe = &bctx->frames.a[ctx->curFrame];
instsurf_t *is;
vulktex_t *tex = 0;
elechain_t *ec = 0;
if (!bctx->sky_chain)
if (!bctx->main_pass.draw_queues[1].size)
return;
sky_begin (rFrame);
@ -1192,41 +1046,13 @@ Vulkan_DrawSky (qfv_renderframe_t *rFrame)
bsp_push_constants_t frag_constants = { .time = vr_data.realtime };
push_fragconst (&frag_constants, bctx->layout, device,
bframe->cmdSet.a[QFV_bspSky]);
for (is = bctx->sky_chain; is; is = is->tex_chain) {
msurface_t *surf = is->surface;
if (tex != surf->texinfo->texture->render) {
if (tex) {
bind_texture (tex, 1, bctx->layout, dfunc,
bframe->cmdSet.a[QFV_bspSky]);
for (ec = tex->elechain; ec; ec = ec->next) {
draw_elechain (ec, bctx->layout, device,
bframe->cmdSet.a[QFV_bspSky]);
reset_elechain (ec);
}
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
tex = surf->texinfo->texture->render;
}
// emit the polygon indices for the surface to the texture's
// element chain
add_surf_elements (tex, is, &ec, bctx, bframe);
}
if (tex) {
bind_texture (tex, 1, bctx->layout, dfunc,
bframe->cmdSet.a[QFV_bspSky]);
for (ec = tex->elechain; ec; ec = ec->next) {
draw_elechain (ec, bctx->layout, device,
bframe->cmdSet.a[QFV_bspSky]);
reset_elechain (ec);
}
tex->elechain = 0;
tex->elechain_tail = &tex->elechain;
}
sky_end (ctx);
bctx->sky_chain = 0;
bctx->sky_chain_tail = &bctx->sky_chain;
VkPipelineLayout layout = bctx->layout;
__auto_type pass = &bctx->main_pass;
pass->textures = &bctx->registered_textures;
draw_queue (pass, 1, layout, device, bframe->cmdSet.a[QFV_bspSky]);
sky_end (ctx);
}
static void
@ -1348,12 +1174,6 @@ Vulkan_Bsp_Init (vulkan_ctx_t *ctx)
bspctx_t *bctx = calloc (1, sizeof (bspctx_t));
ctx->bsp_context = bctx;
bctx->waterchain_tail = &bctx->waterchain;
bctx->sky_chain_tail = &bctx->sky_chain;
bctx->static_instsurfs_tail = &bctx->static_instsurfs;
bctx->elechains_tail = &bctx->elechains;
bctx->instsurfs_tail = &bctx->instsurfs;
bctx->light_scrap = QFV_CreateScrap (device, "lightmap_atlas", 2048,
tex_frgba, ctx->staging);
size_t size = QFV_ScrapSize (bctx->light_scrap);
@ -1362,7 +1182,14 @@ Vulkan_Bsp_Init (vulkan_ctx_t *ctx)
create_default_skys (ctx);
DARRAY_INIT (&bctx->texture_chains, 64);
DARRAY_INIT (&bctx->registered_textures, 64);
bctx->main_pass.num_queues = 3;
bctx->main_pass.draw_queues = malloc (bctx->main_pass.num_queues
* sizeof (bsp_drawset_t));
for (int i = 0; i < bctx->main_pass.num_queues; i++) {
DARRAY_INIT (&bctx->main_pass.draw_queues[i], 64);
}
size_t frames = ctx->frames.size;
DARRAY_INIT (&bctx->frames, frames);
@ -1418,7 +1245,11 @@ Vulkan_Bsp_Shutdown (struct vulkan_ctx_s *ctx)
dfunc->vkDestroyPipeline (device->dev, bctx->skybox, 0);
dfunc->vkDestroyPipeline (device->dev, bctx->skysheet, 0);
dfunc->vkDestroyPipeline (device->dev, bctx->turb, 0);
DARRAY_CLEAR (&bctx->texture_chains);
DARRAY_CLEAR (&bctx->registered_textures);
for (int i = 0; i < bctx->main_pass.num_queues; i++) {
DARRAY_CLEAR (&bctx->main_pass.draw_queues[i]);
}
free (bctx->main_pass.draw_queues);
DARRAY_CLEAR (&bctx->frames);
QFV_DestroyStagingBuffer (bctx->light_stage);
QFV_DestroyScrap (bctx->light_scrap);