mirror of
https://github.com/w23/xash3d-fwgs
synced 2024-12-16 06:00:33 +01:00
328 lines
9.2 KiB
C
328 lines
9.2 KiB
C
#include "vk_meatpipe.h"
|
|
|
|
#include "vk_pipeline.h"
|
|
#include "ray_resources.h"
|
|
|
|
#include "ray_pass.h"
|
|
#include "vk_common.h"
|
|
|
|
#define MIN(a,b) ((a)<(b)?(a):(b))
|
|
|
|
#define CHAR4UINT(a,b,c,d) (((d)<<24)|((c)<<16)|((b)<<8)|(a))
|
|
static const uint32_t k_meatpipe_magic = CHAR4UINT('M', 'E', 'A', 'T');
|
|
|
|
typedef struct {
|
|
const byte *data;
|
|
int off, size;
|
|
qboolean error;
|
|
} cursor_t;
|
|
|
|
typedef struct {
|
|
cursor_t cur;
|
|
int shaders_count;
|
|
VkShaderModule *shaders;
|
|
} load_context_t;
|
|
|
|
const void* curReadPtr(cursor_t *cur, int size) {
|
|
const int left = cur->size - cur->off;
|
|
if (left < size) {
|
|
cur->error = true;
|
|
return NULL;
|
|
}
|
|
|
|
const void* const ret = cur->data + cur->off;
|
|
cur->off += size;
|
|
return ret;
|
|
}
|
|
|
|
#define CUR_ERROR(errmsg, ...) \
|
|
if (ctx->cur.error) { \
|
|
gEngine.Con_Printf(S_ERROR "(off=%d left=%d) " errmsg "\n", ctx->cur.off, (ctx->cur.size - ctx->cur.off), ##__VA_ARGS__); \
|
|
goto finalize; \
|
|
}
|
|
|
|
#define CUR_ERROR_RETURN(retval, errmsg, ...) \
|
|
if (ctx->cur.error) { \
|
|
gEngine.Con_Printf(S_ERROR "(off=%d left=%d) " errmsg "\n", ctx->cur.off, (ctx->cur.size - ctx->cur.off), ##__VA_ARGS__); \
|
|
return retval; \
|
|
}
|
|
|
|
#define READ_PTR(size, errmsg, ...) \
|
|
curReadPtr(&ctx->cur, size); CUR_ERROR(errmsg, ##__VA_ARGS__)
|
|
|
|
uint32_t curReadU32(cursor_t *cur) {
|
|
const void *src = curReadPtr(cur, sizeof(uint32_t));
|
|
if (!src)
|
|
return 0;
|
|
|
|
uint32_t ret;
|
|
memcpy(&ret, src, sizeof(uint32_t));
|
|
return ret;
|
|
}
|
|
|
|
#define READ_U32(errmsg, ...) \
|
|
curReadU32(&ctx->cur); CUR_ERROR(errmsg, ##__VA_ARGS__)
|
|
|
|
#define READ_U32_RETURN(retval, errmsg, ...) \
|
|
curReadU32(&ctx->cur); CUR_ERROR_RETURN(retval, errmsg, ##__VA_ARGS__)
|
|
|
|
int curReadStr(cursor_t *cur, char* out, int out_size) {
|
|
const int len = curReadU32(cur);
|
|
if (cur->error)
|
|
return -1;
|
|
|
|
const char *src = curReadPtr(cur, len);
|
|
if (cur->error)
|
|
return -1;
|
|
|
|
const int max = MIN(out_size, len); \
|
|
memcpy(out, src, max); \
|
|
out[max] = '\0';
|
|
return len;
|
|
}
|
|
|
|
#define READ_STR(out, errmsg, ...) \
|
|
curReadStr(&ctx->cur, out, sizeof(out)); CUR_ERROR(errmsg, ##__VA_ARGS__)
|
|
|
|
#define READ_STR_RETURN(retval, out, errmsg, ...) \
|
|
curReadStr(&ctx->cur, out, sizeof(out)); CUR_ERROR_RETURN(retval, errmsg, ##__VA_ARGS__)
|
|
|
|
#define NO_SHADER 0xffffffff
|
|
|
|
static struct ray_pass_s *pipelineLoadCompute(load_context_t *ctx, int i, const char *name, const ray_pass_layout_t *layout) {
|
|
const uint32_t shader_comp = READ_U32_RETURN(NULL, "Couldn't read comp shader for %d %s", i, name);
|
|
|
|
if (shader_comp >= ctx->shaders_count) {
|
|
gEngine.Con_Printf(S_ERROR "Pipeline %s shader index out of bounds %d (count %d)\n", name, shader_comp, ctx->shaders_count);
|
|
return NULL;
|
|
}
|
|
|
|
const ray_pass_create_compute_t rpcc = {
|
|
.debug_name = name,
|
|
.layout = *layout,
|
|
.shader_module = ctx->shaders[shader_comp],
|
|
};
|
|
|
|
return RayPassCreateCompute(&rpcc);
|
|
}
|
|
|
|
static struct ray_pass_s *pipelineLoadRT(load_context_t *ctx, int i, const char *name, const ray_pass_layout_t *layout) {
|
|
ray_pass_p ret = NULL;
|
|
ray_pass_create_tracing_t rpct = {
|
|
.debug_name = name,
|
|
.layout = *layout,
|
|
};
|
|
|
|
// FIXME bounds check shader indices
|
|
|
|
const uint32_t shader_rgen = READ_U32("Couldn't read rgen shader for %d %s", i, name);
|
|
rpct.raygen_module = ctx->shaders[shader_rgen];
|
|
|
|
rpct.miss_count = READ_U32("Couldn't read miss count for %d %s", i, name);
|
|
if (rpct.miss_count) {
|
|
rpct.miss_module = Mem_Malloc(vk_core.pool, sizeof(VkShaderModule) * rpct.miss_count);
|
|
for (int j = 0; j < rpct.miss_count; ++j) {
|
|
const uint32_t shader_miss = READ_U32("Couldn't read miss shader %d for %d %s", j, i, name);
|
|
rpct.miss_module[j] = ctx->shaders[shader_miss];
|
|
}
|
|
}
|
|
|
|
rpct.hit_count = READ_U32("Couldn't read hit count for %d %s", i, name);
|
|
if (rpct.hit_count) {
|
|
ray_pass_hit_group_t *hit = Mem_Malloc(vk_core.pool, sizeof(rpct.hit[0]) * rpct.hit_count);
|
|
rpct.hit = hit;
|
|
for (int j = 0; j < rpct.hit_count; ++j) {
|
|
const uint32_t closest = READ_U32("Couldn't read closest shader %d for %d %s", j, i, name);
|
|
const uint32_t any = READ_U32("Couldn't read any shader %d for %d %s", j, i, name);
|
|
|
|
hit[j] = (ray_pass_hit_group_t){
|
|
.closest_module = (closest == NO_SHADER) ? VK_NULL_HANDLE : ctx->shaders[closest],
|
|
.any_module = (any == NO_SHADER) ? VK_NULL_HANDLE : ctx->shaders[any],
|
|
};
|
|
}
|
|
}
|
|
|
|
ret = RayPassCreateTracing(&rpct);
|
|
|
|
finalize:
|
|
if (rpct.hit)
|
|
Mem_Free((void*)rpct.hit);
|
|
|
|
if (rpct.miss_module)
|
|
Mem_Free(rpct.miss_module);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define MAX_BINDINGS 32
|
|
static int readBindings(load_context_t *ctx, VkDescriptorSetLayoutBinding *bindings, int* semantics) {
|
|
const int count = READ_U32_RETURN(false, "Coulnd't read bindings count");
|
|
|
|
if (count > MAX_BINDINGS) {
|
|
gEngine.Con_Printf(S_ERROR "Too many binding (%d), max: %d\n", count, MAX_BINDINGS);
|
|
return 0;
|
|
}
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
char name[64];
|
|
READ_STR_RETURN(false, name, "Couldn't read binding name");
|
|
const uint32_t header = READ_U32_RETURN(false, "Couldn't read header for binding %s", name);
|
|
const uint32_t type = READ_U32_RETURN(false, "Couldn't read type for binding %s", name);
|
|
const uint32_t stages = READ_U32_RETURN(false, "Couldn't read stages for binding %s", name);
|
|
|
|
#define BINDING_WRITE_BIT 0x80000000u
|
|
const qboolean write = !!(header & BINDING_WRITE_BIT);
|
|
const uint32_t descriptor_set = (header >> 8) & 0xffu;
|
|
const uint32_t binding = header & 0xffu;
|
|
|
|
const ray_resource_binding_desc_fixme_t *binding_fixme = RayResouceGetBindingForName_FIXME(name);
|
|
if (!binding_fixme) {
|
|
gEngine.Con_Printf(S_ERROR "Couldn't find fixme desc for binding %s\n", name);
|
|
return 0;
|
|
}
|
|
|
|
bindings[i] = (VkDescriptorSetLayoutBinding){
|
|
.binding = binding,
|
|
.descriptorType = type,
|
|
.descriptorCount = binding_fixme->count,
|
|
.stageFlags = stages,
|
|
.pImmutableSamplers = NULL,
|
|
};
|
|
semantics[i] = write ? -binding_fixme->semantic : binding_fixme->semantic;
|
|
|
|
gEngine.Con_Reportf("Binding %d: %s ds=%d b=%d s=%08x type=%d semantic=%d\n", i, name, descriptor_set, binding, stages, type, binding_fixme->semantic);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static struct ray_pass_s *pipelineLoad(load_context_t *ctx, int i) {
|
|
int semantics[MAX_BINDINGS];
|
|
VkDescriptorSetLayoutBinding bindings[MAX_BINDINGS];
|
|
ray_pass_layout_t layout = {
|
|
.bindings_semantics = semantics,
|
|
.bindings = bindings,
|
|
.push_constants = {0},
|
|
};
|
|
|
|
const uint32_t type = READ_U32("Couldn't read pipeline %d type", i);
|
|
|
|
char name[64];
|
|
READ_STR(name, "Couldn't read pipeline %d name", i);
|
|
|
|
gEngine.Con_Reportf("%d: loading pipeline %s\n", i, name);
|
|
|
|
layout.bindings_count = readBindings(ctx, bindings, semantics);
|
|
if (!layout.bindings_count) {
|
|
gEngine.Con_Printf(S_ERROR "Couldn't read bindings for pipeline %s\n", name);
|
|
return NULL;
|
|
}
|
|
|
|
#define PIPELINE_COMPUTE 1
|
|
#define PIPELINE_RAYTRACING 2
|
|
|
|
switch (type) {
|
|
case PIPELINE_COMPUTE:
|
|
return pipelineLoadCompute(ctx, i, name, &layout);
|
|
case PIPELINE_RAYTRACING:
|
|
return pipelineLoadRT(ctx, i, name, &layout);
|
|
default:
|
|
gEngine.Con_Printf(S_ERROR "Unexpected pipeline type %d\n", type);
|
|
return NULL;
|
|
}
|
|
|
|
finalize:
|
|
return NULL;
|
|
}
|
|
|
|
qboolean R_VkMeatpipeLoad(vk_meatpipe_t *out, const char *filename) {
|
|
qboolean ret = false;
|
|
fs_offset_t size = 0;
|
|
byte* const buf = gEngine.fsapi->LoadFile(filename, &size, false);
|
|
|
|
if (!buf) {
|
|
gEngine.Con_Printf(S_ERROR "Couldn't read \"%s\"\n", filename);
|
|
return false;
|
|
}
|
|
|
|
load_context_t context = {
|
|
.cur = { .data = buf, .off = 0, .size = size },
|
|
.shaders_count = 0,
|
|
.shaders = NULL,
|
|
};
|
|
load_context_t *ctx = &context;
|
|
|
|
out->passes_count = 0;
|
|
|
|
{
|
|
const uint32_t magic = READ_U32("Couldn't read magic");
|
|
|
|
if (magic != k_meatpipe_magic) {
|
|
gEngine.Con_Printf(S_ERROR "Meatpipe magic invalid for \"%s\": got %08x expected %08x\n", filename, magic, k_meatpipe_magic);
|
|
goto finalize;
|
|
}
|
|
}
|
|
|
|
ctx->shaders_count = READ_U32("Couldn't read shaders count");
|
|
ctx->shaders = Mem_Malloc(vk_core.pool, sizeof(VkShaderModule) * ctx->shaders_count);
|
|
for (int i = 0; i < ctx->shaders_count; ++i) {
|
|
ctx->shaders[i] = VK_NULL_HANDLE;
|
|
|
|
char name[64];
|
|
READ_STR(name, "Couldn't read shader %d name", i);
|
|
|
|
const int size = READ_U32("Couldn't read shader %s size", name);
|
|
const void *src = READ_PTR(size, "Couldn't read shader %s data", name);
|
|
|
|
if (VK_NULL_HANDLE == (ctx->shaders[i] = R_VkShaderLoadFromMem(src, size, name))) {
|
|
gEngine.Con_Printf(S_ERROR "Failed to load shader %d:%s\n", i, name);
|
|
goto finalize;
|
|
}
|
|
|
|
gEngine.Con_Reportf("%d: Shader loaded %s\n", i, name);
|
|
}
|
|
|
|
out->passes_count = READ_U32("Couldn't read pipelines count");
|
|
out->passes = Mem_Malloc(vk_core.pool, sizeof(out->passes[0]) * out->passes_count);
|
|
for (int i = 0; i < out->passes_count; ++i) {
|
|
if (!(out->passes[i] = pipelineLoad(ctx, i)))
|
|
goto finalize;
|
|
}
|
|
|
|
ret = true;
|
|
finalize:
|
|
if (!ret) {
|
|
R_VkMeatpipeDestroy(out);
|
|
}
|
|
|
|
for (int i = 0; i < ctx->shaders_count; ++i) {
|
|
if (ctx->shaders[i] == VK_NULL_HANDLE)
|
|
break;
|
|
|
|
R_VkShaderDestroy(ctx->shaders[i]);
|
|
}
|
|
|
|
if (ctx->shaders)
|
|
Mem_Free(ctx->shaders);
|
|
|
|
Mem_Free(buf);
|
|
return ret;
|
|
}
|
|
|
|
void R_VkMeatpipeDestroy(vk_meatpipe_t *mp) {
|
|
for (int i = 0; i < mp->passes_count; ++i) {
|
|
if (!mp->passes[i])
|
|
break;
|
|
|
|
RayPassDestroy(mp->passes[i]);
|
|
}
|
|
|
|
mp->passes_count = 0;
|
|
}
|
|
|
|
void R_VkMeatpipePerform(vk_meatpipe_t *mp, VkCommandBuffer cmdbuf, int frame_set_slot, struct vk_ray_resources_s *res) {
|
|
for (int i = 0; i < mp->passes_count; ++i) {
|
|
RayPassPerform(cmdbuf, frame_set_slot, mp->passes[i], res);
|
|
}
|
|
}
|