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=<
+ * transform | color |
+ * transform | color |
+ * ... |
+ * transform | color |
+ * ... |
+ * transform | color |
+ *
>];
+ * 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);