Files
funnygame/materialsystem/vulkan/rtlinker.cpp
2026-04-30 22:03:45 +03:00

320 lines
7.8 KiB
C++

#include "rtlinker.h"
#define SPV_ENABLE_UTILITY_CODE
#include "spirv/unified1/spirv.h"
#include "rtlinker_gen.h"
#include "tier2/ifilesystem.h"
#include "tier1/utlstring.h"
#define MINI_SPIRV_IMPLEMENTATION
#include "../minispv/minispv.h"
class CSpirvModule
{
CUtlVector<SpvCapability> m_capabilities;
CUtlVector<CUtlString> m_extensions;
CUtlVector<CUtlString> m_instructionImports;
SpvAddressingModel m_addressingModel;
SpvMemoryModel m_memoryModel;
CUtlVector<CUtlString> m_entryPoints;
};
void CVkShaderLinker::SetMainSpirv( uint32_t size, uint32_t *data )
{
m_main = (SpirvShader_t){size,data};
}
void CVkShaderLinker::AddSpirv( uint32_t size, uint32_t *data )
{
m_shaders.AppendTail((SpirvShader_t){size,data});
}
#define SET_LAST(a) if ( a > last ) last = a;
void CVkShaderLinker::Build()
{
mspv_module *mod = mspv_read_module(m_main.m_size, m_main.m_data);
uint32_t last = 0;
uint32_t current = 0;
CUtlVector<uint32_t> callers = {};
mspv_array(mspv_function) functions_orig;
V_memcpy(&functions_orig, &mod->functions, sizeof(functions_orig));
mod->functions = {};
for ( int i = 0; i < mod->types.count; i++ )
{
SET_LAST(mod->types.data[i].id);
}
for ( int i = 0; i < mod->ext_instructions.count; i++ )
{
SET_LAST(mod->ext_instructions.data[i].id);
}
for ( int i = 0; i < mod->variables.count; i++ )
{
SET_LAST(mod->variables.data[i].result);
SET_LAST(mod->variables.data[i].resulttype);
}
for ( int i = 0; i < functions_orig.count; i++ )
{
SET_LAST(functions_orig.data[i].result);
mspv_function &f = functions_orig.data[i];
bool bIsCaller = false;
for ( int u = 0; u < f.instructions.len; )
{
SpvOp op = (SpvOp)(f.instructions.data[u]&0xFFFF);
if (op == SpvOpExecuteCallableKHR)
{
bIsCaller = true;
callers.AppendTail(i);
V_printf("found caller: %i %u\n", i, f.result);
}
uint32_t uOpLen = f.instructions.data[u]>>16;
uint32_t uOpCount = SpvGetOperandCount(op);
if (uOpCount == 0)
{
u+=uOpLen;
continue;
}
ESpirvOperandType *peOps = (ESpirvOperandType *)V_malloc(uOpCount*sizeof(ESpirvOperandType));
ESpirvOperandFlags *peFlags = (ESpirvOperandFlags *)V_malloc(uOpCount*sizeof(ESpirvOperandFlags));
SpvGetOperands(op, peOps);
SpvGetOperandFlags(op, peFlags);
for ( int c = 0, uParam = 0; (c < uOpCount) && (c < uOpLen-1); c++ )
{
switch (peOps[c])
{
case k_ESpirv_RefId:
case k_ESpirv_ResultId:
case k_ESpirv_ResultTypeId:
case k_ESpirv_ScopeId:
case k_ESpirv_MemorySemanticsId:
if (peFlags[uParam]==k_ESpirvOperandFlags_None)
{
uParam++;
}
SET_LAST(f.instructions.data[u+c+1]);
break;
default:
break;
}
}
u+=uOpLen;
V_free(peOps);
V_free(peFlags);
}
if (!bIsCaller)
mspv_array_push(mod->functions, f);
}
current = last;
/* now we can combine shaders*/
CUtlVector<uint32_t> functions = {};
CUtlVector<mspv_function> f = {};
for (auto &shader: m_shaders)
{
mspv_module *s = mspv_read_module(shader.m_size, shader.m_data);
for ( int i = 0; i < s->capabilities.count; i++ )
{
mspv_array_push(mod->capabilities, s->capabilities.data[i]);
}
for ( int i = 0; i < s->extensions.count; i++ )
{
mspv_array_push(mod->extensions, s->extensions.data[i]);
}
for ( int i = 0; i < s->ext_instructions.count; i++ )
{
SET_LAST(s->ext_instructions.data[i].id);
}
for ( int i = 0; i < s->entry_points.count; i++ )
{
functions.AppendTail(s->entry_points.data[i].id+current);
for ( uint32_t u = 0; u < s->functions.count; u++ )
{
if (s->functions.data[u].result != s->entry_points.data[i].id)
continue;
f.AppendTail(s->functions.data[u]);
break;
}
SET_LAST(s->entry_points.data[i].id+current);
}
for ( int i = 0; i < s->variables.count; i++ )
{
s->variables.data[i].result+=current;
s->variables.data[i].resulttype+=current;
SET_LAST(s->variables.data[i].result)
SET_LAST(s->variables.data[i].resulttype)
mspv_array_push(mod->variables, s->variables.data[i]);
}
for ( int i = 0; i < s->types.count; i++ )
{
s->types.data[i].id+=current;
mspv_data_view &dv = s->types.data[i].dv;
SET_LAST(s->types.data[i].id);
SpvOp op = (SpvOp)(dv.data[0]&0xFFFF);
uint32_t uOpLen = dv.data[0]>>16;
uint32_t uOpCount = SpvGetOperandCount(op);
if (uOpCount == 0)
{
continue;
}
ESpirvOperandType *peOps = (ESpirvOperandType *)V_malloc(uOpCount*sizeof(ESpirvOperandType));
ESpirvOperandFlags *peFlags = (ESpirvOperandFlags *)V_malloc(uOpCount*sizeof(ESpirvOperandFlags));
SpvGetOperands(op, peOps);
SpvGetOperandFlags(op, peFlags);
/* shift the stuff */
for ( int c = 0, uParam = 0; (c < uOpCount) && (c < uOpLen-1); c++ )
{
switch (peOps[uParam])
{
case k_ESpirv_RefId:
case k_ESpirv_ResultId:
case k_ESpirv_ResultTypeId:
case k_ESpirv_ScopeId:
case k_ESpirv_MemorySemanticsId:
if (peFlags[uParam]==k_ESpirvOperandFlags_None)
{
dv.data[c+1]+=current;
uParam++;
}
if (peFlags[uParam]==k_ESpirvOperandFlags_Array)
{
dv.data[c+1]+=current;
};
SET_LAST(dv.data[c+1]);
break;
default:
break;
}
}
V_free(peFlags);
V_free(peOps);
mspv_array_push(mod->types, s->types.data[i]);
}
for ( int i = 0; i < s->functions.count; i++ )
{
s->functions.data[i].result+=current;
SET_LAST(s->functions.data[i].result);
mspv_function &f = s->functions.data[i];
for ( int u = 0; u < f.instructions.len; )
{
SpvOp op = (SpvOp)(f.instructions.data[u]&0xFFFF);
uint32_t uOpLen = f.instructions.data[u]>>16;
uint32_t uOpCount = SpvGetOperandCount(op);
if (uOpCount == 0)
{
u+=uOpLen;
continue;
}
ESpirvOperandType *peOps = (ESpirvOperandType *)V_malloc(uOpCount*sizeof(ESpirvOperandType));
SpvGetOperands(op, peOps);
/* shift the stuff */
for ( int c = 0; c < uOpCount && c < (uOpLen-1); c++ )
{
switch (peOps[c])
{
case k_ESpirv_RefId:
case k_ESpirv_ResultId:
case k_ESpirv_ResultTypeId:
case k_ESpirv_ScopeId:
case k_ESpirv_MemorySemanticsId:
f.instructions.data[u+c+1]+=current;
SET_LAST(f.instructions.data[u+c+1]);
break;
default:
break;
}
}
u+=uOpLen;
V_free(peOps);
}
mspv_array_push(mod->functions, s->functions.data[i]);
}
current = last;
}
/* generate trace call */
V_printf("functions %i\n", functions.GetSize());
uint32_t n = 0;
for ( auto &c: callers )
{
CUtlVector<uint32_t> ops = {};
mspv_function fn = functions_orig.data[c];
mspv_data_view dv = fn.instructions;
uint32_t u;
CUtlVector<uint32_t> inputs = {};
u = 0;
while(u < fn.instructions.len)
{
SpvOp op = (SpvOp)(dv.data[u]&0xFFFF);
uint32_t uOpLen = dv.data[u]>>16;
if (op != SpvOpFunctionParameter)
{
break;
}
ops.AppendTail(&dv.data[u], uOpLen);
inputs.AppendTail(dv.data[u+2]);
u += uOpLen;
}
ops.AppendTail(mspv_make_op(SpvOpLabel, 1));
ops.AppendTail(current++);
ops.AppendTail(mspv_make_op(SpvOpFunctionCall, 5));
ops.AppendTail(f[n].resulttype);
ops.AppendTail(current++);
ops.AppendTail(f[n].result);
ops.AppendTail(inputs[0]);
ops.AppendTail(inputs[1]);
ops.AppendTail(mspv_make_op(SpvOpReturn, 0));
fn.instructions.data = (uint32_t*)V_malloc(ops.GetSize()*sizeof(uint32_t));
V_memcpy(fn.instructions.data, ops.GetData(), ops.GetSize()*sizeof(uint32_t));
fn.instructions.len = ops.GetSize();
mspv_array_push(mod->functions, fn);
n++;
}
mspv_spv spv = mspv_write_module(mod);
mspv_close_module(mod);
m_out.m_data = spv.data;
m_out.m_size = spv.count;
IFileHandle *ph = filesystem->Open("a.txt", FILEMODE_WRITE);
filesystem->Write(ph, spv.data, spv.count*4);
filesystem->Close(ph);
mspv_array_destroy(spv);
}
uint32_t CVkShaderLinker::GetSize()
{
return m_out.m_size;
}
uint32_t *CVkShaderLinker::GetData()
{
return m_out.m_data;
}