/* vulkan_alias.c Vulkan alias model pipeline Copyright (C) 2012 Bill Currie Copyright (C) 2021 Bill Currie Author: Bill Currie Date: 2012/1/7 Date: 2021/1/26 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 #include #include "QF/cvar.h" #include "QF/va.h" #include "QF/scene/entity.h" #include "QF/Vulkan/qf_alias.h" #include "QF/Vulkan/qf_matrices.h" #include "QF/Vulkan/qf_palette.h" #include "QF/Vulkan/qf_renderpass.h" #include "QF/Vulkan/qf_texture.h" #include "QF/Vulkan/debug.h" #include "QF/Vulkan/device.h" #include "QF/Vulkan/instance.h" #include "QF/Vulkan/render.h" #include "r_internal.h" #include "vid_vulkan.h" typedef struct { mat4f_t mat; float blend; byte colors[4]; vec4f_t base_color; vec4f_t fog; } alias_push_constants_t; static const char * __attribute__((used)) alias_pass_names[] = { "depth", "g-buffer", "translucent", }; static QFV_Subpass subpass_map[] = { QFV_passDepth, // QFV_aliasDepth QFV_passGBuffer, // QFV_aliasGBuffer QFV_passTranslucentFrag,// QFV_aliasTranslucent }; static void emit_commands (VkCommandBuffer cmd, int pose1, int pose2, qfv_alias_skin_t *skin, uint32_t numPC, qfv_push_constants_t *constants, aliashdr_t *hdr, qfv_orenderframe_t *rFrame, entity_t ent) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; aliasctx_t *actx = ctx->alias_context; __auto_type mesh = (qfv_alias_mesh_t *) ((byte *) hdr + hdr->commands); VkDeviceSize offsets[] = { pose1 * hdr->poseverts * sizeof (aliasvrt_t), pose2 * hdr->poseverts * sizeof (aliasvrt_t), 0, }; VkBuffer buffers[] = { mesh->vertex_buffer, mesh->vertex_buffer, mesh->uv_buffer, }; int bindingCount = skin ? 3 : 2; Vulkan_BeginEntityLabel (ctx, cmd, ent); dfunc->vkCmdBindVertexBuffers (cmd, 0, bindingCount, buffers, offsets); dfunc->vkCmdBindIndexBuffer (cmd, mesh->index_buffer, 0, VK_INDEX_TYPE_UINT32); QFV_PushConstants (device, cmd, actx->layout, numPC, constants); if (skin) { VkDescriptorSet sets[] = { skin->descriptor, }; dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, actx->layout, 2, 1, sets, 0, 0); } dfunc->vkCmdDrawIndexed (cmd, 3 * hdr->mdl.numtris, 1, 0, 0, 0); QFV_CmdEndLabel (device, cmd); } void Vulkan_DrawAlias (entity_t ent, qfv_orenderframe_t *rFrame) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; aliasctx_t *actx = ctx->alias_context; aliasframe_t *aframe = &actx->frames.a[ctx->curFrame]; renderer_t *renderer = Ent_GetComponent (ent.id, scene_renderer, ent.reg); model_t *model = renderer->model; aliashdr_t *hdr; qfv_alias_skin_t *skin; alias_push_constants_t constants = {}; if (!(hdr = model->aliashdr)) { hdr = Cache_Get (&model->cache); } animation_t *animation = Ent_GetComponent (ent.id, scene_animation, ent.reg); constants.blend = R_AliasGetLerpedFrames (animation, hdr); transform_t transform = Entity_Transform (ent); qfv_push_constants_t push_constants[] = { { VK_SHADER_STAGE_VERTEX_BIT, field_offset (alias_push_constants_t, mat), sizeof (mat4f_t), Transform_GetWorldMatrixPtr (transform) }, { VK_SHADER_STAGE_VERTEX_BIT, field_offset (alias_push_constants_t, blend), sizeof (float), &constants.blend }, { VK_SHADER_STAGE_FRAGMENT_BIT, field_offset (alias_push_constants_t, colors), sizeof (constants.colors), constants.colors }, { VK_SHADER_STAGE_FRAGMENT_BIT, field_offset (alias_push_constants_t, base_color), sizeof (constants.base_color), &constants.base_color }, { VK_SHADER_STAGE_FRAGMENT_BIT, field_offset (alias_push_constants_t, fog), sizeof (constants.fog), &constants.fog }, }; if (0/*XXX ent->skin && ent->skin->tex*/) { //skin = ent->skin->tex; } else { maliasskindesc_t *skindesc; skindesc = R_AliasGetSkindesc (animation, renderer->skinnum, hdr); skin = (qfv_alias_skin_t *) ((byte *) hdr + skindesc->skin); } QuatCopy (renderer->colormod, constants.base_color); QuatCopy (skin->colors, constants.colors); if (Ent_HasComponent (ent.id, scene_colormap, ent.reg)) { colormap_t *colormap=Ent_GetComponent (ent.id, scene_colormap, ent.reg); constants.colors[0] = colormap->top * 16 + 8; constants.colors[1] = colormap->bottom * 16 + 8; } QuatZero (constants.fog); emit_commands (aframe->cmdSet.a[QFV_aliasDepth], animation->pose1, animation->pose2, 0, 2, push_constants, hdr, rFrame, ent); emit_commands (aframe->cmdSet.a[QFV_aliasGBuffer], animation->pose1, animation->pose2, skin, 5, push_constants, hdr, rFrame, ent); } static void alias_begin_subpass (QFV_AliasSubpass subpass, VkPipeline pipeline, qfv_orenderframe_t *rFrame) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; aliasctx_t *actx = ctx->alias_context; uint32_t curFrame = ctx->curFrame; aliasframe_t *aframe = &actx->frames.a[curFrame]; VkCommandBuffer cmd = aframe->cmdSet.a[subpass]; dfunc->vkResetCommandBuffer (cmd, 0); VkCommandBufferInheritanceInfo inherit = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, 0, rFrame->renderpass->renderpass, subpass_map[subpass], rFrame->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, "alias:%s", alias_pass_names[subpass]), { 0.6, 0.5, 0, 1}); dfunc->vkCmdBindPipeline (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); VkDescriptorSet sets[] = { Vulkan_Matrix_Descriptors (ctx, ctx->curFrame), Vulkan_Palette_Descriptor (ctx), }; dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, actx->layout, 0, 2, sets, 0, 0); dfunc->vkCmdSetViewport (cmd, 0, 1, &rFrame->renderpass->viewport); dfunc->vkCmdSetScissor (cmd, 0, 1, &rFrame->renderpass->scissor); //XXX glsl_Fog_GetColor (fog); //XXX fog[3] = glsl_Fog_GetDensity () / 64.0; } static void alias_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); } void Vulkan_AliasBegin (qfv_orenderframe_t *rFrame) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; aliasctx_t *actx = ctx->alias_context; aliasframe_t *aframe = &actx->frames.a[ctx->curFrame]; //XXX quat_t fog; DARRAY_APPEND (&rFrame->subpassCmdSets[QFV_passDepth], aframe->cmdSet.a[QFV_aliasDepth]); DARRAY_APPEND (&rFrame->subpassCmdSets[QFV_passGBuffer], aframe->cmdSet.a[QFV_aliasGBuffer]); alias_begin_subpass (QFV_aliasDepth, actx->depth, rFrame); alias_begin_subpass (QFV_aliasGBuffer, actx->gbuf, rFrame); } void Vulkan_AliasEnd (qfv_orenderframe_t *rFrame) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; aliasctx_t *actx = ctx->alias_context; aliasframe_t *aframe = &actx->frames.a[ctx->curFrame]; alias_end_subpass (aframe->cmdSet.a[QFV_aliasDepth], ctx); alias_end_subpass (aframe->cmdSet.a[QFV_aliasGBuffer], ctx); } void Vulkan_AliasDepthRange (qfv_orenderframe_t *rFrame, float minDepth, float maxDepth) { vulkan_ctx_t *ctx = rFrame->vulkan_ctx; qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; aliasctx_t *actx = ctx->alias_context; aliasframe_t *aframe = &actx->frames.a[ctx->curFrame]; VkViewport viewport = rFrame->renderpass->viewport; viewport.minDepth = minDepth; viewport.maxDepth = maxDepth; dfunc->vkCmdSetViewport (aframe->cmdSet.a[QFV_aliasDepth], 0, 1, &viewport); dfunc->vkCmdSetViewport (aframe->cmdSet.a[QFV_aliasGBuffer], 0, 1, &viewport); } void Vulkan_AliasAddSkin (vulkan_ctx_t *ctx, qfv_alias_skin_t *skin) { aliasctx_t *actx = ctx->alias_context; skin->descriptor = Vulkan_CreateCombinedImageSampler (ctx, skin->view, actx->sampler); } void Vulkan_AliasRemoveSkin (vulkan_ctx_t *ctx, qfv_alias_skin_t *skin) { Vulkan_FreeTexture (ctx, skin->descriptor); skin->descriptor = 0; } static void alias_draw (const exprval_t **params, exprval_t *result, exprctx_t *ectx) { } static exprfunc_t alias_draw_func[] = { { .func = alias_draw }, {} }; static exprsym_t alias_task_syms[] = { { "alias_draw", &cexpr_function, alias_draw_func }, {} }; void Vulkan_Alias_Init (vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; qfvPushDebug (ctx, "alias init"); QFV_Render_AddTasks (ctx, alias_task_syms); aliasctx_t *actx = calloc (1, sizeof (aliasctx_t)); ctx->alias_context = actx; auto rctx = ctx->render_context; size_t frames = rctx->frames.size; DARRAY_INIT (&actx->frames, frames); DARRAY_RESIZE (&actx->frames, frames); actx->frames.grow = 0; actx->depth = Vulkan_CreateGraphicsPipeline (ctx, "alias_depth"); actx->gbuf = Vulkan_CreateGraphicsPipeline (ctx, "alias_gbuf"); actx->layout = Vulkan_CreatePipelineLayout (ctx, "alias_layout"); actx->sampler = Vulkan_CreateSampler (ctx, "alias_sampler"); for (size_t i = 0; i < frames; i++) { __auto_type aframe = &actx->frames.a[i]; DARRAY_INIT (&aframe->cmdSet, QFV_aliasNumPasses); DARRAY_RESIZE (&aframe->cmdSet, QFV_aliasNumPasses); aframe->cmdSet.grow = 0; QFV_AllocateCommandBuffers (device, ctx->cmdpool, 1, &aframe->cmdSet); for (int j = 0; j < QFV_aliasNumPasses; j++) { QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER, aframe->cmdSet.a[j], va (ctx->va_ctx, "cmd:alias:%zd:%s", i, alias_pass_names[j])); } } qfvPopDebug (ctx); } void Vulkan_Alias_Shutdown (vulkan_ctx_t *ctx) { qfv_device_t *device = ctx->device; qfv_devfuncs_t *dfunc = device->funcs; aliasctx_t *actx = ctx->alias_context; for (size_t i = 0; i < actx->frames.size; i++) { __auto_type aframe = &actx->frames.a[i]; free (aframe->cmdSet.a); } dfunc->vkDestroyPipeline (device->dev, actx->depth, 0); dfunc->vkDestroyPipeline (device->dev, actx->gbuf, 0); free (actx->frames.a); free (actx); }