diff --git a/include/QF/Vulkan/qf_bsp.h b/include/QF/Vulkan/qf_bsp.h index f1713fcbf..3f3973033 100644 --- a/include/QF/Vulkan/qf_bsp.h +++ b/include/QF/Vulkan/qf_bsp.h @@ -39,13 +39,27 @@ #include "QF/simd/types.h" +/** \defgroup vulkan_bsp Brush model rendering + \ingroup vulkan +*/ + +/** Represent a single face (polygon) of a brush model. + * + * There is one of these for each face in the bsp (brush) model, built at run + * time when the model is loaded (actually, after all models are loaded but + * before rendering begins). + */ typedef struct bsp_face_s { - uint32_t first_index; - uint32_t index_count; - uint32_t tex_id; - uint32_t flags; + uint32_t first_index; ///< index of first index in poly_indices + uint32_t index_count; ///< includes primitive restart + uint32_t tex_id; ///< texture bound to this face (maybe animated) + uint32_t flags; ///< face drawing (alpha, side, sky, turb) } bsp_face_t; +/** Represent a brush model, both main and sub-model. + * + * Used for rendering non-world models. + */ typedef struct bsp_model_s { uint32_t first_face; uint32_t face_count; @@ -62,21 +76,57 @@ typedef struct texmip_s { uint32_t offsets[MIPLEVELS]; } texmip_t; #endif +/** \defgroup vulkan_bsp_texanim Animated Textures + * \ingroup vulkan_bsp + * + * Brush models support texture animations. For general details, see + * \ref bsp_texture_animation. These structures allow for quick lookup + * of the correct texture to use in an animation cycle, or even whether there + * is an animation cycle. + */ +///@{ +/** Represent a texture's animation group. + * + * Every texture is in an animation group, even when not animated. When the + * texture is not animated, `count` is 1, otherwise `count` is the number of + * frames in the group, thus every texture has at least one frame. + * + * Each texture in a particular groupp shares the same `base` frame, with + * `offset` giving the texture's relative frame number within the group. + * The current frame is given by `base + (anim_index + offset) % count` where + * `anim_index` is the global time-based texture animation frame. + */ typedef struct texanim_s { - uint16_t base; - byte offset; - byte count; + uint16_t base; ///< first frame in group + byte offset; ///< relative frame in group + byte count; ///< number of frames in group } texanim_t; +/** Holds texture animation data for brush models. + * + * Brush models support one or two texture animation groups, based on the + * entity's frame (0 or non-0). When the entity's frame is 0, group 0 is used, + * otherwise group 1 is used. If there is no alternate (group 1) animation + * data for the texture, then the texture's group 0 data is copied to group 1 + * in order to avoid coplications in selecting which texture a face is to use. + * + * As all of a group's frames are together, `frame_map` is used to get the + * actual texture id for the frame. + */ typedef struct texdata_s { // texname_t *names; // texmip_t **mips; - texanim_t *anim_main; - texanim_t *anim_alt; - uint16_t *anim_map; + texanim_t *anim_main; ///< group 0 animations + texanim_t *anim_alt; ///< group 1 animations + uint16_t *frame_map; ///< map from texture frame to texture id // int num_tex; } texdata_t; +///@} +/** \defgroup vulkan_bsp_draw Brush model drawing + * \ingroup vulkan_bsp + */ +///@{ typedef struct vulktex_s { struct qfv_tex_s *tex; VkDescriptorSet descriptor; @@ -86,57 +136,186 @@ typedef struct vulktex_s { typedef struct regtexset_s DARRAY_TYPE (vulktex_t *) regtexset_t; +/** Represent a single draw call. + * + * For each texture that has faces to be rendered, one or more draw calls is + * made. Normally, only one call per texture is made, but if different models + * use the same texture, then a separate draw call is made for each model. + * When multiple entities use the same model, instanced rendering is used to + * draw all the faces sharing a texture for all the entities using that model. + * Thus when there are multiple draw calls for a single texture, they are + * grouped together so there is only one bind per texture. + * + * The index buffer is populated every frame with the vertex indices of the + * faces to be rendered for the current frame, grouped by texture and instance + * id (model render id). + * + * The model render id is assigned after models are loaded but before rendering + * begins and remains constant until the next time models are loaded (level + * change). + * + * The entid buffer is also populated every frame with the render id of the + * entities to be drawn that frame, It is used to map gl_InstanceIndex to + * entity id so as to look up the entity's transform and color (and any other + * data in the future). + * + * \dot + * digraph vulkan_bsp_draw_call { + * layout=dot; rankdir=LR; compound=true; nodesep=1.0; + * vertices [shape=none,label=< + * + * + * + * + * + *
vertex
vertex
...
vertex
vertex
>]; + * indices [shape=none,label=< + * + * + * + * + * + *
index
index
...
index
index
>]; + * entids [shape=none,label=< + * + * + * + * + * + * + *
entid
...
entid
entid
...
entid
>]; + * entdata [shape=none,label=< + * + * + * + * + * + * + *
transformcolor
transformcolor
...
transformcolor
...
transformcolor
>]; + * drawcall [shape=none,label=< + * + * + * + * + * + * + *
tex_id
inst_id
first_index
index_count
first_instance
instance_count
>]; + * textures [shape=none,label=< + * + * + * + * + * + *
texture
texture
texture
...
texture
>]; + * vertex [label="vertex shader"]; + * fragment [label="fragment shader"]; + * drawcall:tex -> textures:p; + * drawcall:ind -> indices:p; + * drawcall:inst -> entids:p; + * entids:p -> entdata:p; + * indices:p -> vertices:p; + * vertex -> entdata [label="storage buffer"]; + * vertex -> entids [label="per instance"]; + * vertex -> indices [label="index buffer"]; + * vertex -> vertices [label="per vertex"]; + * fragment -> textures [label="per call"]; + * } + * \enddot + */ +///@{ typedef struct bsp_draw_s { - uint32_t tex_id; - uint32_t inst_id; - uint32_t index_count; - uint32_t instance_count; - uint32_t first_index; - uint32_t first_instance; + uint32_t tex_id; ///< texture to bind for this draw call + uint32_t inst_id; ///< model render id owning this draw call + uint32_t index_count; ///< number of indices for this draw call + uint32_t instance_count; ///< number of instances to draw + uint32_t first_index; ///< index into index buffer + uint32_t first_instance; ///< index into entid buffer } bsp_draw_t; typedef struct bsp_drawset_s DARRAY_TYPE (bsp_draw_t) bsp_drawset_t; +///@} +/** Tag models that are to be queued for translucent drawing. + */ #define INST_ALPHA (1u<<31) + +/** Representation of a single face queued for drawing. + */ +///@{ typedef struct instface_s { - uint32_t inst_id; - uint32_t face; + uint32_t inst_id; ///< model render id owning this face + uint32_t face; ///< index of face in context array } instface_t; typedef struct bsp_instfaceset_s DARRAY_TYPE (instface_t) bsp_instfaceset_t; +///@} +/** Track entities using a model. + */ +///@{ typedef struct bsp_modelentset_s DARRAY_TYPE (uint32_t) bsp_modelentset_t; +/** Represent a single model and the entities using it. + */ typedef struct bsp_instance_s { - int first_instance; - bsp_modelentset_t entities; + int first_instance; ///< index into entid buffer + bsp_modelentset_t entities; ///< list of entity render ids using this model } bsp_instance_t; +///@} typedef struct bsp_pass_s { - vec4f_t position; - plane_t *frustum; - const struct mod_brush_s *brush; - struct bspctx_s *bsp_context; - uint32_t *indices; // points into index buffer - uint32_t index_count; // number of indices written to buffer - uint32_t *entid_data; // points into entid buffer - uint32_t entid_count; - 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; - uint32_t inst_id; - bsp_instance_t *instances; - int ent_frame; + vec4f_t position; ///< view position + plane_t *frustum; ///< view frustum for culling + const struct mod_brush_s *brush;///< data for current model + struct bspctx_s *bsp_context; ///< owning bsp context + /** \name GPU data + * + * The indices to be drawn and the entity ids associated with each draw + * instance are updated each frame. The pointers are to the per-frame + * mapped buffers for the respective data. + */ + ///@{ + uint32_t *indices; ///< polygon vertex indices + uint32_t index_count; ///< number of indices written to buffer + uint32_t *entid_data; ///< instance id to entity id map + uint32_t entid_count; ///< numer of entids written to buffer + ///@} + /** \name Potentially Visible Sets + * + * For an object to be in the PVS, its frame id must match the current + * visibility frame id, thus clearing all sets is done by incrementing + * `vis_frame`, and adding an object to the PVS is done by setting its + * current frame id to the current visibility frame id. + */ + ///@{ + int vis_frame; ///< current visibility frame id + int *face_frames; ///< per-face visibility frame ids + int *leaf_frames; ///< per-leaf visibility frame ids + int *node_frames; ///< per-node visibility frame ids + ///@} + bsp_instfaceset_t *face_queue; ///< per-texture face queues + regtexset_t *textures; ///< textures to bind when emitting calls + int num_queues; ///< number of pipeline queues + bsp_drawset_t *draw_queues; ///< per-pipeline draw queues + uint32_t inst_id; ///< render id of current model + bsp_instance_t *instances; ///< per-model entid lists + // FIXME There are several potential optimizations here: + // 1) ent_frame could be forced to be 0 or 1 and then used to index a + // two-element array of texanim pointers + // 2) ent_frame could be a pointer to the correct texanim array + // 3) could update a tex_id map each frame and unconditionally index that + // + // As the texture id is used for selecting the face queue, 3 could be used + // for mapping all textures to 1 or two queues for shadow rendering + int ent_frame; ///< animation frame of current entity } bsp_pass_t; +///@} +/// \ingroup vulkan_bsp +///@{ typedef enum { QFV_bspDepth, QFV_bspGBuffer, @@ -159,31 +338,33 @@ typedef struct bspframe_s { typedef struct bspframeset_s DARRAY_TYPE (bspframe_t) bspframeset_t; +/** Main BSP context structure + * + * This holds all the state and resources needed for rendering brush models. + */ typedef struct bspctx_s { - regtexset_t registered_textures; - struct qfv_tex_s *default_skysheet; - struct qfv_tex_s *skysheet_tex; - - struct qfv_tex_s *default_skybox; - struct qfv_tex_s *skybox_tex; - VkDescriptorSet skybox_descriptor; - - vulktex_t notexture; + vulktex_t notexture; ///< replacement for invalid textures struct scrap_s *light_scrap; struct qfv_stagebuf_s *light_stage; - bsp_model_t *models; - bsp_face_t *faces; - uint32_t *poly_indices; + int num_models; ///< number of loaded brush models + bsp_model_t *models; ///< all loaded brush models + bsp_face_t *faces; ///< all faces from all loaded brush models + uint32_t *poly_indices; ///< face indices from all loaded brush models - texdata_t texdata; - int anim_index; + regtexset_t registered_textures;///< textures for all loaded brush models + texdata_t texdata; ///< texture animation data + int anim_index; ///< texture animation frame (5fps) + struct qfv_tex_s *default_skysheet; + struct qfv_tex_s *skysheet_tex; ///< scrolling sky texture for current map - int model_id; + struct qfv_tex_s *default_skybox; + struct qfv_tex_s *skybox_tex; ///< sky box texture for current map + VkDescriptorSet skybox_descriptor; - bsp_pass_t main_pass; // camera view depth, gbuffer, etc + bsp_pass_t main_pass; ///< camera view depth, gbuffer, etc VkSampler sampler; VkPipelineLayout layout; @@ -207,7 +388,6 @@ typedef struct bspctx_s { struct vulkan_ctx_s; struct qfv_renderframe_s; -void Vulkan_ClearElements (struct vulkan_ctx_s *ctx); void Vulkan_DrawWorld (struct qfv_renderframe_s *rFrame); void Vulkan_DrawSky (struct qfv_renderframe_s *rFrame); void Vulkan_DrawWaterSurfaces (struct qfv_renderframe_s *rFrame); @@ -219,5 +399,6 @@ void Vulkan_BuildDisplayLists (model_t **models, int num_models, struct vulkan_ctx_s *ctx); void Vulkan_Bsp_Init (struct vulkan_ctx_s *ctx); void Vulkan_Bsp_Shutdown (struct vulkan_ctx_s *ctx); +///@} #endif//__QF_Vulkan_qf_bsp_h diff --git a/include/QF/Vulkan/qf_vid.h b/include/QF/Vulkan/qf_vid.h index 19d62795d..7df68837b 100644 --- a/include/QF/Vulkan/qf_vid.h +++ b/include/QF/Vulkan/qf_vid.h @@ -35,6 +35,9 @@ #endif #include +/** \defgroup vulkan Vulkan Renderer +*/ + enum { QFV_rp_shadowmap, QFV_rp_main, diff --git a/libs/video/renderer/vulkan/vulkan_bsp.c b/libs/video/renderer/vulkan/vulkan_bsp.c index 1020bbefa..96101e26f 100644 --- a/libs/video/renderer/vulkan/vulkan_bsp.c +++ b/libs/video/renderer/vulkan/vulkan_bsp.c @@ -91,14 +91,12 @@ static const char * __attribute__((used)) bsp_pass_names[] = { }; static QFV_Subpass subpass_map[] = { - QFV_passDepth, // QFV_bspDepth - QFV_passGBuffer, // QFV_bspGBuffer - QFV_passTranslucent, // QFV_bspSky - QFV_passTranslucent, // QFV_bspTurb + [QFV_bspDepth] = QFV_passDepth, + [QFV_bspGBuffer] = QFV_passGBuffer, + [QFV_bspSky] = QFV_passTranslucent, + [QFV_bspTurb] = QFV_passTranslucent, }; -#define ALLOC_CHUNK 64 - static void add_texture (texture_t *tx, vulkan_ctx_t *ctx) { @@ -113,12 +111,6 @@ add_texture (texture_t *tx, vulkan_ctx_t *ctx) } } -void -Vulkan_ClearElements (vulkan_ctx_t *ctx) -{ -// bspctx_t *bctx = ctx->bsp_context; -} - static inline void chain_surface (const bsp_face_t *face, bsp_pass_t *pass, const bspctx_t *bctx) { @@ -128,7 +120,7 @@ chain_surface (const bsp_face_t *face, bsp_pass_t *pass, const bspctx_t *bctx) const texanim_t *anim = ent_frame ? &bctx->texdata.anim_alt[face->tex_id] : &bctx->texdata.anim_main[face->tex_id]; int anim_ind = (bctx->anim_index + anim->offset) % anim->count; - int tex_id = bctx->texdata.anim_map[anim->base + anim_ind]; + int tex_id = bctx->texdata.frame_map[anim->base + anim_ind]; DARRAY_APPEND (&pass->face_queue[tex_id], ((instface_t) { pass->inst_id, face - bctx->faces })); } @@ -165,7 +157,6 @@ clear_textures (vulkan_ctx_t *ctx) void Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx) { - clear_textures (ctx); add_texture (r_notexture_mip, ctx); { @@ -209,11 +200,13 @@ Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx) } } + // 2.5 for two texanim_t structs (32-bits each) and 1 uint16_t for each + // element size_t texdata_size = 2.5 * num_tex * sizeof (texanim_t); texanim_t *texdata = Hunk_AllocName (0, texdata_size, "texdata"); bctx->texdata.anim_main = texdata; bctx->texdata.anim_alt = texdata + num_tex; - bctx->texdata.anim_map = (uint16_t *) (texdata + 2 * num_tex); + bctx->texdata.frame_map = (uint16_t *) (texdata + 2 * num_tex); int16_t map_index = 0; for (int i = 0; i < num_tex; i++) { texanim_t *anim = bctx->texdata.anim_main + i; @@ -222,7 +215,7 @@ Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx) continue; } *anim = (texanim_t) { .base = map_index, .offset = 0, .count = 1 }; - bctx->texdata.anim_map[anim->base] = i; + bctx->texdata.frame_map[anim->base] = i; if (textures[i]->anim_total > 1) { // bsp loader multiplies anim_total by ANIM_CYCLE to slow the @@ -240,7 +233,7 @@ Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx) } *a = *anim; a->offset = j; - bctx->texdata.anim_map[a->base + a->offset] = vtex->tex_id; + bctx->texdata.frame_map[a->base + a->offset] = vtex->tex_id; tx = tx->anim_next; } if (tx != textures[i]) { @@ -260,6 +253,7 @@ Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx) } } + // create face queue arrays 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] @@ -379,11 +373,11 @@ Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx) face_sets[i] = (facerefset_t) DARRAY_STATIC_INIT (1024); } - for (int i = 0; i < bctx->model_id; i++) { + for (int i = 0; i < bctx->num_models; i++) { DARRAY_CLEAR (&bctx->main_pass.instances[i].entities); } free (bctx->main_pass.instances); - bctx->model_id = 0; + bctx->num_models = 0; // run through all surfaces, chaining them to their textures, thus // effectively sorting the surfaces by texture (without worrying about @@ -396,7 +390,7 @@ Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx) if (!m || m->type != mod_brush) { continue; } - m->render_id = bctx->model_id++; + m->render_id = bctx->num_models++; if (*m->path == '*') { continue; } @@ -422,9 +416,9 @@ Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx) } face_base += brush->numsurfaces; } - bctx->main_pass.instances = malloc (bctx->model_id + bctx->main_pass.instances = malloc (bctx->num_models * sizeof (bsp_instance_t)); - for (int i = 0; i < bctx->model_id; i++) { + for (int i = 0; i < bctx->num_models; i++) { DARRAY_INIT (&bctx->main_pass.instances[i].entities, 16); } // All vertices from all brush models go into one giant vbo. @@ -469,7 +463,7 @@ Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx) free (bctx->faces); free (bctx->poly_indices); free (bctx->models); - bctx->models = malloc (bctx->model_id * sizeof (bsp_model_t)); + bctx->models = malloc (bctx->num_models * sizeof (bsp_model_t)); bctx->faces = malloc (face_base * sizeof (bsp_face_t)); bctx->poly_indices = malloc (index_count * sizeof (uint32_t)); @@ -943,7 +937,7 @@ clear_queues (bspctx_t *bctx, bsp_pass_t *pass) for (int i = 0; i < pass->num_queues; i++) { DARRAY_RESIZE (&pass->draw_queues[i], 0); } - for (int i = 0; i < bctx->model_id; i++) { + for (int i = 0; i < bctx->num_models; i++) { pass->instances[i].first_instance = -1; DARRAY_RESIZE (&pass->instances[i].entities, 0); } @@ -1507,7 +1501,7 @@ Vulkan_Bsp_Shutdown (struct vulkan_ctx_s *ctx) free (bctx->models); free (bctx->main_pass.draw_queues); - for (int i = 0; i < bctx->model_id; i++) { + for (int i = 0; i < bctx->num_models; i++) { DARRAY_CLEAR (&bctx->main_pass.instances[i].entities); } free (bctx->main_pass.instances);