improve shadergen & create shaders

This commit is contained in:
Samuliak 2024-07-26 15:43:15 +02:00
parent 69597166f3
commit aac9b123a5
8 changed files with 306 additions and 262 deletions

View file

@ -2,24 +2,28 @@
namespace LatteDecompiler
{
static void _emitUniformVariables(LatteDecompilerShaderContext* decompilerContext, LatteDecompilerOutputUniformOffsets& uniformOffsets)
static void _emitUniformVariables(LatteDecompilerShaderContext* decompilerContext)
{
LatteDecompilerShaderResourceMapping& resourceMapping = decompilerContext->output->resourceMappingVK;
auto src = decompilerContext->shaderSource;
LatteDecompilerShaderResourceMapping& resourceMapping = decompilerContext->output->resourceMappingGL;
auto& uniformOffsets = decompilerContext->output->uniformOffsetsVK;
src->add("struct DefualtUniforms {" _CRLF);
sint32 uniformCurrentOffset = 0;
auto shader = decompilerContext->shader;
auto shaderType = decompilerContext->shader->shaderType;
auto shaderSrc = decompilerContext->shaderSource;
if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_REMAPPED)
{
// uniform registers or buffers are accessed statically with predictable offsets
// this allows us to remap the used entries into a more compact array
if (shaderType == LatteConst::ShaderType::Vertex)
shaderSrc->addFmt("uniform ivec4 uf_remappedVS[{}];" _CRLF, (sint32)shader->list_remappedUniformEntries.size());
src->addFmt("ivec4 uf_remappedVS[{}];" _CRLF, (sint32)shader->list_remappedUniformEntries.size());
else if (shaderType == LatteConst::ShaderType::Pixel)
shaderSrc->addFmt("uniform ivec4 uf_remappedPS[{}];" _CRLF, (sint32)shader->list_remappedUniformEntries.size());
src->addFmt("ivec4 uf_remappedPS[{}];" _CRLF, (sint32)shader->list_remappedUniformEntries.size());
else if (shaderType == LatteConst::ShaderType::Geometry)
shaderSrc->addFmt("uniform ivec4 uf_remappedGS[{}];" _CRLF, (sint32)shader->list_remappedUniformEntries.size());
src->addFmt("ivec4 uf_remappedGS[{}];" _CRLF, (sint32)shader->list_remappedUniformEntries.size());
else
debugBreakpoint();
uniformOffsets.offset_remapped = uniformCurrentOffset;
@ -30,11 +34,11 @@ namespace LatteDecompiler
uint32 cfileSize = decompilerContext->analyzer.uniformRegisterAccessTracker.DetermineSize(decompilerContext->shaderBaseHash, 256);
// full or partial uniform register file has to be present
if (shaderType == LatteConst::ShaderType::Vertex)
shaderSrc->addFmt("uniform ivec4 uf_uniformRegisterVS[{}];" _CRLF, cfileSize);
src->addFmt("ivec4 uf_uniformRegisterVS[{}];" _CRLF, cfileSize);
else if (shaderType == LatteConst::ShaderType::Pixel)
shaderSrc->addFmt("uniform ivec4 uf_uniformRegisterPS[{}];" _CRLF, cfileSize);
src->addFmt("ivec4 uf_uniformRegisterPS[{}];" _CRLF, cfileSize);
else if (shaderType == LatteConst::ShaderType::Geometry)
shaderSrc->addFmt("uniform ivec4 uf_uniformRegisterGS[{}];" _CRLF, cfileSize);
src->addFmt("ivec4 uf_uniformRegisterGS[{}];" _CRLF, cfileSize);
uniformOffsets.offset_uniformRegister = uniformCurrentOffset;
uniformOffsets.count_uniformRegister = cfileSize;
uniformCurrentOffset += 16 * cfileSize;
@ -49,7 +53,7 @@ namespace LatteDecompiler
{
// aka GX2 special state 0
uniformCurrentOffset = (uniformCurrentOffset + 7)&~7;
shaderSrc->add("uniform vec2 uf_windowSpaceToClipSpaceTransform;" _CRLF);
src->add("float2 uf_windowSpaceToClipSpaceTransform;" _CRLF);
uniformOffsets.offset_windowSpaceToClipSpaceTransform = uniformCurrentOffset;
uniformCurrentOffset += 8;
}
@ -57,7 +61,7 @@ namespace LatteDecompiler
if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel && alphaTestEnable)
{
uniformCurrentOffset = (uniformCurrentOffset + 3)&~3;
shaderSrc->add("uniform float uf_alphaTestRef;" _CRLF);
src->add("float uf_alphaTestRef;" _CRLF);
uniformOffsets.offset_alphaTestRef = uniformCurrentOffset;
uniformCurrentOffset += 4;
}
@ -67,7 +71,7 @@ namespace LatteDecompiler
decompilerContext->shaderType == LatteConst::ShaderType::Geometry)
{
uniformCurrentOffset = (uniformCurrentOffset + 3)&~3;
shaderSrc->add("uniform float uf_pointSize;" _CRLF);
src->add("float uf_pointSize;" _CRLF);
uniformOffsets.offset_pointSize = uniformCurrentOffset;
uniformCurrentOffset += 4;
}
@ -76,7 +80,7 @@ namespace LatteDecompiler
if (shader->shaderType == LatteConst::ShaderType::Pixel)
{
uniformCurrentOffset = (uniformCurrentOffset + 7)&~7;
shaderSrc->add("uniform vec2 uf_fragCoordScale;" _CRLF);
src->add("vec2 uf_fragCoordScale;" _CRLF);
uniformOffsets.offset_fragCoordScale = uniformCurrentOffset;
uniformCurrentOffset += 8;
}
@ -86,7 +90,7 @@ namespace LatteDecompiler
if (decompilerContext->analyzer.texUnitUsesTexelCoordinates.test(t) == false)
continue;
uniformCurrentOffset = (uniformCurrentOffset + 7) & ~7;
shaderSrc->addFmt("uniform vec2 uf_tex{}Scale;" _CRLF, t);
src->addFmt("vec2 uf_tex{}Scale;" _CRLF, t);
uniformOffsets.offset_texScale[t] = uniformCurrentOffset;
uniformCurrentOffset += 8;
}
@ -95,20 +99,22 @@ namespace LatteDecompiler
(shader->shaderType == LatteConst::ShaderType::Vertex && decompilerContext->options->usesGeometryShader == false) ||
(shader->shaderType == LatteConst::ShaderType::Geometry) )
{
shaderSrc->add("uniform int uf_verticesPerInstance;" _CRLF);
src->add("int uf_verticesPerInstance;" _CRLF);
uniformOffsets.offset_verticesPerInstance = uniformCurrentOffset;
uniformCurrentOffset += 4;
for (uint32 i = 0; i < LATTE_NUM_STREAMOUT_BUFFER; i++)
{
if (decompilerContext->output->streamoutBufferWriteMask[i])
{
shaderSrc->addFmt("uniform int uf_streamoutBufferBase{};" _CRLF, i);
src->addFmt("int uf_streamoutBufferBase{};" _CRLF, i);
uniformOffsets.offset_streamoutBufferBase[i] = uniformCurrentOffset;
uniformCurrentOffset += 4;
}
}
}
src->add("}" _CRLF _CRLF);
uniformOffsets.offset_endOfBlock = uniformCurrentOffset;
}
@ -126,13 +132,11 @@ namespace LatteDecompiler
cemu_assert_debug(decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i] >= 0);
cemu_assert_debug(decompilerContext->output->resourceMappingVK.uniformBuffersBindingPoint[i] >= 0);
shaderSrc->addFmt("UNIFORM_BUFFER_LAYOUT({}, {}, {}) ", (sint32)decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i], (sint32)decompilerContext->output->resourceMappingVK.setIndex, (sint32)decompilerContext->output->resourceMappingVK.uniformBuffersBindingPoint[i]);
//shaderSrc->addFmt("UNIFORM_BUFFER_LAYOUT({}, {}, {}) ", (sint32)decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i], (sint32)decompilerContext->output->resourceMappingVK.setIndex, (sint32)decompilerContext->output->resourceMappingVK.uniformBuffersBindingPoint[i]);
shaderSrc->addFmt("uniform ubuff{}" _CRLF, i);
shaderSrc->add("{" _CRLF);
shaderSrc->addFmt("float4 ubuff{}[{}];" _CRLF, i, decompilerContext->analyzer.uniformBufferAccessTracker[i].DetermineSize(decompilerContext->shaderBaseHash, LATTE_GLSL_DYNAMIC_UNIFORM_BLOCK_SIZE));
shaderSrc->addFmt("struct UBuff{} {" _CRLF, i);
shaderSrc->addFmt("float4 d{}[{}];" _CRLF, i, decompilerContext->analyzer.uniformBufferAccessTracker[i].DetermineSize(decompilerContext->shaderBaseHash, LATTE_GLSL_DYNAMIC_UNIFORM_BLOCK_SIZE));
shaderSrc->add("};" _CRLF _CRLF);
shaderSrc->add(_CRLF);
}
}
else if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_REMAPPED)
@ -153,6 +157,161 @@ namespace LatteDecompiler
}
}
static void _emitAttributes(LatteDecompilerShaderContext* decompilerContext)
{
auto src = decompilerContext->shaderSource;
if (decompilerContext->shader->shaderType == LatteConst::ShaderType::Vertex)
{
src->add("struct VertexIn {" _CRLF);
// attribute inputs
for (uint32 i = 0; i < LATTE_NUM_MAX_ATTRIBUTE_LOCATIONS; i++)
{
if (decompilerContext->analyzer.inputAttributSemanticMask[i])
{
cemu_assert_debug(decompilerContext->output->resourceMappingGL.attributeMapping[i] >= 0);
cemu_assert_debug(decompilerContext->output->resourceMappingVK.attributeMapping[i] >= 0);
cemu_assert_debug(decompilerContext->output->resourceMappingGL.attributeMapping[i] == decompilerContext->output->resourceMappingVK.attributeMapping[i]);
src->addFmt("ATTR_LAYOUT({}, {}) in uvec4 attrDataSem{};" _CRLF, (sint32)decompilerContext->output->resourceMappingVK.setIndex, (sint32)decompilerContext->output->resourceMappingVK.attributeMapping[i], i);
}
}
src->add("};" _CRLF _CRLF);
}
}
static void _emitVSOutputs(LatteDecompilerShaderContext* shaderContext)
{
auto* src = shaderContext->shaderSource;
src->add("struct VertexOut {" _CRLF);
src->add("float4 position [[position]];" _CRLF);
if (shaderContext->analyzer.outputPointSize)
src->add("float pointSize[[point_size]];" _CRLF);
LatteShaderPSInputTable* psInputTable = LatteSHRC_GetPSInputTable();
auto parameterMask = shaderContext->shader->outputParameterMask;
for (uint32 i = 0; i < 32; i++)
{
if ((parameterMask&(1 << i)) == 0)
continue;
uint32 vsSemanticId = _getVertexShaderOutParamSemanticId(shaderContext->contextRegisters, i);
if (vsSemanticId > LATTE_ANALYZER_IMPORT_INDEX_PARAM_MAX)
continue;
// get import based on semanticId
sint32 psInputIndex = -1;
for (sint32 f = 0; f < psInputTable->count; f++)
{
if (psInputTable->import[f].semanticId == vsSemanticId)
{
psInputIndex = f;
break;
}
}
if (psInputIndex == -1)
continue; // no ps input
src->addFmt("float4 passParameterSem{}", psInputTable->import[psInputIndex].semanticId);
src->addFmt(" [[user(locn{})]]", psInputIndex);
if (psInputTable->import[psInputIndex].isFlat)
src->add(" [[flat]]");
if (psInputTable->import[psInputIndex].isNoPerspective)
src->add(" [[center_no_perspective]]");
src->addFmt(";" _CRLF);
}
src->add("};" _CRLF _CRLF);
}
static void _emitPSInputs(LatteDecompilerShaderContext* shaderContext)
{
auto* src = shaderContext->shaderSource;
src->add("struct FragmentIn {" _CRLF);
LatteShaderPSInputTable* psInputTable = LatteSHRC_GetPSInputTable();
for (sint32 i = 0; i < psInputTable->count; i++)
{
if (psInputTable->import[i].semanticId > LATTE_ANALYZER_IMPORT_INDEX_PARAM_MAX)
continue;
src->addFmt("float4 passParameterSem{}", psInputTable->import[i].semanticId);
src->addFmt(" [[user(locn{})]]", i);
if (psInputTable->import[i].isFlat)
src->add(" [[flat]]");
if (psInputTable->import[i].isNoPerspective)
src->add(" [[center_no_perspective]]");
src->add(";" _CRLF);
}
src->add("};" _CRLF _CRLF);
}
static void _emitInputsAndOutputs(LatteDecompilerShaderContext* decompilerContext)
{
auto src = decompilerContext->shaderSource;
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex)
{
_emitAttributes(decompilerContext);
_emitVSOutputs(decompilerContext);
// TODO: transform feedback
}
else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel)
{
_emitPSInputs(decompilerContext);
src->add("struct FragmentOut {" _CRLF);
// generate pixel outputs for pixel shader
for (uint32 i = 0; i < LATTE_NUM_COLOR_TARGET; i++)
{
if ((decompilerContext->shader->pixelColorOutputMask&(1 << i)) != 0)
{
src->addFmt("float4 passPixelColor{} [[color({})]];" _CRLF, i, i);
}
}
src->add("};" _CRLF _CRLF);
}
}
static void emitHeader(LatteDecompilerShaderContext* decompilerContext)
{
const bool dump_shaders_enabled = ActiveSettings::DumpShadersEnabled();
if(dump_shaders_enabled)
decompilerContext->shaderSource->add("// start of shader inputs/outputs, predetermined by Cemu. Do not touch" _CRLF);
// uniform variables
_emitUniformVariables(decompilerContext);
// uniform buffers
_emitUniformBuffers(decompilerContext);
// inputs and outputs
_emitInputsAndOutputs(decompilerContext);
if (dump_shaders_enabled)
decompilerContext->shaderSource->add("// end of shader inputs/outputs" _CRLF);
}
static void _emitUniformBufferDefinitions(LatteDecompilerShaderContext* decompilerContext)
{
auto src = decompilerContext->shaderSource;
// uniform buffer definition
if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK)
{
for (uint32 i = 0; i < LATTE_NUM_MAX_UNIFORM_BUFFERS; i++)
{
if (!decompilerContext->analyzer.uniformBufferAccessTracker[i].HasAccess())
continue;
cemu_assert_debug(decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i] >= 0);
cemu_assert_debug(decompilerContext->output->resourceMappingVK.uniformBuffersBindingPoint[i] >= 0);
src->addFmt("constant UBuff{}& ubuff{} [[buffer({})]]" _CRLF, i, i, (sint32)decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i]);
}
}
}
static void _emitTextureDefinitions(LatteDecompilerShaderContext* shaderContext)
{
auto src = shaderContext->shaderSource;
@ -162,6 +321,8 @@ namespace LatteDecompiler
if (!shaderContext->output->textureUnitMask[i])
continue;
src->add(", ");
if (shaderContext->shader->textureIsIntegerFormat[i])
{
// integer samplers
@ -187,240 +348,29 @@ namespace LatteDecompiler
cemu_assert_unimplemented();
}
src->addFmt(" tex{} [[texture({})]], ", i, shaderContext->output->resourceMappingGL.textureUnitToBindingPoint[i]);
src->addFmt("sampler samplr{} [[sampler({})]], ", i, shaderContext->output->resourceMappingGL.textureUnitToBindingPoint[i]);
src->addFmt(" tex{} [[texture({})]]", i, shaderContext->output->resourceMappingGL.textureUnitToBindingPoint[i]);
src->addFmt(", sampler samplr{} [[sampler({})]]", i, shaderContext->output->resourceMappingGL.textureUnitToBindingPoint[i]);
}
}
static void _emitAttributes(LatteDecompilerShaderContext* decompilerContext)
static void emitInputs(LatteDecompilerShaderContext* decompilerContext)
{
auto shaderSrc = decompilerContext->shaderSource;
if (decompilerContext->shader->shaderType == LatteConst::ShaderType::Vertex)
auto src = decompilerContext->shaderSource;
switch (decompilerContext->shaderType)
{
// attribute inputs
for (uint32 i = 0; i < LATTE_NUM_MAX_ATTRIBUTE_LOCATIONS; i++)
{
if (decompilerContext->analyzer.inputAttributSemanticMask[i])
{
cemu_assert_debug(decompilerContext->output->resourceMappingGL.attributeMapping[i] >= 0);
cemu_assert_debug(decompilerContext->output->resourceMappingVK.attributeMapping[i] >= 0);
cemu_assert_debug(decompilerContext->output->resourceMappingGL.attributeMapping[i] == decompilerContext->output->resourceMappingVK.attributeMapping[i]);
shaderSrc->addFmt("ATTR_LAYOUT({}, {}) in uvec4 attrDataSem{};" _CRLF, (sint32)decompilerContext->output->resourceMappingVK.setIndex, (sint32)decompilerContext->output->resourceMappingVK.attributeMapping[i], i);
}
}
case LatteConst::ShaderType::Vertex:
src->add("VertexIn");
break;
case LatteConst::ShaderType::Pixel:
src->add("FragmentIn");
break;
}
}
static void _emitVSExports(LatteDecompilerShaderContext* shaderContext)
{
auto* src = shaderContext->shaderSource;
LatteShaderPSInputTable* psInputTable = LatteSHRC_GetPSInputTable();
auto parameterMask = shaderContext->shader->outputParameterMask;
for (uint32 i = 0; i < 32; i++)
{
if ((parameterMask&(1 << i)) == 0)
continue;
uint32 vsSemanticId = _getVertexShaderOutParamSemanticId(shaderContext->contextRegisters, i);
if (vsSemanticId > LATTE_ANALYZER_IMPORT_INDEX_PARAM_MAX)
continue;
// get import based on semanticId
sint32 psInputIndex = -1;
for (sint32 f = 0; f < psInputTable->count; f++)
{
if (psInputTable->import[f].semanticId == vsSemanticId)
{
psInputIndex = f;
break;
}
}
if (psInputIndex == -1)
continue; // no ps input
src->addFmt("layout(location = {}) ", psInputIndex);
if (psInputTable->import[psInputIndex].isFlat)
src->add("flat ");
if (psInputTable->import[psInputIndex].isNoPerspective)
src->add("noperspective ");
src->add("out");
src->addFmt(" vec4 passParameterSem{};" _CRLF, psInputTable->import[psInputIndex].semanticId);
}
}
static void _emitPSImports(LatteDecompilerShaderContext* shaderContext)
{
auto* src = shaderContext->shaderSource;
LatteShaderPSInputTable* psInputTable = LatteSHRC_GetPSInputTable();
for (sint32 i = 0; i < psInputTable->count; i++)
{
if (psInputTable->import[i].semanticId > LATTE_ANALYZER_IMPORT_INDEX_PARAM_MAX)
continue;
src->addFmt("layout(location = {}) ", i);
if (psInputTable->import[i].isFlat)
src->add("flat ");
if (psInputTable->import[i].isNoPerspective)
src->add("noperspective ");
src->add("in");
src->addFmt(" vec4 passParameterSem{};" _CRLF, psInputTable->import[i].semanticId);
}
}
static void _emitMisc(LatteDecompilerShaderContext* decompilerContext)
{
auto src = decompilerContext->shaderSource;
// per-vertex output (VS or GS)
if ((decompilerContext->shaderType == LatteConst::ShaderType::Vertex && !decompilerContext->options->usesGeometryShader) ||
(decompilerContext->shaderType == LatteConst::ShaderType::Geometry))
{
src->add("out gl_PerVertex" _CRLF);
src->add("{" _CRLF);
src->add(" vec4 gl_Position;" _CRLF);
if (decompilerContext->analyzer.outputPointSize)
src->add(" float gl_PointSize;" _CRLF);
src->add("};" _CRLF);
}
// varyings (variables passed from vertex to pixel shader, only if geometry stage is disabled
if (decompilerContext->options->usesGeometryShader == false)
{
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex)
{
_emitVSExports(decompilerContext);
}
else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel)
{
_emitPSImports(decompilerContext);
}
}
else
{
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex || decompilerContext->shaderType == LatteConst::ShaderType::Geometry)
{
// parameters shared between vertex shader and geometry shader
src->add("V2G_LAYOUT ");
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex)
src->add("out Vertex" _CRLF);
else if (decompilerContext->shaderType == LatteConst::ShaderType::Geometry)
src->add("in Vertex" _CRLF);
src->add("{" _CRLF);
uint32 ringParameterCountVS2GS = 0;
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex)
{
ringParameterCountVS2GS = decompilerContext->shader->ringParameterCount;
}
else
{
ringParameterCountVS2GS = decompilerContext->shader->ringParameterCountFromPrevStage;
}
for (uint32 f = 0; f < ringParameterCountVS2GS; f++)
src->addFmt(" ivec4 passV2GParameter{};" _CRLF, f);
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex)
src->add("}v2g;" _CRLF);
else if (decompilerContext->shaderType == LatteConst::ShaderType::Geometry)
src->add("}v2g[];" _CRLF);
}
if (decompilerContext->shaderType == LatteConst::ShaderType::Geometry)
{
// parameters shared between geometry and pixel shader
uint32 ringItemSize = decompilerContext->contextRegisters[mmSQ_GSVS_RING_ITEMSIZE] & 0x7FFF;
if ((ringItemSize & 0xF) != 0)
debugBreakpoint();
if (((decompilerContext->contextRegisters[mmSQ_GSVS_RING_ITEMSIZE] & 0x7FFF) & 0xF) != 0)
debugBreakpoint();
for (sint32 p = 0; p < decompilerContext->parsedGSCopyShader->numParam; p++)
{
if (decompilerContext->parsedGSCopyShader->paramMapping[p].exportType != 2)
continue;
src->addFmt("layout(location = {}) out vec4 passG2PParameter{};" _CRLF, decompilerContext->parsedGSCopyShader->paramMapping[p].exportParam & 0x7F, (sint32)decompilerContext->parsedGSCopyShader->paramMapping[p].exportParam);
}
}
else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel)
{
// pixel shader with geometry shader
LatteShaderPSInputTable* psInputTable = LatteSHRC_GetPSInputTable();
for (sint32 i = 0; i < psInputTable->count; i++)
{
if (psInputTable->import[i].semanticId > LATTE_ANALYZER_IMPORT_INDEX_PARAM_MAX)
continue;
uint32 location = psInputTable->import[i].semanticId & 0x7F; // todo - the range above 128 has special meaning?
src->addFmt("layout(location = {}) ", location);
if (psInputTable->import[i].isFlat)
src->add("flat ");
if (psInputTable->import[i].isNoPerspective)
src->add("noperspective ");
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex)
src->add("out");
else if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel)
src->add("in");
else
debugBreakpoint();
src->addFmt(" vec4 passG2PParameter{};" _CRLF, (sint32)location);
}
}
}
// output defines
if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel)
{
// generate pixel outputs for pixel shader
for (uint32 i = 0; i < LATTE_NUM_COLOR_TARGET; i++)
{
if ((decompilerContext->shader->pixelColorOutputMask&(1 << i)) != 0)
{
src->addFmt("layout(location = {}) out vec4 passPixelColor{};" _CRLF, i, i);
}
}
}
// streamout buffer (transform feedback)
if ((decompilerContext->shaderType == LatteConst::ShaderType::Vertex || decompilerContext->shaderType == LatteConst::ShaderType::Geometry) && decompilerContext->analyzer.hasStreamoutEnable)
{
if (decompilerContext->options->useTFViaSSBO)
{
if (decompilerContext->analyzer.useSSBOForStreamout && decompilerContext->analyzer.hasStreamoutWrite)
{
src->addFmt("layout(set = {}, binding = {}) buffer StreamoutBuffer" _CRLF, decompilerContext->output->resourceMappingVK.setIndex, decompilerContext->output->resourceMappingVK.getTFStorageBufferBindingPoint());
src->add("{" _CRLF);
src->add("int sb_buffer[];" _CRLF);
src->add("};" _CRLF);
}
}
else
{
sint32 locationOffset = 0; // glslang wants a location for xfb outputs
for (uint32 i = 0; i < LATTE_NUM_STREAMOUT_BUFFER; i++)
{
if (!decompilerContext->output->streamoutBufferWriteMask[i])
continue;
uint32 bufferStride = decompilerContext->output->streamoutBufferStride[i];
src->addFmt("XFB_BLOCK_LAYOUT({}, {}, {}) out XfbBlock{} " _CRLF, i, bufferStride, locationOffset, i);
src->add("{" _CRLF);
src->addFmt("layout(xfb_buffer = {}, xfb_offset = 0) int sb{}[{}];" _CRLF, i, i, decompilerContext->output->streamoutBufferStride[i] / 4);
src->add("};" _CRLF);
locationOffset += (decompilerContext->output->streamoutBufferStride[i] / 4);
}
}
}
}
static void emitHeader(LatteDecompilerShaderContext* decompilerContext)
{
const bool dump_shaders_enabled = ActiveSettings::DumpShadersEnabled();
if(dump_shaders_enabled)
decompilerContext->shaderSource->add("// start of shader inputs/outputs, predetermined by Cemu. Do not touch" _CRLF);
// uniform variables
_emitUniformVariables(decompilerContext, decompilerContext->output->uniformOffsetsVK);
src->add(" in [[stage_in]], DefaultVariables defaultVars [[buffer(29)]]");
// uniform buffers
_emitUniformBuffers(decompilerContext);
_emitUniformBufferDefinitions(decompilerContext);
// textures
_emitTextureDefinitions(decompilerContext);
// attributes
_emitAttributes(decompilerContext);
// misc stuff
_emitMisc(decompilerContext);
if (dump_shaders_enabled)
decompilerContext->shaderSource->add("// end of shader inputs/outputs" _CRLF);
}
}