[vulkan] Add support for building and loading shaders

Shaders can be built as spv files and installed into
$libdir/quakeforge/shaders or as spvc files and compiled into the
engine. Loading supports $builtin/name to access builtin shaders,
$shader/path to access external standard shaders and quake filesystem
access for all other paths.
This commit is contained in:
Bill Currie 2020-12-23 14:32:29 +09:00
parent f1848bb5b7
commit 7fb335a215
9 changed files with 296 additions and 9 deletions

View file

@ -107,6 +107,27 @@ qcautodep = $(join $(addsuffix $(DEPDIR)/,$(dir $(basename $(1)))),$(addsuffix .
r_depfiles_remade=
pas_depfiles_remade=
V_GLSLANG = $(V_GLSLANG_@AM_V@)
V_GLSLANG_ = $(V_GLSLANG_@AM_DEFAULT_V@)
V_GLSLANG_0 = @echo " GLSLANG " $@;
V_GLSLANG_1 =
V_XXD = $(V_XXD_@AM_V@)
V_XXD_ = $(V_XXD_@AM_DEFAULT_V@)
V_XXD_0 = @echo " XXD " $@;
V_XXD_1 =
%.spv: %
@$(mkdir_p) $(builddir)/`dirname $@`
$(V_GLSLANG)$(GLSLANGVALIDATOR) -V $< -o $@ > /dev/null
%.spvc: %
@$(mkdir_p) $(builddir)/`dirname $@`
$(V_GLSLANG)$(GLSLANGVALIDATOR) --vn `basename $< | tr . _` -V $< -o $@ > /dev/null
shaderdir = @shaderdir@
shader_DATA =
include doc/Makemodule.am
include RPM/Makemodule.am
include debian/Makemodule.am
@ -122,6 +143,7 @@ include tools/Makemodule.am
include ruamoko/Makemodule.am
DISTCLEANFILES += $(r_depfiles_remade) $(pas_depfiles_remade)
CLEANFILES += $(shader_DATA)
$(r_depfiles_remade):
$(MKDIR_P) $(@D)

View file

@ -98,6 +98,19 @@ eval expanded_plugindir="$expanded_plugindir"
AC_DEFINE_UNQUOTED(FS_PLUGINPATH, "$expanded_plugindir", [Define this to the path from which to load plugins])
AC_SUBST(plugindir)
SHADERDIR="\${libdir}/quakeforge/shaders"
if test "x$shaderdir" = "xauto" -o "x$shaderdir" = "xyes" -o "x$shaderdir" = "x"; then
shaderdir="$SHADERDIR"
elif test "x$shaderdir" = xno; then
shaderdir="."
else
SHADERDIR="$shaderdir"
fi
eval expanded_shaderdir="$shaderdir"
eval expanded_shaderdir="$expanded_shaderdir"
AC_DEFINE_UNQUOTED(FS_SHADERPATH, "$expanded_shaderdir", [Define this to the path from which to load shaders])
AC_SUBST(plugindir)
AC_ARG_WITH(gl-driver,
[ --with-gl-driver=NAME Name of OpenGL driver DLL/DSO],
gl_driver=$withval,

View file

@ -16,5 +16,6 @@ if test "x$HAVE_VULKAN" = xyes; then
AC_DEFINE([HAVE_VULKAN], [1], [Define if yhou have the Vulkan libs])
fi
AC_SUBST(VULKAN_LIBS)
AC_SUBST(GLSLANGVALIDATOR, [$glslangvalidator])
AM_CONDITIONAL(X11_VULKAN, test "x$HAVE_VULKAN" = "xyes")

View file

@ -0,0 +1,13 @@
#ifndef __QF_Vulkan_shader_h
#define __QF_Vulkan_shader_h
struct qfv_device_s;
struct vulkan_ctx_s;
VkShaderModule QFV_CreateShaderModule (struct qfv_device_s *device,
const char *path);
void QFV_RegisterShaderModule (struct vulkan_ctx_s *ctx, const char *name,
VkShaderModule module);
void QFV_DeregisterShaderModule (struct vulkan_ctx_s *ctx, const char *name);
#endif//__QF_Vulkan_shader_h

View file

@ -44,6 +44,8 @@ typedef struct vulkan_ctx_s {
VkSampleCountFlagBits msaaSamples; // FIXME not here?
struct hashlink_s *hashlinks; //FIXME want per thread
VkSurfaceKHR surface; //FIXME surface = window, so "contains" swapchain
struct hashtab_s *shadermodules;
struct shadermodule_s *shadermodule_freelist;
VkCommandPool cmdpool;
VkCommandBuffer cmdbuffer;

View file

@ -78,7 +78,7 @@ V_SED_ = $(V_SED_@AM_DEFAULT_V@)
V_SED_0 = @echo " SED " $@;
V_SED_1 =
SUFFICES=.frag .vert .fc .vc .slc .glsl .plist .plc
SUFFICES=.frag .vert .spv .spvc .fc .vc .slc .glsl .plist .plc
.glsl.slc:
$(V_SED)sed -e 's/^/"/' -e 's/$$/\\n"/' $< > $@.t &&\
$(am__mv) $@.t $@
@ -224,6 +224,7 @@ libs_video_renderer_vid_render_vulkan_la_SOURCES = \
libs/video/renderer/vulkan/namehack.h \
libs/video/renderer/vulkan/pipeline.c \
libs/video/renderer/vulkan/renderpass.c \
libs/video/renderer/vulkan/shader.c \
libs/video/renderer/vulkan/swapchain.c \
libs/video/renderer/vulkan/util.c \
libs/video/renderer/vulkan/util.h \
@ -233,29 +234,44 @@ libs_video_renderer_vid_render_vulkan_la_SOURCES = \
libs/video/renderer/vulkan/vkparse.lo: libs/video/renderer/vulkan/vkparse.c $(vkparse_src)
libs/video/renderer/vulkan/vulkan_vid_common.lo: libs/video/renderer/vulkan/vulkan_vid_common.c ${vkparse_src} $(pipeline_gen)
libs/video/renderer/vulkan/shader.lo: libs/video/renderer/vulkan/shader.c $(vkshader_c)
libs/video/renderer/vulkan/vulkan_vid_common.lo: libs/video/renderer/vulkan/vulkan_vid_common.c $(vkparse_src) $(pipeline_gen)
qwaq_curses = ruamoko/qwaq/qwaq-curses$(EXEEXT)
vkparse_cinc = libs/video/renderer/vulkan/vkparse.cinc
vkparse_hinc = libs/video/renderer/vulkan/vkparse.hinc
vkparse_src = \
${vkparse_cinc} \
${vkparse_hinc}
$(vkparse_cinc) \
$(vkparse_hinc)
vkparse_plist = \
$(srcdir)/libs/video/renderer/vulkan/vkparse.plist
passthrough_src = libs/video/renderer/vulkan/passthrough.vert
passthrough_c = libs/video/renderer/vulkan/passthrough.vert.spvc
pushcolor_src = libs/video/renderer/vulkan/pushcolor.frag
pushcolor_c = libs/video/renderer/vulkan/pushcolor.frag.spvc
$(passthrough_c): $(passthrough_src)
$(pushcolor_c): $(pushcolor_src)
vkshader_c = \
$(passthrough_c) \
$(pushcolor_c)
V_VKGEN = $(V_VKGEN_@AM_V@)
V_VKGEN_ = $(V_VKGEN_@AM_DEFAULT_V@)
V_VKGEN_0 = @echo " VKGEN " $@;
V_VKGEN_1 =
${vkparse_cinc}: $(vkgen) $(qwaq_curses) $(vkparse_plist)
$(V_VKGEN)$(qwaq_curses) $(vkgen) -- $(vkparse_plist) ${vkparse_cinc}.t ${vkparse_hinc}.t &&\
$(am__mv) ${vkparse_cinc}.t ${vkparse_cinc} &&\
$(am__mv) ${vkparse_hinc}.t ${vkparse_hinc}
$(vkparse_cinc): $(vkgen) $(qwaq_curses) $(vkparse_plist)
$(V_VKGEN)$(qwaq_curses) $(vkgen) -- $(vkparse_plist) $(vkparse_cinc).t $(vkparse_hinc).t &&\
$(am__mv) $(vkparse_cinc).t $(vkparse_cinc) &&\
$(am__mv) $(vkparse_hinc).t $(vkparse_hinc)
${vkparse_hinc}: ${vkparse_cinc}
$(vkparse_hinc): $(vkparse_cinc)
# do nothing: hinc generated at the same time as cinc
CLEANFILES += \
@ -263,7 +279,15 @@ CLEANFILES += \
libs/video/renderer/glsl/*.fc \
libs/video/renderer/glsl/*.slc \
libs/video/renderer/vulkan/*.plc \
libs/video/renderer/vulkan/*.spv \
libs/video/renderer/vulkan/*.spvc \
libs/video/renderer/vulkan/vkgen.sym \
$(vkparse_src)
BUILT_SOURCES += $(shader_gen)
#shader_DATA += \
# libs/video/renderer/vulkan/passthrough.vert.spv \
# libs/video/renderer/vulkan/pushcolor.frag.spv
EXTRA_DIST += $(shader_DATA:.spv=)

View file

@ -0,0 +1,8 @@
#version 450
layout (location = 0) in vec4 app_position;
void main()
{
gl_Position = app_position;
}

View file

@ -0,0 +1,12 @@
#version 450
layout (location = 0) out vec4 frag_color;
layout (push_constant) uniform ColorBlock {
vec4 Color;
} PushConstant;
void main()
{
frag_color = PushConstant.Color;
}

View file

@ -0,0 +1,192 @@
/*
shader.c
Vulkan shader manager
Copyright (C) 2020 Bill Currie <bill@taniwha.org>
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_MATH_H
# include <math.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "QF/alloc.h"
#include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/hash.h"
#include "QF/quakefs.h"
#include "QF/sys.h"
#include "QF/Vulkan/qf_vid.h"
#include "QF/Vulkan/device.h"
#include "QF/Vulkan/image.h"
#include "QF/Vulkan/instance.h"
#include "QF/Vulkan/renderpass.h"
#include "QF/Vulkan/shader.h"
#include "vid_vulkan.h"
static
#include "libs/video/renderer/vulkan/passthrough.vert.spvc"
static
#include "libs/video/renderer/vulkan/pushcolor.frag.spvc"
typedef struct shaderdata_s {
const char *name;
const uint32_t *data;
size_t size;
} shaderdata_t;
typedef struct shadermodule_s {
char *name;
union {
VkShaderModule module;
struct shadermodule_s *next;
};
} shadermodule_t;
static shaderdata_t builtin_shaders[] = {
{ "passthrough.vert", passthrough_vert, sizeof (passthrough_vert) },
{ "pushcolor.frag", pushcolor_frag, sizeof (pushcolor_frag) },
{}
};
#define BUILTIN "$builtin/"
#define BUILTIN_SIZE (sizeof (BUILTIN) - 1)
#define SHADER "$shader/"
#define SHADER_SIZE (sizeof (SHADER) - 1)
VkShaderModule
QFV_CreateShaderModule (qfv_device_t *device, const char *shader_path)
{
VkDevice dev = device->dev;
qfv_devfuncs_t *dfunc = device->funcs;
shaderdata_t _data = {};
shaderdata_t *data = 0;
dstring_t *path = 0;
QFile *file;
VkShaderModule shader = 0;
if (strncmp (shader_path, BUILTIN, BUILTIN_SIZE) == 0) {
const char *name = shader_path + BUILTIN_SIZE;
for (int i = 0; builtin_shaders[i].name; i++) {
if (strcmp (builtin_shaders[i].name, name) == 0) {
data = &builtin_shaders[i];
break;
}
}
} else if (strncmp (shader_path, SHADER, SHADER_SIZE)) {
path = dstring_new ();
dsprintf (path, "%s/%s", FS_SHADERPATH, shader_path + SHADER_SIZE);
file = Qopen (path->str, "rbz");
} else {
file = QFS_FOpenFile (shader_path);
}
if (file) {
_data.size = Qfilesize (file);
_data.data = malloc (_data.size);
Qread (file, (void *) _data.data, _data.size);
Qclose (file);
data = &_data;
}
if (data) {
VkShaderModuleCreateInfo createInfo = {
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, 0,
0, data->size, data->data
};
dfunc->vkCreateShaderModule (dev, &createInfo, 0, &shader);
} else {
Sys_MaskPrintf (SYS_VULKAN,
"QFV_CreateShaderModule: could not find shader %s\n",
shader_path);
}
if (path) {
dstring_delete (path);
}
if (_data.data) {
free ((void *) _data.data);
}
return shader;
}
static shadermodule_t *
new_module (vulkan_ctx_t *ctx)
{
shadermodule_t *shadermodule;
ALLOC (128, shadermodule_t, ctx->shadermodule, shadermodule);
return shadermodule;
}
static void
del_module (shadermodule_t *shadermodule, vulkan_ctx_t *ctx)
{
free (shadermodule->name);
FREE (ctx->shadermodule, shadermodule);
}
static const char *
sm_getkey (const void *sm, void *unused)
{
return ((shadermodule_t *) sm)->name;
}
static void
sm_free (void *sm, void *ctx)
{
del_module (sm, ctx);
}
void
QFV_RegisterShaderModule (vulkan_ctx_t *ctx, const char *name,
VkShaderModule module)
{
if (!ctx->shadermodules) {
ctx->shadermodules = Hash_NewTable (127, sm_getkey, sm_free, ctx, 0);
}
shadermodule_t *shadermodule = new_module (ctx);
shadermodule->name = strdup (name);
shadermodule->module = module;
Hash_Add (ctx->shadermodules, shadermodule);
}
void
QFV_DeregisterShaderModule (vulkan_ctx_t *ctx, const char *name)
{
if (!ctx->shadermodules) {
return;
}
Hash_Free (ctx->shadermodules, Hash_Del (ctx->shadermodules, name));
}