mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-09 17:14:47 -06:00
Latte+Vulkan: Several smaller optimizations
This commit is contained in:
parent
7884f095dd
commit
545754635a
@ -1,9 +1,6 @@
|
||||
#include "Cafe/HW/Latte/Core/LatteConst.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteShaderAssembly.h"
|
||||
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
||||
#include "Cafe/OS/libs/gx2/GX2.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/LegacyShaderDecompiler/LatteDecompilerInstructions.h"
|
||||
#include "Cafe/HW/Latte/Core/FetchShader.h"
|
||||
@ -503,7 +500,7 @@ LatteFetchShader::CacheHash LatteFetchShader::CalculateCacheHash(void* programCo
|
||||
|
||||
LatteFetchShader* LatteFetchShader::FindInCacheByHash(LatteFetchShader::CacheHash fsHash)
|
||||
{
|
||||
// does not hold s_fetchShaderCache for better performance. Be careful not to call this while another thread invokes RegisterInCache()
|
||||
// does not hold s_spinlockFetchShaderCache for better performance. Be careful not to call this while another thread invokes RegisterInCache()
|
||||
auto itr = s_fetchShaderByHash.find(fsHash);
|
||||
if (itr == s_fetchShaderByHash.end())
|
||||
return nullptr;
|
||||
@ -539,6 +536,11 @@ LatteFetchShader* LatteFetchShader::FindByGPUState()
|
||||
}
|
||||
// update lookup info
|
||||
CacheHash fsHash = CalculateCacheHash(_getFSProgramPtr(), _getFSProgramSize());
|
||||
if (lookupInfo->fetchShader->m_cacheHash == fsHash && lookupInfo->programSize == fsSize) // check if its still the same hash
|
||||
{
|
||||
lookupInfo->lastFrameAccessed = LatteGPUState.frameCounter;
|
||||
return lookupInfo->fetchShader;
|
||||
}
|
||||
LatteFetchShader* fetchShader = FindInCacheByHash(fsHash);
|
||||
if (!fetchShader)
|
||||
{
|
||||
|
||||
@ -171,15 +171,16 @@ bool LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance
|
||||
LatteFetchShader* parsedFetchShader = LatteSHRC_GetActiveFetchShader();
|
||||
if (!parsedFetchShader)
|
||||
return false;
|
||||
uint32* __restrict bufferRegStartPtr = LatteGPUState.contextRegister + mmSQ_VTX_ATTRIBUTE_BLOCK_START;
|
||||
for (auto& bufferGroup : parsedFetchShader->bufferGroups)
|
||||
{
|
||||
uint32 bufferIndex = bufferGroup.attributeBufferIndex;
|
||||
uint32 bufferBaseRegisterIndex = mmSQ_VTX_ATTRIBUTE_BLOCK_START + bufferIndex * 7;
|
||||
MPTR bufferAddress = LatteGPUState.contextRegister[bufferBaseRegisterIndex + 0];
|
||||
uint32 bufferSize = LatteGPUState.contextRegister[bufferBaseRegisterIndex + 1] + 1;
|
||||
uint32 bufferStride = (LatteGPUState.contextRegister[bufferBaseRegisterIndex + 2] >> 11) & 0xFFFF;
|
||||
uint32* __restrict bufferRegs = bufferRegStartPtr + bufferIndex * 7;
|
||||
MPTR bufferAddress = bufferRegs[0];
|
||||
/* uint32 bufferSize = bufferRegs[1] + 1; */
|
||||
uint32 bufferStride = (bufferRegs[2] >> 11) & 0xFFFF;
|
||||
|
||||
if (bufferAddress == MPTR_NULL)
|
||||
if (bufferAddress == MPTR_NULL) [[unlikely]]
|
||||
{
|
||||
g_renderer->buffer_bindVertexBuffer(bufferIndex, 0, 0);
|
||||
continue;
|
||||
|
||||
@ -124,7 +124,7 @@ private:
|
||||
bool m_isFirstDraw{false};
|
||||
bool m_vertexBufferChanged{ false };
|
||||
bool m_uniformBufferChanged{ false };
|
||||
boost::container::small_vector<CmdQueuePos, 4> m_queuePosStack;
|
||||
boost::container::static_vector<CmdQueuePos, 4> m_queuePosStack;
|
||||
};
|
||||
|
||||
void LatteCP_processCommandBuffer(DrawPassContext& drawPassCtx);
|
||||
@ -299,11 +299,11 @@ LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords)
|
||||
#ifdef CEMU_DEBUG_ASSERT
|
||||
cemu_assert_debug((registerIndex + nWords) <= LATTE_MAX_REGISTER);
|
||||
#endif
|
||||
uint32* outputReg = (uint32*)(LatteGPUState.contextRegister + registerIndex);
|
||||
uint32* __restrict outputReg = (uint32*)(LatteGPUState.contextRegister + registerIndex);
|
||||
if (LatteGPUState.contextControl0 == 0x80000077)
|
||||
{
|
||||
// state shadowing enabled
|
||||
uint32* shadowAddrs = LatteGPUState.contextRegisterShadowAddr + registerIndex;
|
||||
uint32* __restrict shadowAddrs = LatteGPUState.contextRegisterShadowAddr + registerIndex;
|
||||
sint32 indexCounter = 0;
|
||||
while (--nWords)
|
||||
{
|
||||
@ -318,7 +318,6 @@ LatteCMDPtr LatteCP_itSetRegistersGeneric(LatteCMDPtr cmd, uint32 nWords)
|
||||
else
|
||||
{
|
||||
// state shadowing disabled
|
||||
sint32 indexCounter = 0;
|
||||
while (--nWords)
|
||||
{
|
||||
*outputReg = LatteReadCMD();
|
||||
|
||||
@ -325,22 +325,17 @@ LatteTextureView* LatteMRT::GetColorAttachmentTexture(uint32 index, bool createN
|
||||
// get mask of all used color buffers
|
||||
uint8 LatteMRT::GetActiveColorBufferMask(const LatteDecompilerShader* pixelShader, const LatteContextRegister& lcr)
|
||||
{
|
||||
if (!pixelShader) [[unlikely]]
|
||||
return 0;
|
||||
const uint32* regView = lcr.GetRawView();
|
||||
|
||||
uint8 colorBufferMask = 0;
|
||||
for (uint32 i = 0; i < 8; i++)
|
||||
{
|
||||
if (regView[mmCB_COLOR0_BASE + i] != MPTR_NULL)
|
||||
colorBufferMask |= (1 << i);
|
||||
}
|
||||
// check if color buffer output is active
|
||||
const Latte::LATTE_CB_COLOR_CONTROL& colorControlReg = lcr.CB_COLOR_CONTROL;
|
||||
uint32 colorBufferDisable = colorControlReg.get_SPECIAL_OP() == Latte::LATTE_CB_COLOR_CONTROL::E_SPECIALOP::DISABLE;
|
||||
if (colorBufferDisable)
|
||||
return 0;
|
||||
cemu_assert_debug(colorControlReg.get_DEGAMMA_ENABLE() == false); // not supported
|
||||
// combine color buffer mask with pixel output mask from pixel shader
|
||||
colorBufferMask &= (pixelShader ? pixelShader->pixelColorOutputMask : 0);
|
||||
// start with color buffer mask from pixel shader output
|
||||
uint8 colorBufferMask = pixelShader->pixelColorOutputMask;
|
||||
// combine color buffer mask with color channel mask from mmCB_TARGET_MASK (disable render buffer if all colors are blocked)
|
||||
uint32 channelTargetMask = lcr.CB_TARGET_MASK.get_MASK();
|
||||
for (uint32 i = 0; i < 8; i++)
|
||||
@ -348,9 +343,9 @@ uint8 LatteMRT::GetActiveColorBufferMask(const LatteDecompilerShader* pixelShade
|
||||
if (((channelTargetMask >> (i * 4)) & 0xF) == 0)
|
||||
colorBufferMask &= ~(1 << i);
|
||||
}
|
||||
|
||||
// render targets smaller than the scissor size are not allowed
|
||||
// this fixes a few render issues in Cemu but we dont know if this matches HW behavior
|
||||
// also check for color buffers without a valid pointer
|
||||
cemu_assert_debug(lcr.PA_SC_GENERIC_SCISSOR_TL.get_WINDOW_OFFSET_DISABLE() == true); // todo (not exposed by GX2 API)
|
||||
uint32 scissorAccessWidth = lcr.PA_SC_GENERIC_SCISSOR_BR.get_BR_X();
|
||||
uint32 scissorAccessHeight = lcr.PA_SC_GENERIC_SCISSOR_BR.get_BR_Y();
|
||||
@ -358,6 +353,8 @@ uint8 LatteMRT::GetActiveColorBufferMask(const LatteDecompilerShader* pixelShade
|
||||
{
|
||||
if( (colorBufferMask&(1<<i)) == 0 )
|
||||
continue;
|
||||
if (regView[mmCB_COLOR0_BASE + i] == MPTR_NULL) [[unlikely]]
|
||||
colorBufferMask &= ~(1 << i);
|
||||
// get width/height
|
||||
uint32 regColorSize = regView[mmCB_COLOR0_SIZE + i];
|
||||
uint32 regColorInfo = regView[mmCB_COLOR0_INFO + i];
|
||||
@ -369,15 +366,12 @@ uint8 LatteMRT::GetActiveColorBufferMask(const LatteDecompilerShader* pixelShade
|
||||
uint32 colorBufferHeight = pitchHeight / colorBufferPitch;
|
||||
uint32 colorBufferWidth = colorBufferPitch;
|
||||
|
||||
if ((colorBufferWidth < (sint32)scissorAccessWidth) ||
|
||||
(colorBufferHeight < (sint32)scissorAccessHeight))
|
||||
if ((colorBufferWidth < (sint32)scissorAccessWidth) || (colorBufferHeight < (sint32)scissorAccessHeight))
|
||||
{
|
||||
// log this?
|
||||
colorBufferMask &= ~(1<<i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return colorBufferMask;
|
||||
}
|
||||
|
||||
|
||||
@ -217,7 +217,7 @@ void LatteShader_CreatePSInputTable(LatteShaderPSInputTable* psInputTable, uint3
|
||||
uint32 spi0_positionEnable = (psControl0 >> 8) & 1;
|
||||
uint32 spi0_positionCentroid = (psControl0 >> 9) & 1;
|
||||
cemu_assert_debug(spi0_positionCentroid == 0); // controls gl_FragCoord
|
||||
uint32 spi0_positionAddr = (psControl0 >> 10) & 0x1F; // controls gl_FragCoord
|
||||
uint32 spi0_positionAddr = spi0_positionEnable ? ((psControl0 >> 10) & 0x1F) : 0xFFFFFFFF; // controls gl_FragCoord
|
||||
uint32 spi0_paramGen = (psControl0 >> 15) & 0xF; // used for gl_PointCoords
|
||||
uint32 spi0_paramGenAddr = (psControl0 >> 19) & 0x7F;
|
||||
sint32 importIndex = 0;
|
||||
@ -278,7 +278,7 @@ void LatteShader_CreatePSInputTable(LatteShaderPSInputTable* psInputTable, uint3
|
||||
|
||||
key += (uint64)psInputControl;
|
||||
key = std::rotl<uint64>(key, 7);
|
||||
if (spi0_positionEnable && f == spi0_positionAddr)
|
||||
if (f == spi0_positionAddr)
|
||||
{
|
||||
psInputTable->import[f].semanticId = LATTE_ANALYZER_IMPORT_INDEX_SPIPOSITION;
|
||||
psInputTable->import[f].isFlat = false;
|
||||
@ -1053,7 +1053,7 @@ void LatteSHRC_UpdateActiveShaders()
|
||||
vsProgramCode = (uint8*)memory_getPointerFromPhysicalOffset((LatteGPUState.contextRegister[mmSQ_PGM_START_ES] & 0xFFFFFF) << 8);
|
||||
vsProgramSize = LatteGPUState.contextRegister[mmSQ_PGM_START_ES + 1] << 3;
|
||||
copyProgramCode = (uint8*)memory_getPointerFromPhysicalOffset((LatteGPUState.contextRegister[mmSQ_PGM_START_VS] & 0xFFFFFF) << 8);
|
||||
if (LatteGPUState.contextRegister[mmSQ_PGM_START_VS] == 0)
|
||||
if (LatteGPUState.contextRegister[mmSQ_PGM_START_VS] == 0) [[unlikely]]
|
||||
{
|
||||
copyProgramCode = NULL;
|
||||
debug_printf("copyProgram is NULL but used. Might be because of unsupported vertex/geometry mode?");
|
||||
@ -1062,7 +1062,7 @@ void LatteSHRC_UpdateActiveShaders()
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LatteGPUState.contextRegister[mmSQ_PGM_START_VS] == 0)
|
||||
if (LatteGPUState.contextRegister[mmSQ_PGM_START_VS] == 0) [[unlikely]]
|
||||
{
|
||||
debug_printf("No vertex shader program set\n");
|
||||
LatteGPUState.activeShaderHasError = true;
|
||||
@ -1089,7 +1089,6 @@ void LatteSHRC_UpdateActiveShaders()
|
||||
// returns the sampler base index for the given shader type
|
||||
sint32 LatteDecompiler_getTextureSamplerBaseIndex(LatteConst::ShaderType shaderType)
|
||||
{
|
||||
uint32 samplerId = LATTE_DECOMPILER_SAMPLER_NONE;
|
||||
if (shaderType == LatteConst::ShaderType::Vertex)
|
||||
return Latte::SAMPLER_BASE_INDEX_VERTEX;
|
||||
else if (shaderType == LatteConst::ShaderType::Pixel)
|
||||
|
||||
@ -1365,10 +1365,8 @@ void LatteTexture_MarkConnectedTexturesForReloadFromDynamicTextures(LatteTexture
|
||||
{
|
||||
for (auto& it : texture->list_compatibleRelations)
|
||||
{
|
||||
if (texture == it->baseTexture)
|
||||
it->subTexture->reloadFromDynamicTextures = true;
|
||||
else
|
||||
it->baseTexture->reloadFromDynamicTextures = true;
|
||||
LatteTexture* connectedTexture = (texture == it->baseTexture) ? it->subTexture : it->baseTexture;
|
||||
connectedTexture->reloadFromDynamicTextures = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -334,6 +334,8 @@ void VulkanRenderer::GetDeviceFeatures()
|
||||
m_featureControl.limits.minUniformBufferOffsetAlignment = std::max(prop2.properties.limits.minUniformBufferOffsetAlignment, (VkDeviceSize)4);
|
||||
m_featureControl.limits.nonCoherentAtomSize = std::max(prop2.properties.limits.nonCoherentAtomSize, (VkDeviceSize)4);
|
||||
cemuLog_log(LogType::Force, fmt::format("VulkanLimits: UBAlignment {0} nonCoherentAtomSize {1}", prop2.properties.limits.minUniformBufferOffsetAlignment, prop2.properties.limits.nonCoherentAtomSize));
|
||||
// calculate used limits
|
||||
m_featureControl.limits.calcUniformBufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1;
|
||||
}
|
||||
|
||||
#if BOOST_OS_LINUX
|
||||
|
||||
@ -470,6 +470,8 @@ private:
|
||||
{
|
||||
uint32 minUniformBufferOffsetAlignment = 256;
|
||||
uint32 nonCoherentAtomSize = 256;
|
||||
// calculated
|
||||
uint32 calcUniformBufferAlignmentM1{};
|
||||
}limits;
|
||||
|
||||
bool usingDebugMarkerTool{ false }; // validation layer or other tool capable of handling debug markers is used
|
||||
|
||||
@ -315,7 +315,7 @@ float s_vkUniformData[512 * 4];
|
||||
|
||||
uint32 VulkanRenderer::uniformData_uploadUniformDataBufferGetOffset(std::span<uint8> data)
|
||||
{
|
||||
const uint32 bufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1;
|
||||
const uint32 bufferAlignmentM1 = m_featureControl.limits.calcUniformBufferAlignmentM1;
|
||||
const uint32 uniformSize = ((uint32)data.size() + bufferAlignmentM1) & ~bufferAlignmentM1;
|
||||
|
||||
auto waitWhileCondition = [&](std::function<bool()> condition) {
|
||||
@ -481,40 +481,44 @@ uint64 VulkanRenderer::GetDescriptorSetStateHash(LatteDecompilerShader* shader)
|
||||
uint64 hash = 0;
|
||||
|
||||
const sint32 textureCount = shader->resourceMapping.getTextureCount();
|
||||
|
||||
LatteTextureViewVk** texViewBase = m_state.boundTexture;
|
||||
uint32* __restrict texRegBase = LatteGPUState.contextRegister;
|
||||
uint32 samplerBaseIndex = 0;
|
||||
switch (shader->shaderType)
|
||||
{
|
||||
case LatteConst::ShaderType::Vertex:
|
||||
texViewBase += LATTE_CEMU_VS_TEX_UNIT_BASE;
|
||||
texRegBase += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS;
|
||||
samplerBaseIndex = Latte::SAMPLER_BASE_INDEX_VERTEX;
|
||||
break;
|
||||
case LatteConst::ShaderType::Pixel:
|
||||
texViewBase += LATTE_CEMU_PS_TEX_UNIT_BASE;
|
||||
texRegBase += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS;
|
||||
samplerBaseIndex = Latte::SAMPLER_BASE_INDEX_PIXEL;
|
||||
break;
|
||||
case LatteConst::ShaderType::Geometry:
|
||||
texViewBase += LATTE_CEMU_GS_TEX_UNIT_BASE;
|
||||
texRegBase += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS;
|
||||
samplerBaseIndex = Latte::SAMPLER_BASE_INDEX_GEOMETRY;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < textureCount; ++i)
|
||||
{
|
||||
const auto relative_textureUnit = shader->resourceMapping.getRelativeTextureUnitFromRelativeBindingPoint(i);
|
||||
auto hostTextureUnit = relative_textureUnit;
|
||||
auto textureDim = shader->textureUnitDim[relative_textureUnit];
|
||||
auto texUnitRegIndex = hostTextureUnit * 7;
|
||||
switch (shader->shaderType)
|
||||
{
|
||||
case LatteConst::ShaderType::Vertex:
|
||||
hostTextureUnit += LATTE_CEMU_VS_TEX_UNIT_BASE;
|
||||
texUnitRegIndex += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS;
|
||||
break;
|
||||
case LatteConst::ShaderType::Pixel:
|
||||
hostTextureUnit += LATTE_CEMU_PS_TEX_UNIT_BASE;
|
||||
texUnitRegIndex += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS;
|
||||
break;
|
||||
case LatteConst::ShaderType::Geometry:
|
||||
hostTextureUnit += LATTE_CEMU_GS_TEX_UNIT_BASE;
|
||||
texUnitRegIndex += Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
auto texture = m_state.boundTexture[hostTextureUnit];
|
||||
uint32* __restrict texRegs = texRegBase + relative_textureUnit * 7;
|
||||
auto texture = texViewBase[relative_textureUnit];
|
||||
if (!texture)
|
||||
continue;
|
||||
|
||||
const uint32 word4 = LatteGPUState.contextRegister[texUnitRegIndex + 4];
|
||||
|
||||
const uint32 word4 = texRegs[4];
|
||||
uint32 samplerIndex = shader->textureUnitSamplerAssignment[relative_textureUnit];
|
||||
if (samplerIndex != LATTE_DECOMPILER_SAMPLER_NONE)
|
||||
if (samplerIndex != LATTE_DECOMPILER_SAMPLER_NONE) [[likely]]
|
||||
{
|
||||
samplerIndex += LatteDecompiler_getTextureSamplerBaseIndex(shader->shaderType);
|
||||
samplerIndex += samplerBaseIndex;
|
||||
hash += LatteGPUState.contextRegister[Latte::REGADDR::SQ_TEX_SAMPLER_WORD0_0 + samplerIndex * 3 + 0];
|
||||
hash = std::rotl<uint64>(hash, 7);
|
||||
hash += LatteGPUState.contextRegister[Latte::REGADDR::SQ_TEX_SAMPLER_WORD0_0 + samplerIndex * 3 + 1];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user