[vulkan] Use a buffer for entity transform and color data

This allows the use of an entity id to index into the entity data and
fetch the transform and colormod data in the vertex shader, thus making
instanced rendering possible. Non-world brush entities are still not
rendered, but the world entity is using both the entity data buffer and
entid buffer.
This commit is contained in:
Bill Currie 2022-05-25 00:17:57 +09:00
parent 3900e59f1a
commit a08261c620
19 changed files with 502 additions and 137 deletions

View file

@ -197,6 +197,7 @@ include_qf_vulkan = \
include/QF/Vulkan/qf_matrices.h \
include/QF/Vulkan/qf_model.h \
include/QF/Vulkan/qf_particles.h \
include/QF/Vulkan/qf_scene.h \
include/QF/Vulkan/qf_sprite.h \
include/QF/Vulkan/qf_texture.h \
include/QF/Vulkan/qf_vid.h \

View file

@ -99,6 +99,8 @@ typedef struct bsp_instfaceset_s
typedef struct bsp_pass_s {
uint32_t *indices; // points into index buffer
uint32_t index_count; // number of indices written to buffer
uint32_t *entid_data;
uint32_t entid_count;
int vis_frame;
int *face_frames;
int *leaf_frames;
@ -132,9 +134,12 @@ typedef enum {
} QFV_BspSubpass;
typedef struct bspframe_s {
uint32_t *index_data; // pointer into mega-buffer for this frame (c)
uint32_t index_offset; // offset of index_data within mega-buffer (c)
uint32_t index_count; // number if indices queued (d)
uint32_t *index_data; // pointer into mega-buffer for this frame (c)
uint32_t index_offset; // offset of index_data within mega-buffer (c)
uint32_t index_count; // number if indices queued (d)
uint32_t *entid_data;
uint32_t entid_offset;
uint32_t entid_count;
qfv_cmdbufferset_t cmdSet;
} bspframe_t;
@ -142,9 +147,6 @@ typedef struct bspframeset_s
DARRAY_TYPE (bspframe_t) bspframeset_t;
typedef struct bspctx_s {
struct entity_s *entity;
vec_t *transform;
float *color;
uint32_t inst_id;
regtexset_t registered_textures;
@ -182,6 +184,8 @@ typedef struct bspctx_s {
VkDeviceMemory vertex_memory;
VkBuffer index_buffer;
VkDeviceMemory index_memory;
VkBuffer entid_buffer;
VkDeviceMemory entid_memory;
bspframeset_t frames;
} bspctx_t;

View file

@ -0,0 +1,80 @@
/*
qf_scene.h
Vulkan specific scene stuff
Copyright (C) 2022 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2022/5/24
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
*/
#ifndef __QF_Vulkan_qf_scene_h
#define __QF_Vulkan_qf_scene_h
#ifndef VK_NO_PROTOTYPES
#define VK_NO_PROTOTYPES
#endif
#include <vulkan/vulkan.h>
#include "QF/darray.h"
#include "QF/simd/types.h"
typedef struct entdata_s {
// transpose of entity transform matrix without row 4
vec4f_t xform[3];
vec4f_t color;
} entdata_t;
typedef struct entdataset_s
DARRAY_TYPE (entdata_t) entdataset_t;
typedef struct scnframe_s {
// used to check if the entity has been pooled this frame (cleared
// every frame)
struct set_s *pooled_entities;
// data for entities pooled this frame (cleared every frame). transfered
// to gpu
entdataset_t entity_pool;
VkDescriptorSet descriptors;
} scnframe_t;
typedef struct scnframeset_s
DARRAY_TYPE (scnframe_t) scnframeset_t;
typedef struct scenectx_s {
struct qfv_resource_s *entities;
scnframeset_t frames;
VkDescriptorPool pool;
VkDescriptorSetLayout setLayout;
int max_entities;
} scenectx_t;
struct vulkan_ctx_s;
struct entity_s;
void Vulkan_Scene_Init (struct vulkan_ctx_s *ctx);
void Vulkan_Scene_Shutdown (struct vulkan_ctx_s *ctx);
int Vulkan_Scene_MaxEntities (struct vulkan_ctx_s *ctx) __attribute__((pure));
VkDescriptorSet Vulkan_Scene_Descriptors (struct vulkan_ctx_s *ctx) __attribute__((pure));
int Vulkan_Scene_AddEntity (struct vulkan_ctx_s *ctx, struct entity_s *entity);
#endif//__QF_Vulkan_qf_scene_h

View file

@ -73,6 +73,7 @@ typedef struct renderer_s {
int skinnum; // for Alias models
int fullbright;
float min_light;
int render_id;
mat4_t full_transform;
} renderer_t;

View file

@ -68,6 +68,7 @@ typedef struct vulkan_ctx_s {
struct aliasctx_s *alias_context;
struct bspctx_s *bsp_context;
struct iqmctx_s *iqm_context;
struct scenectx_s *scene_context;
struct particlectx_s *particle_context;
struct spritectx_s *sprite_context;
struct drawctx_s *draw_context;

View file

@ -227,6 +227,7 @@ libs_video_renderer_librender_vulkan_la_SOURCES = \
libs/video/renderer/vulkan/vulkan_main.c \
libs/video/renderer/vulkan/vulkan_matrices.c \
libs/video/renderer/vulkan/vulkan_particles.c \
libs/video/renderer/vulkan/vulkan_scene.c \
libs/video/renderer/vulkan/vulkan_sprite.c \
libs/video/renderer/vulkan/vulkan_texture.c \
libs/video/renderer/vulkan/vulkan_vid_common.c

View file

@ -50,6 +50,7 @@
#include "QF/Vulkan/qf_main.h"
#include "QF/Vulkan/qf_matrices.h"
#include "QF/Vulkan/qf_particles.h"
#include "QF/Vulkan/qf_scene.h"
#include "QF/Vulkan/qf_sprite.h"
#include "QF/Vulkan/qf_texture.h"
#include "QF/Vulkan/qf_vid.h"
@ -92,6 +93,7 @@ vulkan_R_Init (void)
Vulkan_Texture_Init (vulkan_ctx);
Vulkan_Matrix_Init (vulkan_ctx);
Vulkan_Scene_Init (vulkan_ctx);
Vulkan_Alias_Init (vulkan_ctx);
Vulkan_Bsp_Init (vulkan_ctx);
Vulkan_IQM_Init (vulkan_ctx);
@ -705,6 +707,7 @@ vulkan_vid_render_shutdown (void)
Vulkan_IQM_Shutdown (vulkan_ctx);
Vulkan_Bsp_Shutdown (vulkan_ctx);
Vulkan_Alias_Shutdown (vulkan_ctx);
Vulkan_Scene_Shutdown (vulkan_ctx);
Vulkan_Matrix_Shutdown (vulkan_ctx);
Vulkan_Texture_Shutdown (vulkan_ctx);

View file

@ -97,6 +97,16 @@
},
);
};
entity_pool = {
flags = free_descriptor_set;
maxSets = 512;
bindings = (
{
type = storage_buffer;
descriptorCount = $properties.descriptorPools.entity_pool.maxSets;
},
);
};
bone_pool = {
flags = free_descriptor_set;
maxSets = 512;
@ -216,6 +226,16 @@
},
);
};
entity_set = {
bindings = (
{
binding = 0;
descriptorType = storage_buffer;
descriptorCount = 1;
stageFlags = vertex;
},
);
};
bone_set = {
bindings = (
{
@ -340,16 +360,11 @@
setLayouts = (matrix_set, twod_set);
};
quakebsp_layout = {
setLayouts = (matrix_set, texture_set, texture_set);
setLayouts = (matrix_set, entity_set, texture_set, texture_set);
pushConstantRanges = (
{
stageFlags = vertex;
offset = 0;
size = "16 * 4";
},
{
stageFlags = fragment;
offset = 64;
offset = 0;
size = "4 * 4 + 4 + 4";
},
);
@ -499,10 +514,12 @@
brush = {
bindings = (
{ binding = 0; stride = "2 * 4 * 4"; inputRate = vertex; },
{ binding = 1; stride = "4"; inputRate = instance; },
);
attributes = (
{ location = 0; binding = 0; format = r32g32b32a32_sfloat; offset = 0; },
{ location = 1; binding = 0; format = r32g32b32a32_sfloat; offset = 16; },
{ location = 2; binding = 1; format = r32_uint; offset = 0; },
);
};
iqm = {
@ -786,9 +803,11 @@
vertexInput = {
bindings = (
"$properties.vertexInput.brush.bindings[0]",
"$properties.vertexInput.brush.bindings[1]",
);
attributes = (
"$properties.vertexInput.brush.attributes[0]",
"$properties.vertexInput.brush.attributes[2]",
);
};
inputAssembly = $properties.inputAssembly.brush;

View file

@ -1,4 +1,7 @@
#version 450
#extension GL_GOOGLE_include_directive : enable
#include "entity.h"
layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection3d;
@ -7,14 +10,16 @@ layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection2d;
};
layout (push_constant) uniform PushConstants {
mat4 Model;
layout (set = 1, binding = 0) buffer Entities {
Entity entities[];
};
layout (location = 0) in vec4 vertex;
layout (location = 2) in uint entid;
void
main (void)
{
gl_Position = Projection3d * (View * (Model * vertex));
vec3 vert = vertex * entities[entid].transform;
gl_Position = Projection3d * (View * vec4 (vert, 1));
}

View file

@ -1,9 +1,8 @@
#version 450
layout (set = 1, binding = 0) uniform sampler2DArray Texture;
layout (set = 2, binding = 0) uniform sampler2DArray Texture;
layout (push_constant) uniform PushConstants {
layout (offset = 64)
vec4 fog;
float time;
float alpha;

View file

@ -1,4 +1,7 @@
#version 450
#extension GL_GOOGLE_include_directive : enable
#include "entity.h"
layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection3d;
@ -7,12 +10,13 @@ layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection2d;
};
layout (push_constant) uniform PushConstants {
mat4 Model;
layout (set = 1, binding = 0) buffer Entities {
Entity entities[];
};
layout (location = 0) in vec4 vertex;
layout (location = 1) in vec4 tl_uv;
layout (location = 2) in uint entid;
layout (location = 0) out vec4 tl_st;
layout (location = 1) out vec3 direction;
@ -21,7 +25,8 @@ void
main (void)
{
// geometry shader will take care of Projection and View
gl_Position = Model * vertex;
vec3 vert = vertex * entities[entid].transform;
gl_Position = vec4 (vert, 1);
direction = (Sky * vertex).xyz;
tl_st = tl_uv;
}

View file

@ -1,16 +1,21 @@
#version 450
#extension GL_GOOGLE_include_directive : enable
layout (push_constant) uniform PushConstants {
mat4 Model;
#include "entity.h"
layout (set = 1, binding = 0) buffer Entities {
Entity entities[];
};
layout (location = 0) in vec4 vertex;
layout (location = 2) in uint entid;
layout (location = 0) out int InstanceIndex;
void
main (void)
{
gl_Position = Model * vertex;
vec3 vert = vertex * entities[entid].transform;
gl_Position = vec4 (vert, 1);;
InstanceIndex = gl_InstanceIndex;
}

View file

@ -3,11 +3,10 @@
layout (constant_id = 0) const bool doSkyBox = false;
layout (constant_id = 1) const bool doSkySheet = false;
layout (set = 1, binding = 0) uniform sampler2DArray SkySheet;
layout (set = 2, binding = 0) uniform samplerCube SkyBox;
layout (set = 2, binding = 0) uniform sampler2DArray SkySheet;
layout (set = 3, binding = 0) uniform samplerCube SkyBox;
layout (push_constant) uniform PushConstants {
layout (offset = 64)
vec4 fog;
float time;
float alpha;

View file

@ -1,9 +1,8 @@
#version 450
layout (set = 1, binding = 0) uniform sampler2DArray Texture;
layout (set = 2, binding = 0) uniform sampler2DArray Texture;
layout (push_constant) uniform PushConstants {
layout (offset = 64)
vec4 fog;
float time;
float alpha;

View file

@ -0,0 +1,4 @@
struct Entity {
mat3x4 transform;
vec4 color;
};

View file

@ -7,7 +7,6 @@ layout (set = 0, binding = 4) uniform sampler2DArray SkySheet;
layout (set = 0, binding = 5) uniform samplerCube SkyCube;
layout (push_constant) uniform PushConstants {
layout (offset = 64)
vec4 fog;
float time;
float alpha;

View file

@ -1,4 +1,7 @@
#version 450
#extension GL_GOOGLE_include_directive : enable
#include "entity.h"
layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection3d;
@ -7,12 +10,13 @@ layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection2d;
};
layout (push_constant) uniform PushConstants {
mat4 Model;
layout (set = 1, binding = 0) buffer Entities {
Entity entities[];
};
layout (location = 0) in vec4 vertex;
layout (location = 1) in vec4 tl_uv;
layout (location = 2) in uint entid;
layout (location = 0) out vec4 tl_st;
layout (location = 1) out vec3 direction;
@ -20,7 +24,8 @@ layout (location = 1) out vec3 direction;
void
main (void)
{
gl_Position = Projection3d * (View * (Model * vertex));
vec3 vert = vertex * entities[entid].transform;
gl_Position = Projection3d * (View * vec4 (vert, 1));
direction = (Sky * vertex).xyz;
tl_st = tl_uv;
}

View file

@ -55,6 +55,7 @@
#include "QF/Vulkan/qf_bsp.h"
#include "QF/Vulkan/qf_lightmap.h"
#include "QF/Vulkan/qf_matrices.h"
#include "QF/Vulkan/qf_scene.h"
#include "QF/Vulkan/qf_texture.h"
#include "QF/Vulkan/buffer.h"
#include "QF/Vulkan/barrier.h"
@ -74,7 +75,6 @@
#include "vid_vulkan.h"
typedef struct bsp_push_constants_s {
mat4f_t Model;
quat_t fog;
float time;
float alpha;
@ -94,13 +94,6 @@ static QFV_Subpass subpass_map[] = {
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
@ -486,11 +479,7 @@ R_DrawBrushModel (entity_t *e, vulkan_ctx_t *ctx)
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;
//bspctx_t *bctx = ctx->bsp_context;
model = e->renderer.model;
brush = &model->brush;
@ -660,18 +649,6 @@ R_VisitWorldNodes (mod_brush_t *brush, vulkan_ctx_t *ctx)
}
}
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)
@ -737,16 +714,18 @@ bsp_begin_subpass (QFV_BspSubpass subpass, VkPipeline 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);
VkBuffer buffers[] = { bctx->vertex_buffer, bctx->entid_buffer };
VkDeviceSize offsets[] = { 0, bframe->entid_offset };
dfunc->vkCmdBindVertexBuffers (cmd, 0, 2, buffers, offsets);
dfunc->vkCmdBindIndexBuffer (cmd, bctx->index_buffer, bframe->index_offset,
VK_INDEX_TYPE_UINT32);
VkDescriptorSet sets[] = {
Vulkan_Matrix_Descriptors (ctx, ctx->curFrame),
Vulkan_Scene_Descriptors (ctx),
};
dfunc->vkCmdBindDescriptorSets (cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
layout, 0, 1, sets, 0, 0);
layout, 0, 2, sets, 0, 0);
//XXX glsl_Fog_GetColor (fog);
//XXX fog[3] = glsl_Fog_GetDensity () / 64.0;
@ -869,70 +848,9 @@ clear_queues (bspctx_t *bctx, bsp_pass_t *pass)
}
static void
draw_queue (bsp_pass_t *pass, int queue, VkPipelineLayout layout,
qfv_device_t *device, VkCommandBuffer cmd)
queue_faces (bsp_pass_t *pass, bspctx_t *bctx, bspframe_t *bframe)
{
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;
pass->indices = bframe->index_data + bframe->index_count;
for (size_t i = 0; i < bctx->registered_textures.size; i++) {
__auto_type queue = &pass->face_queue[i];
if (!queue->size) {
@ -962,7 +880,88 @@ Vulkan_DrawWorld (qfv_renderframe_t *rFrame)
}
DARRAY_APPEND (&pass->draw_queues[dq], draw);
}
bframe->index_count += pass->index_count;
}
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, 2, layout, dfunc, cmd);
}
dfunc->vkCmdDrawIndexed (cmd, d.index_count, d.instance_count,
d.first_index, 0, d.first_instance);
}
}
static int
add_entity (vulkan_ctx_t *ctx, bsp_pass_t *pass, entity_t *entity)
{
int entid = Vulkan_Scene_AddEntity (ctx, entity);
if (entid >= 0) {
pass->entid_data[pass->entid_count++] = entid;
}
return entid >= 0;
}
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];
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;
bctx->main_pass.entid_data = bframe->entid_data;
bctx->main_pass.entid_count = 0;
clear_queues (bctx, &bctx->main_pass); // do this first for water and skys
bframe->index_count = 0;
entity_t worldent = {
.renderer = {
.model = r_refdef.worldmodel,
.colormod = { 1, 1, 1, 1 },
},
};
brush = &r_refdef.worldmodel->brush;
add_entity (ctx, &bctx->main_pass, &worldent);
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];
if (add_entity (ctx, &bctx->main_pass, ent)) {
R_DrawBrushModel (ent, ctx);
}
}
}
queue_faces (&bctx->main_pass, bctx, bframe);
bsp_begin (rFrame);
bsp_push_constants_t frag_constants = { .time = vr_data.realtime };
push_fragconst (&frag_constants, bctx->layout, device,
bframe->cmdSet.a[QFV_bspGBuffer]);
VkPipelineLayout layout = bctx->layout;
__auto_type pass = &bctx->main_pass;
pass->textures = 0;
draw_queue (pass, 0, layout, device, bframe->cmdSet.a[QFV_bspDepth]);
pass->textures = &bctx->registered_textures;
@ -979,20 +978,28 @@ Vulkan_Bsp_Flush (vulkan_ctx_t *ctx)
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);
size_t index_offset = bframe->index_offset;
size_t index_size = bframe->index_count * sizeof (uint32_t);
size_t entid_offset = bframe->entid_offset;
size_t entid_size = bframe->entid_count * sizeof (uint32_t);
if (!bframe->index_count) {
return;
}
offset &= ~atom_mask;
size = (size + atom_mask) & ~atom_mask;
index_offset &= ~atom_mask;
index_size = (index_size + atom_mask) & ~atom_mask;
entid_offset &= ~atom_mask;
entid_size = (entid_size + atom_mask) & ~atom_mask;
VkMappedMemoryRange range = {
VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
bctx->index_memory, offset, size
VkMappedMemoryRange ranges[] = {
{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
bctx->index_memory, index_offset, index_size
},
{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
bctx->entid_memory, entid_offset, entid_size
},
};
dfunc->vkFlushMappedMemoryRanges (device->dev, 1, &range);
dfunc->vkFlushMappedMemoryRanges (device->dev, 2, ranges);
}
void
@ -1008,8 +1015,6 @@ Vulkan_DrawWaterSurfaces (qfv_renderframe_t *rFrame)
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
@ -1039,10 +1044,8 @@ Vulkan_DrawSky (qfv_renderframe_t *rFrame)
sky_begin (rFrame);
vulktex_t skybox = { .descriptor = bctx->skybox_descriptor };
bind_texture (&skybox, 2, bctx->layout, dfunc,
bind_texture (&skybox, 3, 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]);
@ -1166,6 +1169,7 @@ void
Vulkan_Bsp_Init (vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
r_notexture_mip->render = &vulkan_notexture;
@ -1204,6 +1208,28 @@ Vulkan_Bsp_Init (vulkan_ctx_t *ctx)
bctx->layout = Vulkan_CreatePipelineLayout (ctx, "quakebsp_layout");
bctx->sampler = Vulkan_CreateSampler (ctx, "quakebsp_sampler");
size_t entid_size = Vulkan_Scene_MaxEntities (ctx) * sizeof (uint32_t);
size_t atom = device->physDev->properties.limits.nonCoherentAtomSize;
size_t atom_mask = atom - 1;
entid_size = (entid_size + atom_mask) & ~atom_mask;
bctx->entid_buffer
= QFV_CreateBuffer (device, frames * entid_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_BUFFER, bctx->entid_buffer,
"buffer:bsp:entid");
bctx->entid_memory
= QFV_AllocBufferMemory (device, bctx->entid_buffer,
VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
frames * entid_size, 0);
QFV_duSetObjectName (device, VK_OBJECT_TYPE_DEVICE_MEMORY,
bctx->entid_memory, "memory:bsp:entid");
QFV_BindBufferMemory (device,
bctx->entid_buffer, bctx->entid_memory, 0);
uint32_t *entid_data;
dfunc->vkMapMemory (device->dev, bctx->entid_memory, 0,
frames * entid_size, 0, (void **) &entid_data);
for (size_t i = 0; i < frames; i++) {
__auto_type bframe = &bctx->frames.a[i];
@ -1219,6 +1245,8 @@ Vulkan_Bsp_Init (vulkan_ctx_t *ctx)
va (ctx->va_ctx, "cmd:bsp:%zd:%s", i,
bsp_pass_names[j]));
}
bframe->entid_data = entid_data + i * entid_size;
bframe->entid_offset = i * entid_size;
}
bctx->skybox_descriptor
@ -1261,6 +1289,8 @@ Vulkan_Bsp_Shutdown (struct vulkan_ctx_s *ctx)
dfunc->vkDestroyBuffer (device->dev, bctx->index_buffer, 0);
dfunc->vkFreeMemory (device->dev, bctx->index_memory, 0);
}
dfunc->vkDestroyBuffer (device->dev, bctx->entid_buffer, 0);
dfunc->vkFreeMemory (device->dev, bctx->entid_memory, 0);
if (bctx->skybox_tex) {
Vulkan_UnloadTex (ctx, bctx->skybox_tex);

View file

@ -0,0 +1,205 @@
/*
vulkan_scene.c
Vulkan scene handling
Copyright (C) 2022 Bill Currie <bill@taniwha.org>
Author: Bill Currie <bill@taniwha.org>
Date: 2022/5/24
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 <string.h>
#include "QF/mathlib.h"
#include "QF/set.h"
#include "QF/va.h"
#include "QF/scene/entity.h"
#include "QF/Vulkan/qf_scene.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/descriptor.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/instance.h"
#include "QF/Vulkan/resource.h"
#include "vid_vulkan.h"
static const int qfv_max_entities = 4096; //FIXME should make dynamic
int
Vulkan_Scene_MaxEntities (vulkan_ctx_t *ctx)
{
scenectx_t *sctx = ctx->scene_context;
return sctx->max_entities;
}
VkDescriptorSet
Vulkan_Scene_Descriptors (vulkan_ctx_t *ctx)
{
scenectx_t *sctx = ctx->scene_context;
scnframe_t *sframe = &sctx->frames.a[ctx->curFrame];
return sframe->descriptors;
}
int
Vulkan_Scene_AddEntity (vulkan_ctx_t *ctx, entity_t *entity)
{
scenectx_t *sctx = ctx->scene_context;
scnframe_t *sframe = &sctx->frames.a[ctx->curFrame];
entdata_t *entdata = 0;
//lock
int id = -entity->id;
if (!set_is_member (sframe->pooled_entities, id)) {
if (sframe->entity_pool.size < sframe->entity_pool.maxSize) {
set_add (sframe->pooled_entities, id);
entity->renderer.render_id = sframe->entity_pool.size++;
entdata = sframe->entity_pool.a + entity->renderer.render_id;
} else {
entity->renderer.render_id = -1;
}
}
//unlock
if (entdata) {
mat4f_t f;
if (entity->transform) { //FIXME give world entity an entity :P
mat4ftranspose (f, Transform_GetWorldMatrixPtr (entity->transform));
entdata->xform[0] = f[0];
entdata->xform[1] = f[1];
entdata->xform[2] = f[2];
} else {
entdata->xform[0] = (vec4f_t) { 1, 0, 0, 0 };
entdata->xform[1] = (vec4f_t) { 0, 1, 0, 0 };
entdata->xform[2] = (vec4f_t) { 0, 0, 1, 0 };
}
entdata->color = (vec4f_t) { QuatExpand (entity->renderer.colormod) };
}
return entity->renderer.render_id;
}
static VkWriteDescriptorSet base_buffer_write = {
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0, 0,
0, 0, 1,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
0, 0, 0
};
void
Vulkan_Scene_Init (vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
qfvPushDebug (ctx, "scene init");
scenectx_t *sctx = calloc (1, sizeof (scenectx_t)
+ sizeof (qfv_resource_t)
+ sizeof (qfv_resobj_t));
ctx->scene_context = sctx;
sctx->max_entities = qfv_max_entities;
size_t frames = ctx->frames.size;
DARRAY_INIT (&sctx->frames, frames);
DARRAY_RESIZE (&sctx->frames, frames);
sctx->frames.grow = 0;
sctx->entities = (qfv_resource_t *) &sctx[1];
*sctx->entities = (qfv_resource_t) {
.name = "scene",
.va_ctx = ctx->va_ctx,
.memory_properties = VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
.num_objects = 1,
.objects = (qfv_resobj_t *) &sctx->entities[1],
};
sctx->entities->objects[0] = (qfv_resobj_t) {
.name = "entities",
.type = qfv_res_buffer,
.buffer = {
.size = frames * qfv_max_entities * sizeof (entdata_t),
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
},
};
QFV_CreateResource (device, sctx->entities);
sctx->pool = Vulkan_CreateDescriptorPool (ctx, "entity_pool");
sctx->setLayout = Vulkan_CreateDescriptorSetLayout (ctx, "entity_set");
__auto_type layouts = QFV_AllocDescriptorSetLayoutSet (frames, alloca);
for (size_t i = 0; i < layouts->size; i++) {
layouts->a[i] = sctx->setLayout;
}
__auto_type sets = QFV_AllocateDescriptorSet (device, sctx->pool, layouts);
entdata_t *entdata;
dfunc->vkMapMemory (device->dev, sctx->entities->memory, 0, VK_WHOLE_SIZE,
0, (void **) &entdata);
VkBuffer buffer = sctx->entities->objects[0].buffer.buffer;
size_t entdata_size = qfv_max_entities * sizeof (entdata_t);
for (size_t i = 0; i < frames; i++) {
__auto_type sframe = &sctx->frames.a[i];
sframe->descriptors = sets->a[i];
VkDescriptorBufferInfo bufferInfo = {
buffer, i * entdata_size, entdata_size
};
VkWriteDescriptorSet write[1];
write[0] = base_buffer_write;
write[0].dstSet = sframe->descriptors;
write[0].pBufferInfo = &bufferInfo;
dfunc->vkUpdateDescriptorSets (device->dev, 1, write, 0, 0);
sframe->entity_pool = (entdataset_t) {
.maxSize = qfv_max_entities,
.a = entdata + i * qfv_max_entities,
};
sframe->pooled_entities = set_new ();
}
free (sets);
qfvPopDebug (ctx);
}
void
Vulkan_Scene_Shutdown (vulkan_ctx_t *ctx)
{
qfvPushDebug (ctx, "scene shutdown");
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
scenectx_t *sctx = ctx->scene_context;
for (size_t i = 0; i < sctx->frames.size; i++) {
__auto_type sframe = &sctx->frames.a[i];
set_delete (sframe->pooled_entities);
}
dfunc->vkUnmapMemory (device->dev, sctx->entities->memory);
QFV_DestroyResource (device, sctx->entities);
free (sctx->frames.a);
free (sctx);
qfvPopDebug (ctx);
}