[vulkan] Start work on particles for Vulkan

This gets the pipelines loaded (and unloaded on shutdown). Probably the
easy part :P. Still need to sort out the command buffers,
synchronization, and particle generation (and probably a bunch else
that's not coming to mind).
This commit is contained in:
Bill Currie 2021-12-16 23:17:01 +09:00
parent 9dcaa98205
commit 6aaf5c3722
12 changed files with 544 additions and 25 deletions

View file

@ -1,8 +1,62 @@
#ifndef __QF_Vulkan_qf_particles_h
#define __QF_Vulkan_qf_particles_h
#include "QF/darray.h"
#include "QF/image.h"
#include "QF/Vulkan/command.h"
typedef struct qfv_particle_s {
vec4f_t pos;
vec4f_t vel;
vec4f_t color;
float texture;
float ramp;
float scale;
float live;
} qfv_particle_t;
typedef struct qfv_parameters_s {
vec4f_t drag;
vec4f_t ramp;
} qfv_parameters_t;
typedef enum {
QFV_particleTranslucent,
QFV_particleNumPasses
} QFV_ParticleSubpass;
typedef struct particleframe_s {
VkCommandBuffer compute;
VkSemaphore physSem;
VkSemaphore drawSem;
VkSemaphore updateSem;
VkBuffer state;
VkBuffer params;
VkBuffer system;
VkDescriptorSet descriptors;
qfv_cmdbufferset_t cmdSet;
} particleframe_t;
typedef struct particleframeset_s
DARRAY_TYPE (particleframe_t) particleframeset_t;
typedef struct particlectx_s {
particleframeset_t frames;
VkPipeline physics;
VkPipeline update;
VkPipeline draw;
VkDescriptorPool pool;
VkDescriptorSetLayout setLayout;
VkPipelineLayout physics_layout;
VkPipelineLayout update_layout;
VkPipelineLayout draw_layout;
} particlectx_t;
struct cvar_s;
struct vulkan_ctx_s;;
@ -11,6 +65,7 @@ void Vulkan_InitParticles (struct vulkan_ctx_s *ctx);
void Vulkan_r_easter_eggs_f (struct cvar_s *var, struct vulkan_ctx_s *ctx);
void Vulkan_r_particles_style_f (struct cvar_s *var, struct vulkan_ctx_s *ctx);
void Vulkan_Particles_Init (struct vulkan_ctx_s *ctx);
void Vulkan_Particles_Shutdown (struct vulkan_ctx_s *ctx);
void Vulkan_DrawParticles (struct vulkan_ctx_s *ctx);
#endif//__QF_Vulkan_qf_particles_h

View file

@ -59,6 +59,7 @@ typedef struct vulkan_ctx_s {
struct matrixctx_s *matrix_context;
struct aliasctx_s *alias_context;
struct bspctx_s *bsp_context;
struct particlectx_s *particle_context;
struct spritectx_s *sprite_context;
struct drawctx_s *draw_context;
struct lightingctx_s *lighting_context;

View file

@ -289,6 +289,16 @@ vkparse_plist = \
vkshaderpath = libs/video/renderer/vulkan/shader
partphysicsc_src = $(vkshaderpath)/partphysics.comp
partphysicsc_c = $(vkshaderpath)/partphysics.comp.spvc
partupdatec_src = $(vkshaderpath)/partupdate.comp
partupdatec_c = $(vkshaderpath)/partupdate.comp.spvc
particlev_src = $(vkshaderpath)/particle.vert
particlev_c = $(vkshaderpath)/particle.vert.spvc
particleg_src = $(vkshaderpath)/particle.geom
particleg_c = $(vkshaderpath)/particle.geom.spvc
particlef_src = $(vkshaderpath)/particle.frag
particlef_c = $(vkshaderpath)/particle.frag.spvc
sprite_gbufv_src = $(vkshaderpath)/sprite_gbuf.vert
sprite_gbufv_c = $(vkshaderpath)/sprite_gbuf.vert.spvc
sprite_gbuff_src = $(vkshaderpath)/sprite_gbuf.frag
@ -341,6 +351,12 @@ pushcolor_c = $(vkshaderpath)/pushcolor.frag.spvc
shadow_src = $(vkshaderpath)/shadow.geom
shadow_c = $(vkshaderpath)/shadow.geom.spvc
$(partphysicsc_c): $(partphysicsc_src)
$(partupdatec_c): $(partupdatec_src)
$(particlev_c): $(particlev_src)
$(particleg_c): $(particleg_src)
$(particlef_c): $(particlef_src)
$(sprite_gbufv_c): $(sprite_gbufv_src)
$(sprite_gbuff_c): $(sprite_gbuff_src)
@ -392,6 +408,11 @@ $(pushcolor_c): $(pushcolor_src)
$(shadow_c): $(shadow_src)
vkshader_c = \
$(partphysicsc_c) \
$(partupdatec_c) \
$(particlev_c) \
$(particleg_c) \
$(particlef_c) \
$(sprite_gbufv_c) \
$(sprite_gbuff_c) \
$(sprite_depthv_c) \

View file

@ -104,9 +104,9 @@ vulkan_R_Init (void)
Vulkan_Matrix_Init (vulkan_ctx);
Vulkan_Alias_Init (vulkan_ctx);
Vulkan_Bsp_Init (vulkan_ctx);
Vulkan_Particles_Init (vulkan_ctx);
Vulkan_Sprite_Init (vulkan_ctx);
Vulkan_Draw_Init (vulkan_ctx);
Vulkan_Particles_Init (vulkan_ctx);
Vulkan_Lighting_Init (vulkan_ctx);
Vulkan_Compose_Init (vulkan_ctx);
@ -631,6 +631,7 @@ vulkan_vid_render_shutdown (void)
Vulkan_Lighting_Shutdown (vulkan_ctx);
Vulkan_Draw_Shutdown (vulkan_ctx);
Vulkan_Sprite_Shutdown (vulkan_ctx);
Vulkan_Particles_Shutdown (vulkan_ctx);
Vulkan_Bsp_Shutdown (vulkan_ctx);
Vulkan_Alias_Shutdown (vulkan_ctx);
Vulkan_Matrix_Shutdown (vulkan_ctx);

View file

@ -112,6 +112,16 @@
},
);
};
particle_pool = {
flags = 0;
maxSets = $frames.size;
bindings = (
{
type = storage_buffer;
descriptorCount = 3;
},
);
};
sprite_pool = {
flags = free_descriptor_set;
maxSets = 64; //FIXME cvar?
@ -295,6 +305,28 @@
},
);
};
particle_set = {
bindings = (
{
binding = 0;
descriptorType = storage_buffer;
descriptorCount = 1;
stageFlags = compute;
},
{
binding = 1;
descriptorType = storage_buffer;
descriptorCount = 1;
stageFlags = compute;
},
{
binding = 2;
descriptorType = storage_buffer;
descriptorCount = 1;
stageFlags = compute;
},
);
};
};
pipelineLayouts = {
twod_layout = {
@ -351,6 +383,29 @@
compose_layout = {
setLayouts = (compose_attach);
};
partphysics_layout = {
setLayouts = (particle_set);
pushConstantRanges = (
{
stageFlags = compute;
offset = 0;
size = "16 * 4 + 4";
},
);
};
partupdate_layout = {
setLayouts = (particle_set, particle_set, particle_set);
};
partdraw_layout = {
setLayouts = (matrix_set);
pushConstantRanges = (
{
stageFlags = vertex;
offset = 0;
size = "16 * 4";
},
);
};
};
depthStencil = {
@ -424,6 +479,17 @@
{ location = 1; binding = 0; format = r32g32b32a32_sfloat; offset = 16; },
);
};
particle = {
bindings = (
{ binding = 0; stride = "4 * 4 * 4"; inputRate = vertex; },
);
attributes = (
{ location = 0; binding = 0; format = r32g32b32a32_sfloat; offset = 0; },
{ location = 1; binding = 0; format = r32g32b32a32_sfloat; offset = 16; },
{ location = 2; binding = 0; format = r32g32b32a32_sfloat; offset = 16; },
{ location = 3; binding = 0; format = r32g32b32a32_sfloat; offset = 16; },
);
};
twod = {
bindings = (
{ binding = 0; stride = "2 * 4 * 4"; inputRate = vertex; },
@ -856,6 +922,38 @@
layout = quakebsp_layout;
renderPass = renderpass;
};
partdraw = {
subpass = 1;
stages = (
{
stage = vertex;
name = main;
module = $builtin/particle.vert;
},
{
stage = geometry;
name = main;
module = $builtin/particle.geom;
},
{
stage = fragment;
name = main;
module = $builtin/particle.frag;
},
);
vertexInput = $properties.vertexInput.particle;
inputAssembly = $properties.inputAssembly.sprite;
viewport = $properties.viewport;
rasterization = $properties.rasterization.cw_cull_back;
multisample = $properties.multisample;
depthStencil = $properties.depthStencil.test_only;
colorBlend = $properties.pipelines.bsp_turb.colorBlend;
dynamic = {
dynamicState = ( viewport, scissor, blend_constants );
};
layout = partdraw_layout;
renderPass = renderpass;
};
sprite_gbuf = {
subpass = 2;
stages = (
@ -1000,5 +1098,21 @@
layout = compose_layout;
renderPass = renderpass;
};
partphysics = {
stage = {
stage = compute;
name = main;
module = $builtin/partphysics.comp;
};
layout = partphysics_layout;
};
partupdate = {
stage = {
stage = compute;
name = main;
module = $builtin/partupdate.comp;
};
layout = partupdate_layout;
};
};
}

View file

@ -54,6 +54,16 @@
#include "vid_vulkan.h"
static
#include "libs/video/renderer/vulkan/shader/particle.vert.spvc"
static
#include "libs/video/renderer/vulkan/shader/particle.geom.spvc"
static
#include "libs/video/renderer/vulkan/shader/particle.frag.spvc"
static
#include "libs/video/renderer/vulkan/shader/partphysics.comp.spvc"
static
#include "libs/video/renderer/vulkan/shader/partupdate.comp.spvc"
static
#include "libs/video/renderer/vulkan/shader/sprite_gbuf.vert.spvc"
static
@ -112,6 +122,11 @@ typedef struct shaderdata_s {
} shaderdata_t;
static shaderdata_t builtin_shaders[] = {
{ "particle.vert", particle_vert, sizeof (particle_vert) },
{ "particle.geom", particle_geom, sizeof (particle_geom) },
{ "particle.frag", particle_frag, sizeof (particle_frag) },
{ "partphysics.comp", partphysics_comp, sizeof (partphysics_comp) },
{ "partupdate.comp", partupdate_comp, sizeof (partupdate_comp) },
{ "sprite_gbuf.vert", sprite_gbuf_vert, sizeof (sprite_gbuf_vert) },
{ "sprite_gbuf.frag", sprite_gbuf_frag, sizeof (sprite_gbuf_frag) },
{ "sprite_depth.vert", sprite_depth_vert, sizeof (sprite_depth_vert) },

View file

@ -0,0 +1,22 @@
#version 450
layout (constant_id = 0) const int MaxTextures = 256;
layout (location = 0) in vec4 uv_tr;
layout (location = 1) in vec4 color;
layout (location = 0) out vec4 frag_color;
void
main (void)
{
vec4 c = color;
vec2 x = uv_tr.xy;
float a = 1 - dot (x, x);
if (a <= 0) {
discard;
}
c.a *= sqrt (a);
frag_color = c;
}

View file

@ -0,0 +1,57 @@
#version 450
layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection3d;
mat4 View;
mat4 Sky;
mat4 Projection2d;
};
layout (points) in;
layout (triangle_strip, max_vertices = 4) out;
layout (location = 0) in vec4 velocity[];
layout (location = 1) in vec4 color[];
layout (location = 2) in vec4 ramp[];
layout (location = 0) out vec4 uv_tr;
layout (location = 1) out vec4 o_color;
void
main()
{
vec4 pos = gl_in[0].gl_Position;
vec4 tr = vec4 (0, 0, ramp[0].xy);
float s = ramp[0].z;
vec4 d, p;
vec4 c = color[0];
d = vec4 (-1, 1, 0, 0);
p = pos + s * d;
gl_Position = Projection3d * p;
uv_tr = d + tr;
o_color = c;
EmitVertex ();
d = vec4 (-1, -1, 0, 0);
p = pos + s * d;
gl_Position = Projection3d * p;
uv_tr = d + tr;
o_color = c;
EmitVertex ();
d = vec4 (1, 1, 0, 0);
p = pos + s * d;
gl_Position = Projection3d * p;
uv_tr = d + tr;
o_color = c;
EmitVertex ();
d = vec4 (1, -1, 0, 0);
p = pos + s * d;
gl_Position = Projection3d * p;
uv_tr = d + tr;
o_color = c;
EmitVertex ();
EndPrimitive ();
}

View file

@ -0,0 +1,31 @@
#version 450
layout (set = 0, binding = 0) uniform Matrices {
mat4 Projection3d;
mat4 View;
mat4 Sky;
mat4 Projection2d;
};
layout (push_constant) uniform PushConstants {
mat4 Model;
};
layout (location = 0) in vec4 position;
layout (location = 1) in vec4 velocity;
layout (location = 2) in vec4 color;
layout (location = 3) in vec4 ramp;
layout (location = 0) out vec4 o_velocity;
layout (location = 1) out vec4 o_color;
layout (location = 2) out vec4 o_ramp;
void
main (void)
{
// geometry shader will take care of Projection and View
gl_Position = Model * position;
o_velocity = Model * velocity;
o_color = color;
o_ramp = ramp;
}

View file

@ -0,0 +1,56 @@
#version 450
layout (local_size_x = 32, local_size_y = 32) in;
struct Particle {
vec4 pos;
vec4 vel;
vec4 color;
float tex;
float ramp;
float scale;
float live;
};
struct Parameters {
vec4 drag; // [dx, dy, dz, grav scale]
vec4 ramp; // [rate, max, alpha rate, scale rate]
};
layout(std140, set = 0, binding = 0) buffer ParticleStates {
Particle particles[];
};
layout(std140, set = 0, binding = 1) buffer ParticleParameters {
Parameters parameters[];
};
layout(std140, set = 0, binding = 2) buffer ParticleSystem {
uint particleCount;
};
layout (push_constant) uniform PushConstants {
vec4 gravity;
float dT;
};
void
main ()
{
uint ind = gl_GlobalInvocationID.x;
if (ind >= particleCount) {
return;
}
Particle part = particles[ind];
Parameters parm = parameters[ind];
part.pos += dT * part.vel;
part.vel += dT * (part.vel * parm.drag + gravity * parm.drag.w);
part.ramp += dT * parm.ramp.x;
part.scale += dT * parm.ramp.z;
part.color.a -= dT * parm.ramp.a;
part.live -= dT;
particles[ind] = part;
}

View file

@ -0,0 +1,93 @@
#version 450
layout (constant_id = 0) const int MaxParticles = 2048;
layout (local_size_x = 1, local_size_y = 1) in;
struct Particle {
vec4 pos;
vec4 vel;
vec4 color;
float tex;
float ramp;
float scale;
float live;
};
struct Parameters {
vec4 drag; // [dx, dy, dz, grav scale]
vec4 ramp; // [rate, max, alpha rate, scale rate]
};
layout(std140, set = 0, binding = 0) buffer InStates {
Particle particles[];
} inState;
layout(std140, set = 0, binding = 1) buffer InParameters {
Parameters parameters[];
} inParameters;
layout(std140, set = 0, binding = 2) buffer InSystem {
uint particleCount;
} inSystem;
layout(std140, set = 1, binding = 0) buffer OutStates {
Particle particles[];
} outStates;
layout(std140, set = 1, binding = 1) buffer OutParameters {
Parameters parameters[];
} outParameters;
layout(std140, set = 1, binding = 2) buffer OutSystem {
uint particleCount;
} outSystem;
layout(std140, set = 2, binding = 0) buffer NewStates {
Particle particles[];
} newStates;
layout(std140, set = 2, binding = 1) buffer NewParameters {
Parameters parameters[];
} newParameters;
layout(std140, set = 2, binding = 2) buffer NewSystem {
uint particleCount;
} newSystem;
bool
is_dead (in Particle part, in Parameters parm)
{
if (part.live <= 0) {
return true;
}
if (part.ramp >= parm.ramp.y || part.color.a <= 0 || part.scale <= 0) {
return true;
}
return false;
}
void
main ()
{
uint j = 0;
// compact existing partles removing dead particles
for (uint i = 0; i < inSystem.particleCount; i++) {
if (is_dead (inState.particles[i], inParameters.parameters[i])) {
continue;
}
outStates.particles[j] = inState.particles[i];
outParameters.parameters[j] = inParameters.parameters[i];
j++;
}
// inject any new particles that aren't DOA
for (uint i = 0; i < newSystem.particleCount && j < MaxParticles; i++) {
if (is_dead (inState.particles[i], inParameters.parameters[i])) {
continue;
}
outStates.particles[j] = newStates.particles[i];
outParameters.parameters[j] = newParameters.parameters[i];
j++;
}
outSystem.particleCount = j;
}

View file

@ -40,20 +40,29 @@
#include "QF/cvar.h"
#include "QF/render.h"
#include "QF/va.h"
#include "QF/plugin/vid_render.h"
#include "QF/Vulkan/qf_vid.h"
#include "QF/Vulkan/debug.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/instance.h"
#include "QF/Vulkan/qf_particles.h"
#include "r_internal.h"
#include "vid_vulkan.h"
static const char * __attribute__((used)) particle_pass_names[] = {
"draw",
};
void
Vulkan_ClearParticles (struct vulkan_ctx_s *ctx)
Vulkan_ClearParticles (vulkan_ctx_t *ctx)
{
}
void
Vulkan_InitParticles (struct vulkan_ctx_s *ctx)
Vulkan_InitParticles (vulkan_ctx_t *ctx)
{
}
@ -217,7 +226,7 @@ static vid_particle_funcs_t vulkan_particles_QF = {
};
void
Vulkan_r_easter_eggs_f (cvar_t *var, struct vulkan_ctx_s *ctx)
Vulkan_r_easter_eggs_f (cvar_t *var, vulkan_ctx_t *ctx)
{
if (!easter_eggs || !r_particles_style) {
return;
@ -239,35 +248,79 @@ Vulkan_r_easter_eggs_f (cvar_t *var, struct vulkan_ctx_s *ctx)
}
void
Vulkan_r_particles_style_f (cvar_t *var, struct vulkan_ctx_s *ctx)
Vulkan_r_particles_style_f (cvar_t *var, vulkan_ctx_t *ctx)
{
Vulkan_r_easter_eggs_f (var, ctx);
}
void
Vulkan_DrawParticles (struct vulkan_ctx_s *ctx)
Vulkan_DrawParticles (vulkan_ctx_t *ctx)
{
}
void
Vulkan_Particles_Init (struct vulkan_ctx_s *ctx)
Vulkan_Particles_Init (vulkan_ctx_t *ctx)
{
/*
easter_eggs = Cvar_Get ("easter_eggs", "0", CVAR_NONE, r_easter_eggs_f,
"Enables easter eggs.");
r_particles = Cvar_Get ("r_particles", "1", CVAR_ARCHIVE, r_particles_f,
"Toggles drawing of particles.");
r_particles_max = Cvar_Get ("r_particles_max", "2048", CVAR_ARCHIVE,
r_particles_max_f, "Maximum amount of "
"particles to display. No maximum, minimum "
"is 0.");
r_particles_nearclip = Cvar_Get ("r_particles_nearclip", "32",
CVAR_ARCHIVE, r_particles_nearclip_f,
"Distance of the particle near clipping "
"plane from the player.");
r_particles_style = Cvar_Get ("r_particles_style", "1", CVAR_ARCHIVE,
r_particles_style_f, "Sets particle style. "
"0 for Id, 1 for QF.");
*/
vulkan_vid_render_funcs.particles = &vulkan_particles_QF;
qfv_device_t *device = ctx->device;
qfvPushDebug (ctx, "particles init");
particlectx_t *pctx = calloc (1, sizeof (particlectx_t));
ctx->particle_context = pctx;
size_t frames = ctx->frames.size;
DARRAY_INIT (&pctx->frames, frames);
DARRAY_RESIZE (&pctx->frames, frames);
pctx->frames.grow = 0;
pctx->physics = Vulkan_CreateComputePipeline (ctx, "partphysics");
pctx->update = Vulkan_CreateComputePipeline (ctx, "partupdate");
pctx->draw = Vulkan_CreateGraphicsPipeline (ctx, "partdraw");
pctx->physics_layout = Vulkan_CreatePipelineLayout (ctx,
"partphysics_layout");
pctx->update_layout = Vulkan_CreatePipelineLayout (ctx,
"partupdate_layout");
pctx->draw_layout = Vulkan_CreatePipelineLayout (ctx, "draw_layout");
pctx->pool = Vulkan_CreateDescriptorPool (ctx, "particle_pool");
pctx->setLayout = Vulkan_CreateDescriptorSetLayout (ctx, "particle_set");
for (size_t i = 0; i < frames; i++) {
__auto_type pframe = &pctx->frames.a[i];
DARRAY_INIT (&pframe->cmdSet, QFV_particleNumPasses);
DARRAY_RESIZE (&pframe->cmdSet, QFV_particleNumPasses);
pframe->cmdSet.grow = 0;
QFV_AllocateCommandBuffers (device, ctx->cmdpool, 1, &pframe->cmdSet);
for (int j = 0; j < QFV_particleNumPasses; j++) {
QFV_duSetObjectName (device, VK_OBJECT_TYPE_COMMAND_BUFFER,
pframe->cmdSet.a[j],
va (ctx->va_ctx, "cmd:particle:%zd:%s", i,
particle_pass_names[j]));
}
}
qfvPopDebug (ctx);
}
void
Vulkan_Particles_Shutdown (vulkan_ctx_t *ctx)
{
qfv_device_t *device = ctx->device;
qfv_devfuncs_t *dfunc = device->funcs;
particlectx_t *pctx = ctx->particle_context;
for (size_t i = 0; i < pctx->frames.size; i++) {
__auto_type pframe = &pctx->frames.a[i];
free (pframe->cmdSet.a);
}
dfunc->vkDestroyPipeline (device->dev, pctx->physics, 0);
dfunc->vkDestroyPipeline (device->dev, pctx->update, 0);
dfunc->vkDestroyPipeline (device->dev, pctx->draw, 0);
free (pctx->frames.a);
free (pctx);
}