mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-29 20:44:18 +01:00
start the shader decompiler
This commit is contained in:
parent
46981d7b03
commit
69597166f3
@ -151,6 +151,9 @@ add_library(CemuCafe
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLAttrDecoder.cpp
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSL.cpp
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitGLSLHeader.hpp
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLAttrDecoder.cpp
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSLHeader.hpp
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerInstructions.h
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
|
||||
#include "util/helpers/helpers.h"
|
||||
// TODO: remove this include
|
||||
#include "util/helpers/StringBuf.h"
|
||||
|
||||
// parse instruction and if valid append it to instructionList
|
||||
bool LatteDecompiler_ParseCFInstruction(LatteDecompilerShaderContext* shaderContext, uint32 cfIndex, uint32 cfWord0, uint32 cfWord1, bool* endOfProgram, std::vector<LatteDecompilerCFInstruction>& instructionList)
|
||||
@ -323,8 +325,8 @@ bool LatteDecompiler_IsALUTransInstruction(bool isOP3, uint32 opcode)
|
||||
}
|
||||
else if( opcode == ALU_OP2_INST_MOV ||
|
||||
opcode == ALU_OP2_INST_ADD ||
|
||||
opcode == ALU_OP2_INST_NOP ||
|
||||
opcode == ALU_OP2_INST_MUL ||
|
||||
opcode == ALU_OP2_INST_NOP ||
|
||||
opcode == ALU_OP2_INST_MUL ||
|
||||
opcode == ALU_OP2_INST_DOT4 ||
|
||||
opcode == ALU_OP2_INST_DOT4_IEEE ||
|
||||
opcode == ALU_OP2_INST_MAX || // Not sure if MIN/MAX are non-transcendental?
|
||||
@ -927,7 +929,7 @@ void LatteDecompiler_ParseTEXClause(LatteDecompilerShader* shaderContext, LatteD
|
||||
texInstruction.memRead.format = dataFormat;
|
||||
texInstruction.memRead.nfa = nfa;
|
||||
texInstruction.memRead.isSigned = isSigned;
|
||||
|
||||
|
||||
cfInstruction->instructionsTEX.emplace_back(texInstruction);
|
||||
}
|
||||
else
|
||||
@ -1066,9 +1068,19 @@ void _LatteDecompiler_Process(LatteDecompilerShaderContext* shaderContext, uint8
|
||||
LatteDecompiler_analyzeDataTypes(shaderContext);
|
||||
// emit code
|
||||
if (shaderContext->shader->hasError == false)
|
||||
LatteDecompiler_emitGLSLShader(shaderContext, shaderContext->shader);
|
||||
{
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
{
|
||||
LatteDecompiler_emitMSLShader(shaderContext, shaderContext->shader);
|
||||
// HACK
|
||||
std::cout << shaderContext->shaderSource->c_str() << std::endl;
|
||||
} else
|
||||
{
|
||||
LatteDecompiler_emitGLSLShader(shaderContext, shaderContext->shader);
|
||||
}
|
||||
}
|
||||
LatteDecompiler_cleanup(shaderContext);
|
||||
// fast access
|
||||
// fast access
|
||||
_LatteDecompiler_GenerateDataForFastAccess(shaderContext->shader);
|
||||
}
|
||||
|
||||
|
4127
src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp
Normal file
4127
src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,508 @@
|
||||
#include "Cafe/HW/Latte/Core/LatteConst.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteShaderAssembly.h"
|
||||
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDraw.h"
|
||||
#include "Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h"
|
||||
#include "Cafe/HW/Latte/Core/FetchShader.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "util/helpers/StringBuf.h"
|
||||
|
||||
#define _CRLF "\r\n"
|
||||
|
||||
static void _readLittleEndianAttributeU32x4(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder = attrDataSem{};" _CRLF, attributeInputIndex);
|
||||
}
|
||||
|
||||
static void _readLittleEndianAttributeU32x3(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder = uint4(attrDataSem{}.xyz,0);" _CRLF, attributeInputIndex);
|
||||
}
|
||||
|
||||
static void _readLittleEndianAttributeU32x2(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder = uint4(attrDataSem{}.xy,0,0);" _CRLF, attributeInputIndex);
|
||||
}
|
||||
|
||||
static void _readLittleEndianAttributeU32x1(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder = uint4(attrDataSem{}.x,0,0,0);" _CRLF, attributeInputIndex);
|
||||
}
|
||||
|
||||
static void _readLittleEndianAttributeU16x2(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder = uint4(attrDataSem{}.xy,0,0);" _CRLF, attributeInputIndex);
|
||||
}
|
||||
|
||||
static void _readLittleEndianAttributeU16x4(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder = attrDataSem{};" _CRLF, attributeInputIndex);
|
||||
}
|
||||
|
||||
static void _readBigEndianAttributeU32x4(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder = attrDataSem{};" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder = (attrDecoder>>24)|((attrDecoder>>8)&0xFF00)|((attrDecoder<<8)&0xFF0000)|((attrDecoder<<24));" _CRLF);
|
||||
}
|
||||
|
||||
static void _readBigEndianAttributeU32x3(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder.xyz = attrDataSem{}.xyz;" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.xyz = (attrDecoder.xyz>>24)|((attrDecoder.xyz>>8)&0xFF00)|((attrDecoder.xyz<<8)&0xFF0000)|((attrDecoder.xyz<<24));" _CRLF);
|
||||
src->add("attrDecoder.w = 0;" _CRLF);
|
||||
}
|
||||
|
||||
static void _readBigEndianAttributeU32x2(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder.xy = attrDataSem{}.xy;" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.xy = (attrDecoder.xy>>24)|((attrDecoder.xy>>8)&0xFF00)|((attrDecoder.xy<<8)&0xFF0000)|((attrDecoder.xy<<24));" _CRLF);
|
||||
src->add("attrDecoder.z = 0;" _CRLF);
|
||||
src->add("attrDecoder.w = 0;" _CRLF);
|
||||
}
|
||||
|
||||
static void _readBigEndianAttributeU32x1(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder.x = attrDataSem{}.x;" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.x = (attrDecoder.x>>24)|((attrDecoder.x>>8)&0xFF00)|((attrDecoder.x<<8)&0xFF0000)|((attrDecoder.x<<24));" _CRLF);
|
||||
src->add("attrDecoder.y = 0;" _CRLF);
|
||||
src->add("attrDecoder.z = 0;" _CRLF);
|
||||
src->add("attrDecoder.w = 0;" _CRLF);
|
||||
}
|
||||
|
||||
static void _readBigEndianAttributeU16x1(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder.xy = attrDataSem{}.xy;" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.x = ((attrDecoder.x>>8)&0xFF)|((attrDecoder.x<<8)&0xFF00);" _CRLF);
|
||||
src->add("attrDecoder.y = 0;" _CRLF);
|
||||
src->add("attrDecoder.z = 0;" _CRLF);
|
||||
src->add("attrDecoder.w = 0;" _CRLF);
|
||||
}
|
||||
|
||||
static void _readBigEndianAttributeU16x2(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder.xy = attrDataSem{}.xy;" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.xy = ((attrDecoder.xy>>8)&0xFF)|((attrDecoder.xy<<8)&0xFF00);" _CRLF);
|
||||
src->add("attrDecoder.z = 0;" _CRLF);
|
||||
src->add("attrDecoder.w = 0;" _CRLF);
|
||||
}
|
||||
|
||||
static void _readBigEndianAttributeU16x4(LatteDecompilerShader* shaderContext, StringBuf* src, uint32 attributeInputIndex)
|
||||
{
|
||||
src->addFmt("attrDecoder.xyzw = attrDataSem{}.xyzw;" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder = ((attrDecoder>>8)&0xFF)|((attrDecoder<<8)&0xFF00);" _CRLF);
|
||||
}
|
||||
|
||||
void LatteDecompiler_emitAttributeDecodeMSL(LatteDecompilerShader* shaderContext, StringBuf* src, LatteParsedFetchShaderAttribute_t* attrib)
|
||||
{
|
||||
if (attrib->attributeBufferIndex >= Latte::GPU_LIMITS::NUM_VERTEX_BUFFERS)
|
||||
{
|
||||
src->add("attrDecoder = int4(0);" _CRLF);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 attributeInputIndex = attrib->semanticId;
|
||||
if( attrib->endianSwap == LatteConst::VertexFetchEndianMode::SWAP_U32 )
|
||||
{
|
||||
if( attrib->format == FMT_32_32_32_32_FLOAT && attrib->nfa == 2 )
|
||||
{
|
||||
_readBigEndianAttributeU32x4(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_32_32_32_FLOAT && attrib->nfa == 2 )
|
||||
{
|
||||
_readBigEndianAttributeU32x3(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_32_32_FLOAT && attrib->nfa == 2 )
|
||||
{
|
||||
_readBigEndianAttributeU32x2(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_32_FLOAT && attrib->nfa == 2 )
|
||||
{
|
||||
_readBigEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_2_10_10_10 && attrib->nfa == 0 )
|
||||
{
|
||||
_readBigEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
|
||||
// Bayonetta 2 uses this format to store normals
|
||||
src->add("attrDecoder.xyzw = uint4((attrDecoder.x>>0)&0x3FF,(attrDecoder.x>>10)&0x3FF,(attrDecoder.x>>20)&0x3FF,(attrDecoder.x>>30)&0x3);" _CRLF);
|
||||
if (attrib->isSigned != 0)
|
||||
{
|
||||
src->add("if( (attrDecoder.x&0x200) != 0 ) attrDecoder.x |= 0xFFFFFC00;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x200) != 0 ) attrDecoder.y |= 0xFFFFFC00;" _CRLF);
|
||||
src->add("if( (attrDecoder.z&0x200) != 0 ) attrDecoder.z |= 0xFFFFFC00;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/511.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/511.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.z = as_type<uint>(max(float(int(attrDecoder.z))/511.0,-1.0));" _CRLF);
|
||||
}
|
||||
else
|
||||
{
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/1023.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/1023.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.z = as_type<uint>(max(float(int(attrDecoder.z))/1023.0,-1.0));" _CRLF);
|
||||
}
|
||||
src->add("attrDecoder.w = as_type<uint>(float(attrDecoder.w));" _CRLF); // unsure?
|
||||
|
||||
}
|
||||
else if( attrib->format == FMT_32_32_32_32 && attrib->nfa == 1 && attrib->isSigned == 0 )
|
||||
{
|
||||
_readBigEndianAttributeU32x4(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_32_32_32 && attrib->nfa == 1 && attrib->isSigned == 0 )
|
||||
{
|
||||
_readBigEndianAttributeU32x3(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_32_32 && attrib->nfa == 1 && attrib->isSigned == 0 )
|
||||
{
|
||||
_readBigEndianAttributeU32x2(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_32 && attrib->nfa == 1 && attrib->isSigned == 0)
|
||||
{
|
||||
_readBigEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_32 && attrib->nfa == 1 && attrib->isSigned == 1)
|
||||
{
|
||||
// we can just read the signed s32 as a u32 since no sign-extension is necessary
|
||||
_readBigEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 0 && attrib->isSigned == 0 )
|
||||
{
|
||||
// seen in Minecraft Wii U Edition
|
||||
src->addFmt("attrDecoder.xyzw = as_type<uint>(vec4(attrDataSem{}.wzyx)/255.0);" _CRLF, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 0 && attrib->isSigned != 0 )
|
||||
{
|
||||
// seen in Minecraft Wii U Edition
|
||||
src->addFmt("attrDecoder.xyzw = attrDataSem{}.wzyx;" _CRLF, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.z&0x80) != 0 ) attrDecoder.z |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.w&0x80) != 0 ) attrDecoder.w |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.z = as_type<uint>(max(float(int(attrDecoder.z))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.w = as_type<uint>(max(float(int(attrDecoder.w))/127.0,-1.0));" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 1 && attrib->isSigned == 0 )
|
||||
{
|
||||
// seen in Minecraft Wii U Edition
|
||||
src->addFmt("attrDecoder.xyzw = attrDataSem{}.wzyx;" _CRLF, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_8_8_8_8 && attrib->nfa == 2 && attrib->isSigned == 0)
|
||||
{
|
||||
// seen in Ben 10 Omniverse
|
||||
src->addFmt("attrDecoder.xyzw = as_type<uint>(vec4(attrDataSem{}.wzyx));" _CRLF, attributeInputIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
cemuLog_log(LogType::Force, "_emitAttributeDecode(): Unsupported fmt {:02x} nfa {} signed {} endian {}\n", attrib->format, attrib->nfa, attrib->isSigned, attrib->endianSwap);
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
}
|
||||
else if( attrib->endianSwap == LatteConst::VertexFetchEndianMode::SWAP_NONE )
|
||||
{
|
||||
if( attrib->format == FMT_32_32_32_32_FLOAT && attrib->nfa == 2 )
|
||||
{
|
||||
_readLittleEndianAttributeU32x4(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_32_32_32_FLOAT && attrib->nfa == 2)
|
||||
{
|
||||
_readLittleEndianAttributeU32x3(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_32_32_FLOAT && attrib->nfa == 2)
|
||||
{
|
||||
// seen in Cities of Gold
|
||||
_readLittleEndianAttributeU32x2(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_32 && attrib->nfa == 1 && attrib->isSigned == 0)
|
||||
{
|
||||
// seen in Nano Assault Neo
|
||||
_readLittleEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_2_10_10_10 && attrib->nfa == 0 && attrib->isSigned == 0)
|
||||
{
|
||||
// seen in Fast Racing Neo
|
||||
_readLittleEndianAttributeU32x1(shaderContext, src, attributeInputIndex);
|
||||
src->add("attrDecoder.xyzw = uint4((attrDecoder.x>>0)&0x3FF,(attrDecoder.x>>10)&0x3FF,(attrDecoder.x>>20)&0x3FF,(attrDecoder.x>>30)&0x3);" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/1023.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/1023.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.z = as_type<uint>(max(float(int(attrDecoder.z))/1023.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.w = as_type<uint>(float(attrDecoder.w));" _CRLF); // todo - is this correct?
|
||||
}
|
||||
else if (attrib->format == FMT_16_16_16_16 && attrib->nfa == 0 && attrib->isSigned != 0)
|
||||
{
|
||||
// seen in CoD ghosts
|
||||
_readLittleEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.z&0x8000) != 0 ) attrDecoder.z |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.w&0x8000) != 0 ) attrDecoder.w |= 0xFFFF0000;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/32767.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/32767.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.z = as_type<uint>(max(float(int(attrDecoder.z))/32767.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.w = as_type<uint>(max(float(int(attrDecoder.w))/32767.0,-1.0));" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16_16_16 && attrib->nfa == 2 && attrib->isSigned == 1 )
|
||||
{
|
||||
// seen in Rabbids Land
|
||||
_readLittleEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.z&0x8000) != 0 ) attrDecoder.z |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.w&0x8000) != 0 ) attrDecoder.w |= 0xFFFF0000;" _CRLF);
|
||||
src->add("attrDecoder.xyzw = as_type<uint4>(float4(int4(attrDecoder)));" _CRLF);
|
||||
}
|
||||
else if (attrib->format == FMT_16_16_16_16_FLOAT && attrib->nfa == 2)
|
||||
{
|
||||
// seen in Giana Sisters: Twisted Dreams
|
||||
_readLittleEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
|
||||
src->add("attrDecoder.xyzw = as_type<int4>(float4(unpackHalf2x16(attrDecoder.x|(attrDecoder.y<<16)),unpackHalf2x16(attrDecoder.z|(attrDecoder.w<<16))));" _CRLF);
|
||||
}
|
||||
else if (attrib->format == FMT_16_16 && attrib->nfa == 0 && attrib->isSigned != 0)
|
||||
{
|
||||
// seen in Nano Assault Neo
|
||||
_readLittleEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/32767.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/32767.0,-1.0));" _CRLF);
|
||||
}
|
||||
else if (attrib->format == FMT_16_16_FLOAT && attrib->nfa == 2)
|
||||
{
|
||||
// seen in Giana Sisters: Twisted Dreams
|
||||
_readLittleEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
|
||||
src->add("attrDecoder.xy = as_type<uint2>(unpackHalf2x16(attrDecoder.x|(attrDecoder.y<<16)));" _CRLF);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 0 && attrib->isSigned == 0 )
|
||||
{
|
||||
src->addFmt("attrDecoder.xyzw = as_type<uint4>(float4(attrDataSem{}.xyzw)/255.0);" _CRLF, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 0 && attrib->isSigned != 0 )
|
||||
{
|
||||
src->addFmt("attrDecoder.xyzw = attrDataSem{}.xyzw;" _CRLF, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.z&0x80) != 0 ) attrDecoder.z |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.w&0x80) != 0 ) attrDecoder.w |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.z = as_type<uint>(max(float(int(attrDecoder.z))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.w = as_type<uint>(max(float(int(attrDecoder.w))/127.0,-1.0));" _CRLF);
|
||||
}
|
||||
else if (attrib->format == FMT_8_8_8_8 && attrib->nfa == 1 && attrib->isSigned == 0)
|
||||
{
|
||||
src->addFmt("attrDecoder.xyzw = attrDataSem{}.xyzw;" _CRLF, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_8_8_8_8 && attrib->nfa == 1 && attrib->isSigned != 0)
|
||||
{
|
||||
// seen in Sonic Lost World
|
||||
src->addFmt("attrDecoder.xyzw = attrDataSem{}.xyzw;" _CRLF, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.z&0x80) != 0 ) attrDecoder.z |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.w&0x80) != 0 ) attrDecoder.w |= 0xFFFFFF00;" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_8_8_8_8 && attrib->nfa == 2 && attrib->isSigned == 0 )
|
||||
{
|
||||
// seen in One Piece
|
||||
src->addFmt("attrDecoder.xyzw = as_type<int4>(float4(attrDataSem{}.xyzw));" _CRLF, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_8_8 && attrib->nfa == 0 && attrib->isSigned == 0)
|
||||
{
|
||||
if( (attrib->offset&3) == 2 && LatteGPUState.glVendor == GLVENDOR_AMD && g_renderer->GetType() == RendererAPI::OpenGL )
|
||||
{
|
||||
// AMD workaround
|
||||
src->addFmt("attrDecoder.xy = as_type<uint2>(float2(attrDataSem{}.zw)/255.0);" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else
|
||||
{
|
||||
src->addFmt("attrDecoder.xy = as_type<uint2>(float2(attrDataSem{}.xy)/255.0);" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
}
|
||||
else if (attrib->format == FMT_8_8 && attrib->nfa == 2 && attrib->isSigned == 0)
|
||||
{
|
||||
// seen in BotW
|
||||
if ((attrib->offset & 3) == 2 && LatteGPUState.glVendor == GLVENDOR_AMD && g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
// AMD workaround
|
||||
src->addFmt("attrDecoder.xy = as_type<uint2>(float2(attrDataSem{}.zw));" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else
|
||||
{
|
||||
src->addFmt("attrDecoder.xy = as_type<uint2>(float2(attrDataSem{}.xy));" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
}
|
||||
else if (attrib->format == FMT_8_8 && attrib->nfa == 0 && attrib->isSigned != 0)
|
||||
{
|
||||
if ((attrib->offset & 3) == 2 && LatteGPUState.glVendor == GLVENDOR_AMD && g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
// AMD workaround
|
||||
src->addFmt("attrDecoder.xy = attrDataSem{}.zw;" _CRLF, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else
|
||||
{
|
||||
src->addFmt("attrDecoder.xy = attrDataSem{}.xy;" _CRLF, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x80) != 0 ) attrDecoder.x |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x80) != 0 ) attrDecoder.y |= 0xFFFFFF00;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/127.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
}
|
||||
else if (attrib->format == FMT_8_8 && attrib->nfa == 1 && attrib->isSigned == 0)
|
||||
{
|
||||
if ((attrib->offset & 3) == 2 && LatteGPUState.glVendor == GLVENDOR_AMD && g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
// AMD workaround
|
||||
src->addFmt("attrDecoder.xyzw = uint4(attrDataSem{}.zw,0,0);" _CRLF, attributeInputIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
src->addFmt("attrDecoder.xyzw = uint4(attrDataSem{}.xy,0,0);" _CRLF, attributeInputIndex);
|
||||
}
|
||||
}
|
||||
else if( attrib->format == FMT_8 && attrib->nfa == 0 && attrib->isSigned == 0 )
|
||||
{
|
||||
// seen in Pikmin 3
|
||||
src->addFmt("attrDecoder.x = as_type<uint>(float(attrDataSem{}.x)/255.0);" _CRLF, attributeInputIndex);
|
||||
src->add("attrDecoder.yzw = uint3(0);" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_8 && attrib->nfa == 1 && attrib->isSigned == 0 )
|
||||
{
|
||||
src->addFmt("attrDecoder.xyzw = uint4(attrDataSem{}.x,0,0,0);" _CRLF, attributeInputIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
cemuLog_log(LogType::Force, "_emitAttributeDecode(): Unsupported fmt {:02x} nfa {} signed {} endian {}\n", attrib->format, attrib->nfa, attrib->isSigned, attrib->endianSwap);
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
}
|
||||
else if( attrib->endianSwap == LatteConst::VertexFetchEndianMode::SWAP_U16 )
|
||||
{
|
||||
if( attrib->format == FMT_16_16_16_16_FLOAT && attrib->nfa == 2 )
|
||||
{
|
||||
_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
|
||||
src->add("attrDecoder.xyzw = as_type<int4>(float4(unpackHalf2x16(attrDecoder.x|(attrDecoder.y<<16)),unpackHalf2x16(attrDecoder.z|(attrDecoder.w<<16))));" _CRLF);
|
||||
}
|
||||
else if (attrib->format == FMT_16_16_16_16 && attrib->nfa == 0 && attrib->isSigned != 0)
|
||||
{
|
||||
_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.z&0x8000) != 0 ) attrDecoder.z |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.w&0x8000) != 0 ) attrDecoder.w |= 0xFFFF0000;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/32767.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/32767.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.z = as_type<uint>(max(float(int(attrDecoder.z))/32767.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.w = as_type<uint>(max(float(int(attrDecoder.w))/32767.0,-1.0));" _CRLF);
|
||||
}
|
||||
else if (attrib->format == FMT_16_16_16_16 && attrib->nfa == 0 && attrib->isSigned == 0)
|
||||
{
|
||||
// seen in BotW
|
||||
_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
|
||||
src->add("attrDecoder.x = as_type<uint>(float(int(attrDecoder.x))/65535.0);" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(float(int(attrDecoder.y))/65535.0);" _CRLF);
|
||||
src->add("attrDecoder.z = as_type<uint>(float(int(attrDecoder.z))/65535.0);" _CRLF);
|
||||
src->add("attrDecoder.w = as_type<uint>(float(int(attrDecoder.w))/65535.0);" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16_16_16 && attrib->nfa == 2 && attrib->isSigned != 0 )
|
||||
{
|
||||
// seen in Minecraft Wii U Edition
|
||||
_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.z&0x8000) != 0 ) attrDecoder.z |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.w&0x8000) != 0 ) attrDecoder.w |= 0xFFFF0000;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(float(int(attrDecoder.x)));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(float(int(attrDecoder.y)));" _CRLF);
|
||||
src->add("attrDecoder.z = as_type<uint>(float(int(attrDecoder.z)));" _CRLF);
|
||||
src->add("attrDecoder.w = as_type<uint>(float(int(attrDecoder.w)));" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16_16_16 && attrib->nfa == 1 && attrib->isSigned != 0 )
|
||||
{
|
||||
// seen in Minecraft Wii U Edition
|
||||
_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.z&0x8000) != 0 ) attrDecoder.z |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.w&0x8000) != 0 ) attrDecoder.w |= 0xFFFF0000;" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16_16_16 && attrib->nfa == 1 && attrib->isSigned == 0 )
|
||||
{
|
||||
_readBigEndianAttributeU16x4(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16_FLOAT && attrib->nfa == 2 )
|
||||
{
|
||||
_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
|
||||
src->add("attrDecoder.xy = as_type<uint2>(unpackHalf2x16(attrDecoder.x|(attrDecoder.y<<16)));" _CRLF);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16 && attrib->nfa == 0 && attrib->isSigned == 0 )
|
||||
{
|
||||
_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
|
||||
src->add("attrDecoder.xy = as_type<uint2>(float2(float(attrDecoder.x), float(attrDecoder.y))/65535.0);" _CRLF);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16 && attrib->nfa == 0 && attrib->isSigned != 0 )
|
||||
{
|
||||
_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
|
||||
src->add("attrDecoder.x = as_type<uint>(max(float(int(attrDecoder.x))/32767.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.y = as_type<uint>(max(float(int(attrDecoder.y))/32767.0,-1.0));" _CRLF);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16 && attrib->nfa == 1 && attrib->isSigned == 0 )
|
||||
{
|
||||
_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16 && attrib->nfa == 1 && attrib->isSigned != 0 )
|
||||
{
|
||||
_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16 && attrib->nfa == 2 && attrib->isSigned == 0 )
|
||||
{
|
||||
_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
|
||||
src->add("attrDecoder.xy = as_type<uint2>(float2(float(attrDecoder.x), float(attrDecoder.y)));" _CRLF);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else if( attrib->format == FMT_16_16 && attrib->nfa == 2 && attrib->isSigned != 0 )
|
||||
{
|
||||
_readBigEndianAttributeU16x2(shaderContext, src, attributeInputIndex);
|
||||
src->add("if( (attrDecoder.x&0x8000) != 0 ) attrDecoder.x |= 0xFFFF0000;" _CRLF);
|
||||
src->add("if( (attrDecoder.y&0x8000) != 0 ) attrDecoder.y |= 0xFFFF0000;" _CRLF);
|
||||
src->add("attrDecoder.xy = as_type<uint2>(uint2(float(int(attrDecoder.x)), float(int(attrDecoder.y))));" _CRLF);
|
||||
src->add("attrDecoder.zw = uint2(0);" _CRLF);
|
||||
}
|
||||
else if (attrib->format == FMT_16 && attrib->nfa == 1 && attrib->isSigned == 0)
|
||||
{
|
||||
_readBigEndianAttributeU16x1(shaderContext, src, attributeInputIndex);
|
||||
}
|
||||
else if (attrib->format == FMT_16 && attrib->nfa == 0 && attrib->isSigned == 0)
|
||||
{
|
||||
// seen in CoD ghosts
|
||||
_readBigEndianAttributeU16x1(shaderContext, src, attributeInputIndex);
|
||||
src->add("attrDecoder.x = as_type<uint>(float(int(attrDecoder.x))/65535.0);" _CRLF);
|
||||
}
|
||||
else
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "_emitAttributeDecode(): Unsupported fmt {:02x} nfa {} signed {} endian {}", attrib->format, attrib->nfa, attrib->isSigned, attrib->endianSwap);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,426 @@
|
||||
#pragma once
|
||||
|
||||
namespace LatteDecompiler
|
||||
{
|
||||
static void _emitUniformVariables(LatteDecompilerShaderContext* decompilerContext, LatteDecompilerOutputUniformOffsets& uniformOffsets)
|
||||
{
|
||||
LatteDecompilerShaderResourceMapping& resourceMapping = decompilerContext->output->resourceMappingVK;
|
||||
|
||||
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());
|
||||
else if (shaderType == LatteConst::ShaderType::Pixel)
|
||||
shaderSrc->addFmt("uniform 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());
|
||||
else
|
||||
debugBreakpoint();
|
||||
uniformOffsets.offset_remapped = uniformCurrentOffset;
|
||||
uniformCurrentOffset += 16 * shader->list_remappedUniformEntries.size();
|
||||
}
|
||||
else if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CFILE)
|
||||
{
|
||||
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);
|
||||
else if (shaderType == LatteConst::ShaderType::Pixel)
|
||||
shaderSrc->addFmt("uniform ivec4 uf_uniformRegisterPS[{}];" _CRLF, cfileSize);
|
||||
else if (shaderType == LatteConst::ShaderType::Geometry)
|
||||
shaderSrc->addFmt("uniform ivec4 uf_uniformRegisterGS[{}];" _CRLF, cfileSize);
|
||||
uniformOffsets.offset_uniformRegister = uniformCurrentOffset;
|
||||
uniformOffsets.count_uniformRegister = cfileSize;
|
||||
uniformCurrentOffset += 16 * cfileSize;
|
||||
}
|
||||
// special uniforms
|
||||
bool hasAnyViewportScaleDisabled =
|
||||
!decompilerContext->contextRegistersNew->PA_CL_VTE_CNTL.get_VPORT_X_SCALE_ENA() ||
|
||||
!decompilerContext->contextRegistersNew->PA_CL_VTE_CNTL.get_VPORT_Y_SCALE_ENA() ||
|
||||
!decompilerContext->contextRegistersNew->PA_CL_VTE_CNTL.get_VPORT_Z_SCALE_ENA();
|
||||
|
||||
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex && hasAnyViewportScaleDisabled)
|
||||
{
|
||||
// aka GX2 special state 0
|
||||
uniformCurrentOffset = (uniformCurrentOffset + 7)&~7;
|
||||
shaderSrc->add("uniform vec2 uf_windowSpaceToClipSpaceTransform;" _CRLF);
|
||||
uniformOffsets.offset_windowSpaceToClipSpaceTransform = uniformCurrentOffset;
|
||||
uniformCurrentOffset += 8;
|
||||
}
|
||||
bool alphaTestEnable = decompilerContext->contextRegistersNew->SX_ALPHA_TEST_CONTROL.get_ALPHA_TEST_ENABLE();
|
||||
if (decompilerContext->shaderType == LatteConst::ShaderType::Pixel && alphaTestEnable)
|
||||
{
|
||||
uniformCurrentOffset = (uniformCurrentOffset + 3)&~3;
|
||||
shaderSrc->add("uniform float uf_alphaTestRef;" _CRLF);
|
||||
uniformOffsets.offset_alphaTestRef = uniformCurrentOffset;
|
||||
uniformCurrentOffset += 4;
|
||||
}
|
||||
if (decompilerContext->analyzer.outputPointSize && decompilerContext->analyzer.writesPointSize == false)
|
||||
{
|
||||
if ((decompilerContext->shaderType == LatteConst::ShaderType::Vertex && !decompilerContext->options->usesGeometryShader) ||
|
||||
decompilerContext->shaderType == LatteConst::ShaderType::Geometry)
|
||||
{
|
||||
uniformCurrentOffset = (uniformCurrentOffset + 3)&~3;
|
||||
shaderSrc->add("uniform float uf_pointSize;" _CRLF);
|
||||
uniformOffsets.offset_pointSize = uniformCurrentOffset;
|
||||
uniformCurrentOffset += 4;
|
||||
}
|
||||
}
|
||||
// define uf_fragCoordScale which holds the xy scale for render target resolution vs effective resolution
|
||||
if (shader->shaderType == LatteConst::ShaderType::Pixel)
|
||||
{
|
||||
uniformCurrentOffset = (uniformCurrentOffset + 7)&~7;
|
||||
shaderSrc->add("uniform vec2 uf_fragCoordScale;" _CRLF);
|
||||
uniformOffsets.offset_fragCoordScale = uniformCurrentOffset;
|
||||
uniformCurrentOffset += 8;
|
||||
}
|
||||
// provide scale factor for every texture that is accessed via texel coordinates (texelFetch)
|
||||
for (sint32 t = 0; t < LATTE_NUM_MAX_TEX_UNITS; t++)
|
||||
{
|
||||
if (decompilerContext->analyzer.texUnitUsesTexelCoordinates.test(t) == false)
|
||||
continue;
|
||||
uniformCurrentOffset = (uniformCurrentOffset + 7) & ~7;
|
||||
shaderSrc->addFmt("uniform vec2 uf_tex{}Scale;" _CRLF, t);
|
||||
uniformOffsets.offset_texScale[t] = uniformCurrentOffset;
|
||||
uniformCurrentOffset += 8;
|
||||
}
|
||||
// define uf_verticesPerInstance + uf_streamoutBufferBaseX
|
||||
if (decompilerContext->analyzer.useSSBOForStreamout &&
|
||||
(shader->shaderType == LatteConst::ShaderType::Vertex && decompilerContext->options->usesGeometryShader == false) ||
|
||||
(shader->shaderType == LatteConst::ShaderType::Geometry) )
|
||||
{
|
||||
shaderSrc->add("uniform 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);
|
||||
uniformOffsets.offset_streamoutBufferBase[i] = uniformCurrentOffset;
|
||||
uniformCurrentOffset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uniformOffsets.offset_endOfBlock = uniformCurrentOffset;
|
||||
}
|
||||
|
||||
static void _emitUniformBuffers(LatteDecompilerShaderContext* decompilerContext)
|
||||
{
|
||||
auto shaderSrc = 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);
|
||||
|
||||
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->add("};" _CRLF _CRLF);
|
||||
shaderSrc->add(_CRLF);
|
||||
}
|
||||
}
|
||||
else if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_REMAPPED)
|
||||
{
|
||||
// already generated in _emitUniformVariables
|
||||
}
|
||||
else if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CFILE)
|
||||
{
|
||||
// already generated in _emitUniformVariables
|
||||
}
|
||||
else if (decompilerContext->shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_NONE)
|
||||
{
|
||||
// no uniforms used
|
||||
}
|
||||
else
|
||||
{
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void _emitTextureDefinitions(LatteDecompilerShaderContext* shaderContext)
|
||||
{
|
||||
auto src = shaderContext->shaderSource;
|
||||
// texture sampler definition
|
||||
for (sint32 i = 0; i < LATTE_NUM_MAX_TEX_UNITS; i++)
|
||||
{
|
||||
if (!shaderContext->output->textureUnitMask[i])
|
||||
continue;
|
||||
|
||||
if (shaderContext->shader->textureIsIntegerFormat[i])
|
||||
{
|
||||
// integer samplers
|
||||
if (shaderContext->shader->textureUnitDim[i] == Latte::E_DIM::DIM_1D)
|
||||
src->add("texture1d<uint>");
|
||||
else if (shaderContext->shader->textureUnitDim[i] == Latte::E_DIM::DIM_2D || shaderContext->shader->textureUnitDim[i] == Latte::E_DIM::DIM_2D_MSAA)
|
||||
src->add("texture2d<uint>");
|
||||
else
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
else if (shaderContext->shader->textureUnitDim[i] == Latte::E_DIM::DIM_2D || shaderContext->shader->textureUnitDim[i] == Latte::E_DIM::DIM_2D_MSAA)
|
||||
src->add("texture2d<float>");
|
||||
else if (shaderContext->shader->textureUnitDim[i] == Latte::E_DIM::DIM_1D)
|
||||
src->add("texture1d<float>");
|
||||
else if (shaderContext->shader->textureUnitDim[i] == Latte::E_DIM::DIM_2D_ARRAY)
|
||||
src->add("texture2d_array<float>");
|
||||
else if (shaderContext->shader->textureUnitDim[i] == Latte::E_DIM::DIM_CUBEMAP)
|
||||
src->add("texturecube_array<float>");
|
||||
else if (shaderContext->shader->textureUnitDim[i] == Latte::E_DIM::DIM_3D)
|
||||
src->add("texture3d<float>");
|
||||
else
|
||||
{
|
||||
cemu_assert_unimplemented();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
auto shaderSrc = decompilerContext->shaderSource;
|
||||
if (decompilerContext->shader->shaderType == LatteConst::ShaderType::Vertex)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// uniform buffers
|
||||
_emitUniformBuffers(decompilerContext);
|
||||
// textures
|
||||
_emitTextureDefinitions(decompilerContext);
|
||||
// attributes
|
||||
_emitAttributes(decompilerContext);
|
||||
// misc stuff
|
||||
_emitMisc(decompilerContext);
|
||||
|
||||
if (dump_shaders_enabled)
|
||||
decompilerContext->shaderSource->add("// end of shader inputs/outputs" _CRLF);
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ struct LatteDecompilerTEXInstruction
|
||||
sint32 dstGpr;
|
||||
sint8 dstSel[4];
|
||||
// texture fetch
|
||||
struct
|
||||
struct
|
||||
{
|
||||
sint32 textureIndex{};
|
||||
sint32 samplerIndex{};
|
||||
@ -216,7 +216,7 @@ struct LatteDecompilerShaderContext
|
||||
bool genIntReg; // if set, generate R*i register variables
|
||||
bool useArrayGPRs; // if set, an array is used to represent GPRs instead of individual variables
|
||||
}typeTracker;
|
||||
// analyzer
|
||||
// analyzer
|
||||
struct
|
||||
{
|
||||
// general
|
||||
@ -268,9 +268,10 @@ struct LatteDecompilerShaderContext
|
||||
void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader);
|
||||
void LatteDecompiler_analyzeDataTypes(LatteDecompilerShaderContext* shaderContext);
|
||||
void LatteDecompiler_emitGLSLShader(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader);
|
||||
void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader);
|
||||
|
||||
void LatteDecompiler_cleanup(LatteDecompilerShaderContext* shaderContext);
|
||||
|
||||
// helper functions
|
||||
|
||||
sint32 LatteDecompiler_getColorOutputIndexFromExportIndex(LatteDecompilerShaderContext* shaderContext, sint32 exportIndex);
|
||||
sint32 LatteDecompiler_getColorOutputIndexFromExportIndex(LatteDecompilerShaderContext* shaderContext, sint32 exportIndex);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
|
||||
|
||||
#include "HW/Latte/Core/LatteShader.h"
|
||||
#include "gui/guiWrapper.h"
|
||||
|
||||
MetalRenderer::MetalRenderer()
|
||||
@ -259,6 +260,8 @@ void MetalRenderer::streamout_rendererFinishDrawcall()
|
||||
void MetalRenderer::draw_beginSequence()
|
||||
{
|
||||
cemuLog_logDebug(LogType::Force, "not implemented");
|
||||
|
||||
LatteSHRC_UpdateActiveShaders();
|
||||
}
|
||||
|
||||
void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 instanceCount, uint32 count, MPTR indexDataMPTR, Latte::LATTE_VGT_DMA_INDEX_TYPE::E_INDEX_TYPE indexType, bool isFirst)
|
||||
|
Loading…
Reference in New Issue
Block a user