/* vulkan_bsp.c Vulkan bsp Copyright (C) 2012 Bill Currie Copyright (C) 2021 Bill Currie Author: Bill Currie Date: 2012/1/7 Date: 2021/1/18 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include "qfalloca.h" #include "QF/cvar.h" #include "QF/darray.h" #include "QF/image.h" #include "QF/render.h" #include "QF/sys.h" #include "QF/va.h" #include "QF/scene/entity.h" #include "QF/Vulkan/qf_bsp.h" #include "QF/Vulkan/qf_lightmap.h" #include "QF/Vulkan/qf_matrices.h" #include "QF/Vulkan/qf_texture.h" #include "QF/Vulkan/buffer.h" #include "QF/Vulkan/barrier.h" #include "QF/Vulkan/command.h" #include "QF/Vulkan/debug.h" #include "QF/Vulkan/descriptor.h" #include "QF/Vulkan/device.h" #include "QF/Vulkan/image.h" #include "QF/Vulkan/instance.h" #include "QF/Vulkan/renderpass.h" #include "QF/Vulkan/scrap.h" #include "QF/Vulkan/staging.h" #include "QF/simd/types.h" #include "r_internal.h" #include "vid_vulkan.h" typedef struct bsp_push_constants_s { mat4f_t Model; quat_t fog; float time; float alpha; } bsp_push_constants_t; static const char * __attribute__((used)) bsp_pass_names[] = { "depth", "g-buffer", "sky", "turb", }; static QFV_Subpass subpass_map[] = { QFV_passDepth, // QFV_bspDepth QFV_passGBuffer, // QFV_bspGBuffer QFV_passTranslucent, // QFV_bspSky QFV_passTranslucent, // QFV_bspTurb }; static float identity[] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }; static vulktex_t vulkan_notexture = { }; #define ALLOC_CHUNK 64 static void add_texture (texture_t *tx, vulkan_ctx_t *ctx) { bspctx_t *bctx = ctx->bsp_context; vulktex_t *tex = tx->render; if (tex->tex) { tex->tex_id = bctx->registered_textures.size; DARRAY_APPEND (&bctx->registered_textures, tex); tex->descriptor = Vulkan_CreateTextureDescriptor (ctx, tex->tex, bctx->sampler); } } 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, bspctx_t *bctx) { DARRAY_APPEND (&pass->face_queue[face->tex_id], ((instface_t) { bctx->inst_id, face - bctx->faces })); } static void register_textures (mod_brush_t *brush, vulkan_ctx_t *ctx) { texture_t *tex; for (unsigned i = 0; i < brush->numtextures; i++) { tex = brush->textures[i]; if (!tex) continue; add_texture (tex, ctx); } } static void clear_textures (vulkan_ctx_t *ctx) { bspctx_t *bctx = ctx->bsp_context; 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 Vulkan_RegisterTextures (model_t **models, int num_models, vulkan_ctx_t *ctx) { int i; model_t *m; mod_brush_t *brush = &r_refdef.worldmodel->brush; clear_textures (ctx); add_texture (r_notexture_mip, ctx); register_textures (brush, ctx); for (i = 0; i < num_models; i++) { m = models[i]; if (!m) continue; // sub-models are done as part of the main model if (*m->path == '*') continue; // world has already been done, not interested in non-brush models if (m == r_refdef.worldmodel || m->type != mod_brush) continue; brush = &m->brush; 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); } } typedef struct { msurface_t *face; model_t *model; int model_face_base; } faceref_t; typedef struct DARRAY_TYPE (faceref_t) facerefset_t; static void count_verts_inds (faceref_t *faceref, uint32_t *verts, uint32_t *inds) { msurface_t *surf = faceref->face; *verts = surf->numedges; *inds = surf->numedges + 1; } 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) { 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; for (int i = 0; i < numverts; i++) { vec_t *vec; int index = surfedges[surf->firstedge + i]; if (index > 0) { // forward edge vec = vertices[edges[index].v[0]].position; } else { // reverse edge vec = vertices[edges[-index].v[1]].position; } VectorCopy (vec, verts[i].vertex); verts[i].vertex[3] = 1; // homogeneous coord vec2f_t st = { DotProduct (vec, texinfo->vecs[0]) + texinfo->vecs[0][3], DotProduct (vec, texinfo->vecs[1]) + texinfo->vecs[1][3], }; verts[i].tlst[0] = st[0] / texinfo->texture->width; verts[i].tlst[1] = st[1] / texinfo->texture->height; if (surf->lightpic) { //lightmap texture coordinates //every lit surface has its own lighmap at a 1/16 resolution //(ie, 16 albedo pixels for every lightmap pixel) const vrect_t *rect = surf->lightpic->rect; vec2f_t lmorg = (vec2f_t) { VEC2_EXP (&rect->x) } * 16 + 8; vec2f_t texorg = { VEC2_EXP (surf->texturemins) }; st = ((st - texorg + lmorg) / 16) * surf->lightpic->size; verts[i].tlst[2] = st[0]; verts[i].tlst[3] = st[1]; } else { // no lightmap for this surface (probably sky or water), so // make the lightmap texture polygone degenerate verts[i].tlst[2] = 0; verts[i].tlst[3] = 0; } } } void Vulkan_BuildDisplayLists (model_t **models, int num_models, vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; bspctx_t *bctx = ctx->bsp_context; if (!num_models) { 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 // and non-bsp models don't have surfaces. if (!m || *m->path == '*' || m->type != mod_brush) continue; mod_brush_t *brush = &m->brush; dmodel_t *dm = brush->submodels; for (unsigned j = 0; j < brush->numsurfaces; j++) { if (j == dm->firstface + dm->numfaces) { // move on to the next sub-model dm++; if (dm == brush->submodels + brush->numsubmodels) { // limit the surfaces // probably never hit Sys_Printf ("Vulkan_BuildDisplayLists: too many faces\n"); brush->numsurfaces = j; break; } } msurface_t *surf = brush->surfaces + j; // append surf to the texture chain vulktex_t *tex = surf->texinfo->texture->render; 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; // 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 (faceref, &verts, &inds); vertex_count += verts; index_count += inds; poly_count++; } } size_t atom = device->physDev->properties.limits.nonCoherentAtomSize; size_t atom_mask = atom - 1; size_t frames = bctx->frames.size; size_t index_buffer_size = index_count * frames * sizeof (uint32_t); size_t vertex_buffer_size = vertex_count * sizeof (bspvert_t); index_buffer_size = (index_buffer_size + atom_mask) & ~atom_mask; qfv_stagebuf_t *stage = QFV_CreateStagingBuffer (device, "bsp", vertex_buffer_size, ctx->cmdpool); 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. The primitive restart markers are included // in index_count. // so each polygon within the list: // index count-1 indices // index // ... // "end of primitive" (~0u) 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). 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); } } Sys_MaskPrintf (SYS_vulkan, "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); dfunc->vkDestroyBuffer (device->dev, bctx->index_buffer, 0); dfunc->vkFreeMemory (device->dev, bctx->index_memory, 0); } bctx->index_buffer = QFV_CreateBuffer (device, index_buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT); QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, bctx->index_buffer, "buffer:bsp:index"); bctx->index_memory = QFV_AllocBufferMemory (device, bctx->index_buffer, VK_MEMORY_PROPERTY_HOST_CACHED_BIT, index_buffer_size, 0); QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, bctx->index_memory, "memory:bsp:index"); QFV_BindBufferMemory (device, bctx->index_buffer, bctx->index_memory, 0); bctx->index_buffer_size = index_buffer_size; void *data; dfunc->vkMapMemory (device->dev, bctx->index_memory, 0, index_buffer_size, 0, &data); uint32_t *index_data = data; for (size_t i = 0; i < frames; i++) { uint32_t offset = index_count * i; bctx->frames.a[i].index_data = index_data + offset; bctx->frames.a[i].index_offset = offset * sizeof (uint32_t); bctx->frames.a[i].index_count = 0; } } if (vertex_buffer_size > bctx->vertex_buffer_size) { if (bctx->vertex_buffer) { dfunc->vkDestroyBuffer (device->dev, bctx->vertex_buffer, 0); dfunc->vkFreeMemory (device->dev, bctx->vertex_memory, 0); } bctx->vertex_buffer = QFV_CreateBuffer (device, vertex_buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, bctx->vertex_buffer, "buffer:bsp:vertex"); bctx->vertex_memory = QFV_AllocBufferMemory (device, bctx->vertex_buffer, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertex_buffer_size, 0); QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, bctx->vertex_memory, "memory:bsp:vertex"); QFV_BindBufferMemory (device, bctx->vertex_buffer, bctx->vertex_memory, 0); bctx->vertex_buffer_size = vertex_buffer_size; } qfv_bufferbarrier_t bb = bufferBarriers[qfv_BB_Unknown_to_TransferWrite]; bb.barrier.buffer = bctx->vertex_buffer; bb.barrier.size = vertex_buffer_size; dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, 0, 0, 0, 1, &bb.barrier, 0, 0); VkBufferCopy copy_region = { packet->offset, 0, vertex_buffer_size }; dfunc->vkCmdCopyBuffer (packet->cmd, stage->buffer, bctx->vertex_buffer, 1, ©_region); bb = bufferBarriers[qfv_BB_TransferWrite_to_VertexAttrRead]; bb.barrier.buffer = bctx->vertex_buffer; bb.barrier.size = vertex_buffer_size; dfunc->vkCmdPipelineBarrier (packet->cmd, bb.srcStages, bb.dstStages, 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 R_DrawBrushModel (entity_t *e, vulkan_ctx_t *ctx) { float dot, radius; model_t *model; plane_t *plane; msurface_t *surf; qboolean rotated; vec3_t mins, maxs; vec4f_t org; mod_brush_t *brush; bspctx_t *bctx = ctx->bsp_context; bctx->entity = e; bctx->transform = e->renderer.full_transform; bctx->color = e->renderer.colormod; model = e->renderer.model; brush = &model->brush; mat4f_t mat; Transform_GetWorldMatrix (e->transform, mat); memcpy (e->renderer.full_transform, mat, sizeof (mat));//FIXME if (mat[0][0] != 1 || mat[1][1] != 1 || mat[2][2] != 1) { rotated = true; radius = model->radius; if (R_CullSphere (r_refdef.frustum, (vec_t*)&mat[3], radius)) { //FIXME return; } } else { rotated = false; VectorAdd (mat[3], model->mins, mins); VectorAdd (mat[3], model->maxs, maxs); if (R_CullBox (r_refdef.frustum, mins, maxs)) return; } org = r_refdef.frame.position - mat[3]; if (rotated) { vec4f_t temp = org; org[0] = DotProduct (temp, mat[0]); org[1] = DotProduct (temp, mat[1]); org[2] = DotProduct (temp, mat[2]); } surf = &brush->surfaces[brush->firstmodelsurface]; for (unsigned i = 0; i < brush->nummodelsurfaces; i++, surf++) { // find the node side on which we are plane = surf->plane; dot = PlaneDiff (org, plane); // enqueue the polygon if (((surf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(surf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { //chain_surface (surf, ctx); } } } static inline void visit_leaf (mleaf_t *leaf) { // since this leaf will be rendered, any entities in the leaf also need // to be rendered (the bsp tree doubles as an entity cull structure) if (leaf->efrags) R_StoreEfrags (leaf->efrags); } // 1 = back side, 0 = front side static inline int get_side (mnode_t *node) { // find the node side on which we are vec4f_t org = r_refdef.frame.position; return dotf (org, node->plane)[0] < 0; } 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; // sneaky hack for side = side ? SURF_PLANEBACK : 0; // seems to be microscopically faster even on modern hardware side = (-side) & SURF_PLANEBACK; // 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)) { 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 ^ (face->flags & SURF_PLANEBACK)) continue; // wrong side chain_surface (face, pass, bctx); } } } static inline int test_node (mod_brush_t *brush, int node_id) { if (node_id < 0) return 0; if (r_node_visframes[node_id] != r_visframecount) return 0; mnode_t *node = brush->nodes + node_id; if (R_CullBox (r_refdef.frustum, node->minmaxs, node->minmaxs + 3)) return 0; return 1; } static void R_VisitWorldNodes (mod_brush_t *brush, vulkan_ctx_t *ctx) { typedef struct { int node_id; int side; } rstack_t; rstack_t *node_ptr; rstack_t *node_stack; int node_id; int front; int side; node_id = 0; // +2 for paranoia node_stack = alloca ((brush->depth + 2) * sizeof (rstack_t)); node_ptr = node_stack; while (1) { while (test_node (brush, node_id)) { mnode_t *node = brush->nodes + node_id; side = get_side (node); front = node->children[side]; if (test_node (brush, front)) { node_ptr->node_id = node_id; node_ptr->side = side; node_ptr++; node_id = front; continue; } // front is either not a node (ie, is a leaf) or is not visible // if node is visible, then at least one of its child nodes // must also be visible, and a leaf child in front of the node // will be visible, so no need for vis checks on a leaf if (front < 0) { mleaf_t *leaf = brush->leafs + ~front; if (leaf->contents != CONTENTS_SOLID) { visit_leaf (leaf); } } visit_node (brush, node, side, ctx); node_id = node->children[side ^ 1]; } if (node_id < 0) { mleaf_t *leaf = brush->leafs + ~node_id; if (leaf->contents != CONTENTS_SOLID) { visit_leaf (leaf); } } if (node_ptr != node_stack) { node_ptr--; node_id = node_ptr->node_id; side = node_ptr->side; mnode_t *node = brush->nodes + node_id; visit_node (brush, node, side, ctx); node_id = node->children[side ^ 1]; continue; } break; } } static void push_transform (vec_t *transform, VkPipelineLayout layout, qfv_device_t *device, VkCommandBuffer cmd) { qfv_push_constants_t push_constants[] = { { VK_SHADER_STAGE_VERTEX_BIT, field_offset (bsp_push_constants_t, Model), sizeof (mat4f_t), transform }, }; QFV_PushConstants (device, cmd, layout, 1, push_constants); } static void bind_texture (vulktex_t *tex, uint32_t setnum, VkPipelineLayout layout, qfv_devfuncs_t *dfunc, VkCommandBuffer cmd) { VkDescriptorSet sets[] = { tex->descriptor, }; dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, setnum, 1, sets, 0, 0); } static void push_fragconst (bsp_push_constants_t *constants, VkPipelineLayout layout, qfv_device_t *device, VkCommandBuffer cmd) { qfv_push_constants_t push_constants[] = { //{ VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof (mat), mat }, { VK_SHADER_STAGE_FRAGMENT_BIT, field_offset (bsp_push_constants_t, fog), sizeof (constants->fog), &constants->fog }, { VK_SHADER_STAGE_FRAGMENT_BIT, field_offset (bsp_push_constants_t, time), sizeof (constants->time), &constants->time }, { VK_SHADER_STAGE_FRAGMENT_BIT, field_offset (bsp_push_constants_t, alpha), sizeof (constants->alpha), &constants->alpha }, }; QFV_PushConstants (device, cmd, layout, 3, push_constants); } static void bsp_begin_subpass (QFV_BspSubpass subpass, VkPipeline pipeline, VkPipelineLayout layout, qfv_renderframe_t *rFrame) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; bspctx_t *bctx = ctx->bsp_context; __auto_type cframe = &ctx->frames.a[ctx->curFrame]; bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; VkCommandBuffer cmd = bframe->cmdSet.a[subpass]; dfunc->vkResetCommandBuffer (cmd, 0); VkCommandBufferInheritanceInfo inherit = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, 0, rFrame->renderpass->renderpass, subpass_map[subpass], cframe->framebuffer, 0, 0, 0, }; VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT, &inherit, }; dfunc->vkBeginCommandBuffer (cmd, &beginInfo); QFV_duCmdBeginLabel (device, cmd, va (ctx->va_ctx, "bsp:%s", bsp_pass_names[subpass]), {0, 0.5, 0.6, 1}); dfunc->vkCmdBindPipeline (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); dfunc->vkCmdSetViewport (cmd, 0, 1, &rFrame->renderpass->viewport); dfunc->vkCmdSetScissor (cmd, 0, 1, &rFrame->renderpass->scissor); VkDeviceSize offsets[] = { 0 }; dfunc->vkCmdBindVertexBuffers (cmd, 0, 1, &bctx->vertex_buffer, offsets); dfunc->vkCmdBindIndexBuffer (cmd, bctx->index_buffer, bframe->index_offset, VK_INDEX_TYPE_UINT32); VkDescriptorSet sets[] = { Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), }; dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, 1, sets, 0, 0); //XXX glsl_Fog_GetColor (fog); //XXX fog[3] = glsl_Fog_GetDensity () / 64.0; } static void bsp_end_subpass (VkCommandBuffer cmd, vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; QFV_duCmdEndLabel (device, cmd); dfunc->vkEndCommandBuffer (cmd); } static void bsp_begin (qfv_renderframe_t *rFrame) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; bspctx_t *bctx = ctx->bsp_context; //XXX quat_t fog; bctx->default_color[3] = 1; QuatCopy (bctx->default_color, bctx->last_color); bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; DARRAY_APPEND (&rFrame->subpassCmdSets[QFV_passDepth], bframe->cmdSet.a[QFV_bspDepth]); DARRAY_APPEND (&rFrame->subpassCmdSets[QFV_passGBuffer], bframe->cmdSet.a[QFV_bspGBuffer]); qfvPushDebug (ctx, "bsp_begin_subpass"); bsp_begin_subpass (QFV_bspDepth, bctx->depth, bctx->layout, rFrame); bsp_begin_subpass (QFV_bspGBuffer, bctx->gbuf, bctx->layout, rFrame); qfvPopDebug (ctx); } static void bsp_end (vulkan_ctx_t *ctx) { bspctx_t *bctx = ctx->bsp_context; bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; bsp_end_subpass (bframe->cmdSet.a[QFV_bspDepth], ctx); bsp_end_subpass (bframe->cmdSet.a[QFV_bspGBuffer], ctx); } static void turb_begin (qfv_renderframe_t *rFrame) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; bspctx_t *bctx = ctx->bsp_context; bctx->default_color[3] = bound (0, r_wateralpha, 1); QuatCopy (bctx->default_color, bctx->last_color); bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; DARRAY_APPEND (&rFrame->subpassCmdSets[QFV_passTranslucent], bframe->cmdSet.a[QFV_bspTurb]); qfvPushDebug (ctx, "bsp_begin_subpass"); bsp_begin_subpass (QFV_bspTurb, bctx->turb, bctx->layout, rFrame); qfvPopDebug (ctx); } static void turb_end (vulkan_ctx_t *ctx) { bspctx_t *bctx = ctx->bsp_context; bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; bsp_end_subpass (bframe->cmdSet.a[QFV_bspTurb], ctx); } static void sky_begin (qfv_renderframe_t *rFrame) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; bspctx_t *bctx = ctx->bsp_context; bctx->default_color[3] = 1; QuatCopy (bctx->default_color, bctx->last_color); bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; DARRAY_APPEND (&rFrame->subpassCmdSets[QFV_passTranslucent], bframe->cmdSet.a[QFV_bspSky]); qfvPushDebug (ctx, "bsp_begin_subpass"); if (bctx->skybox_tex) { bsp_begin_subpass (QFV_bspSky, bctx->skybox, bctx->layout, rFrame); } else { bsp_begin_subpass (QFV_bspSky, bctx->skysheet, bctx->layout, rFrame); } qfvPopDebug (ctx); } static void sky_end (vulkan_ctx_t *ctx) { bspctx_t *bctx = ctx->bsp_context; bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; bsp_end_subpass (bframe->cmdSet.a[QFV_bspSky], ctx); } static void clear_queues (bspctx_t *bctx, bsp_pass_t *pass) { for (size_t i = 0; i < bctx->registered_textures.size; i++) { DARRAY_RESIZE (&pass->face_queue[i], 0); } for (int j = 0; j < pass->num_queues; j++) { DARRAY_RESIZE (&pass->draw_queues[j], 0); } pass->index_count = 0; } static void draw_queue (bsp_pass_t *pass, int queue, VkPipelineLayout layout, qfv_device_t *device, VkCommandBuffer cmd) { qfv_devfuncs_t *dfunc = device->funcs; 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); } } void 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; bspctx_t *bctx = ctx->bsp_context; bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; entity_t worldent; mod_brush_t *brush; 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)); worldent.renderer.model = r_refdef.worldmodel; brush = &r_refdef.worldmodel->brush; bctx->entity = &worldent; bctx->transform = 0; bctx->color = 0; R_VisitWorldNodes (brush, ctx); if (!bctx->vertex_buffer) { return; } if (r_drawentities) { for (size_t i = 0; i < r_ent_queue->ent_queues[mod_brush].size; i++) { entity_t *ent = r_ent_queue->ent_queues[mod_brush].a[i]; R_DrawBrushModel (ent, ctx); } } bsp_begin (rFrame); push_transform (identity, bctx->layout, device, bframe->cmdSet.a[QFV_bspDepth]); push_transform (identity, bctx->layout, device, bframe->cmdSet.a[QFV_bspGBuffer]); bsp_push_constants_t frag_constants = { .time = vr_data.realtime }; push_fragconst (&frag_constants, bctx->layout, device, bframe->cmdSet.a[QFV_bspGBuffer]); __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; } //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); } void Vulkan_Bsp_Flush (vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; bspctx_t *bctx = ctx->bsp_context; bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; size_t atom = device->physDev->properties.limits.nonCoherentAtomSize; size_t atom_mask = atom - 1; size_t offset = bframe->index_offset; size_t size = bframe->index_count * sizeof (uint32_t); if (!bframe->index_count) { return; } offset &= ~atom_mask; size = (size + atom_mask) & ~atom_mask; VkMappedMemoryRange range = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0, bctx->index_memory, offset, size }; dfunc->vkFlushMappedMemoryRanges (device->dev, 1, &range); } void 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; bspctx_t *bctx = ctx->bsp_context; bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; if (!bctx->main_pass.draw_queues[2].size) return; turb_begin (rFrame); push_transform (identity, bctx->layout, device, bframe->cmdSet.a[QFV_bspTurb]); bsp_push_constants_t frag_constants = { .time = vr_data.realtime, .alpha = r_wateralpha }; push_fragconst (&frag_constants, bctx->layout, device, bframe->cmdSet.a[QFV_bspTurb]); 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 Vulkan_DrawSky (qfv_renderframe_t *rFrame) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; bspctx_t *bctx = ctx->bsp_context; bspframe_t *bframe = &bctx->frames.a[ctx->curFrame]; if (!bctx->main_pass.draw_queues[1].size) return; sky_begin (rFrame); vulktex_t skybox = { .descriptor = bctx->skybox_descriptor }; bind_texture (&skybox, 2, bctx->layout, dfunc, bframe->cmdSet.a[QFV_bspSky]); push_transform (identity, bctx->layout, device, bframe->cmdSet.a[QFV_bspSky]); bsp_push_constants_t frag_constants = { .time = vr_data.realtime }; push_fragconst (&frag_constants, bctx->layout, device, bframe->cmdSet.a[QFV_bspSky]); 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 create_default_skys (vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; bspctx_t *bctx = ctx->bsp_context; VkImage skybox; VkImage skysheet; VkDeviceMemory memory; VkImageView boxview; VkImageView sheetview; bctx->default_skybox = calloc (2, sizeof (qfv_tex_t)); bctx->default_skysheet = bctx->default_skybox + 1; VkExtent3D extents = { 1, 1, 1 }; skybox = QFV_CreateImage (device, 1, VK_IMAGE_TYPE_2D, VK_FORMAT_B8G8R8A8_UNORM, extents, 1, 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, skybox, "bsp:image:default_skybox"); skysheet = QFV_CreateImage (device, 0, VK_IMAGE_TYPE_2D, VK_FORMAT_B8G8R8A8_UNORM, extents, 1, 2, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE, skysheet, "bsp:image:default_skysheet"); VkMemoryRequirements requirements; dfunc->vkGetImageMemoryRequirements (device->dev, skybox, &requirements); size_t boxsize = requirements.size; dfunc->vkGetImageMemoryRequirements (device->dev, skysheet, &requirements); size_t sheetsize = requirements.size; memory = QFV_AllocImageMemory (device, skybox, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, boxsize + sheetsize, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY, memory, "bsp:memory:default_skys"); QFV_BindImageMemory (device, skybox, memory, 0); QFV_BindImageMemory (device, skysheet, memory, boxsize); boxview = QFV_CreateImageView (device, skybox, VK_IMAGE_VIEW_TYPE_CUBE, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, boxview, "bsp:iview:default_skybox"); sheetview = QFV_CreateImageView (device, skysheet, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); QFV_duSetObjectName (device, VK_OBJECT_TYPE_IMAGE_VIEW, sheetview, "bsp:iview:default_skysheet"); bctx->default_skybox->image = skybox; bctx->default_skybox->view = boxview; bctx->default_skybox->memory = memory; bctx->default_skysheet->image = skysheet; bctx->default_skysheet->view = sheetview; // temporarily commandeer the light map's staging buffer qfv_packet_t *packet = QFV_PacketAcquire (bctx->light_stage); qfv_imagebarrier_t ib = imageBarriers[qfv_LT_Undefined_to_TransferDst]; ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; VkImageMemoryBarrier barriers[2] = { ib.barrier, ib.barrier }; barriers[0].image = skybox; barriers[1].image = skysheet; dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, 0, 0, 0, 0, 0, 2, barriers); VkClearColorValue color = {}; VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS }; dfunc->vkCmdClearColorImage (packet->cmd, skybox, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range); dfunc->vkCmdClearColorImage (packet->cmd, skysheet, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range); ib = imageBarriers[qfv_LT_TransferDst_to_ShaderReadOnly]; ib.barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; ib.barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; barriers[0] = ib.barrier; barriers[1] = ib.barrier; barriers[0].image = skybox; barriers[1].image = skysheet; dfunc->vkCmdPipelineBarrier (packet->cmd, ib.srcStages, ib.dstStages, 0, 0, 0, 0, 0, 2, barriers); QFV_PacketSubmit (packet); } void Vulkan_Bsp_Init (vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; r_notexture_mip->render = &vulkan_notexture; qfvPushDebug (ctx, "bsp init"); bspctx_t *bctx = calloc (1, sizeof (bspctx_t)); ctx->bsp_context = bctx; bctx->light_scrap = QFV_CreateScrap (device, "lightmap_atlas", 2048, tex_frgba, ctx->staging); size_t size = QFV_ScrapSize (bctx->light_scrap); bctx->light_stage = QFV_CreateStagingBuffer (device, "lightmap", size, ctx->cmdpool); create_default_skys (ctx); 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); DARRAY_RESIZE (&bctx->frames, frames); bctx->frames.grow = 0; bctx->depth = Vulkan_CreateGraphicsPipeline (ctx, "bsp_depth"); bctx->gbuf = Vulkan_CreateGraphicsPipeline (ctx, "bsp_gbuf"); bctx->skybox = Vulkan_CreateGraphicsPipeline (ctx, "bsp_skybox"); bctx->skysheet = Vulkan_CreateGraphicsPipeline (ctx, "bsp_skysheet"); bctx->turb = Vulkan_CreateGraphicsPipeline (ctx, "bsp_turb"); bctx->layout = Vulkan_CreatePipelineLayout (ctx, "quakebsp_layout"); bctx->sampler = Vulkan_CreateSampler (ctx, "quakebsp_sampler"); for (size_t i = 0; i < frames; i++) { __auto_type bframe = &bctx->frames.a[i]; DARRAY_INIT (&bframe->cmdSet, QFV_bspNumPasses); DARRAY_RESIZE (&bframe->cmdSet, QFV_bspNumPasses); bframe->cmdSet.grow = 0; QFV_AllocateCommandBuffers (device, ctx->cmdpool, 1, &bframe->cmdSet); for (int j = 0; j < QFV_bspNumPasses; j++) { QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER, bframe->cmdSet.a[i], va (ctx->va_ctx, "cmd:bsp:%zd:%s", i, bsp_pass_names[j])); } } bctx->skybox_descriptor = Vulkan_CreateTextureDescriptor (ctx, bctx->default_skybox, bctx->sampler); qfvPopDebug (ctx); } void Vulkan_Bsp_Shutdown (struct vulkan_ctx_s *ctx) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; bspctx_t *bctx = ctx->bsp_context; for (size_t i = 0; i < bctx->frames.size; i++) { __auto_type bframe = &bctx->frames.a[i]; free (bframe->cmdSet.a); } dfunc->vkDestroyPipeline (device->dev, bctx->depth, 0); dfunc->vkDestroyPipeline (device->dev, bctx->gbuf, 0); dfunc->vkDestroyPipeline (device->dev, bctx->skybox, 0); dfunc->vkDestroyPipeline (device->dev, bctx->skysheet, 0); dfunc->vkDestroyPipeline (device->dev, bctx->turb, 0); 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); if (bctx->vertex_buffer) { dfunc->vkDestroyBuffer (device->dev, bctx->vertex_buffer, 0); dfunc->vkFreeMemory (device->dev, bctx->vertex_memory, 0); } if (bctx->index_buffer) { dfunc->vkDestroyBuffer (device->dev, bctx->index_buffer, 0); dfunc->vkFreeMemory (device->dev, bctx->index_memory, 0); } if (bctx->skybox_tex) { Vulkan_UnloadTex (ctx, bctx->skybox_tex); } dfunc->vkDestroyImageView (device->dev, bctx->default_skysheet->view, 0); dfunc->vkDestroyImage (device->dev, bctx->default_skysheet->image, 0); dfunc->vkDestroyImageView (device->dev, bctx->default_skybox->view, 0); dfunc->vkDestroyImage (device->dev, bctx->default_skybox->image, 0); dfunc->vkFreeMemory (device->dev, bctx->default_skybox->memory, 0); free (bctx->default_skybox); } void Vulkan_LoadSkys (const char *sky, vulkan_ctx_t *ctx) { bspctx_t *bctx = ctx->bsp_context; const char *name; int i; tex_t *tex; static const char *sky_suffix[] = { "ft", "bk", "up", "dn", "rt", "lf"}; if (bctx->skybox_tex) { Vulkan_UnloadTex (ctx, bctx->skybox_tex); Vulkan_FreeTexture (ctx, bctx->skybox_descriptor); } bctx->skybox_tex = 0; if (!sky || !*sky) { sky = r_skyname; } if (!*sky || !strcasecmp (sky, "none")) { Sys_MaskPrintf (SYS_vulkan, "Skybox unloaded\n"); bctx->skybox_descriptor = Vulkan_CreateTextureDescriptor (ctx, bctx->default_skybox, bctx->sampler); return; } name = va (ctx->va_ctx, "env/%s_map", sky); tex = LoadImage (name, 1); if (tex) { bctx->skybox_tex = Vulkan_LoadEnvMap (ctx, tex, sky); Sys_MaskPrintf (SYS_vulkan, "Loaded %s\n", name); } else { int failed = 0; tex_t *sides[6] = { }; for (i = 0; i < 6; i++) { name = va (ctx->va_ctx, "env/%s%s", sky, sky_suffix[i]); tex = LoadImage (name, 1); if (!tex) { Sys_MaskPrintf (SYS_vulkan, "Couldn't load %s\n", name); // also look in gfx/env, where Darkplaces looks for skies name = va (ctx->va_ctx, "gfx/env/%s%s", sky, sky_suffix[i]); tex = LoadImage (name, 1); if (!tex) { Sys_MaskPrintf (SYS_vulkan, "Couldn't load %s\n", name); failed = 1; continue; } } //FIXME find a better way (also, assumes data and struct together) sides[i] = malloc (ImageSize (tex, 1)); memcpy (sides[i], tex, ImageSize (tex, 1)); sides[i]->data = (byte *)(sides[i] + 1); Sys_MaskPrintf (SYS_vulkan, "Loaded %s\n", name); } if (!failed) { bctx->skybox_tex = Vulkan_LoadEnvSides (ctx, sides, sky); } for (i = 0; i < 6; i++) { free (sides[i]); } } if (bctx->skybox_tex) { bctx->skybox_descriptor = Vulkan_CreateTextureDescriptor (ctx, bctx->skybox_tex, bctx->sampler); Sys_MaskPrintf (SYS_vulkan, "Skybox %s loaded\n", sky); } }