Merge pull request #21 from SamoZ256/metal-mesh-shaders-check

Check for mesh shaders support
This commit is contained in:
SamoZ256 2025-03-04 08:38:59 +01:00 committed by GitHub
commit 1367e11d26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 71 additions and 37 deletions

View file

@ -258,6 +258,7 @@ void InfoLog_PrintActiveSettings()
else if (ActiveSettings::GetGraphicsAPI() == GraphicAPI::kMetal)
{
cemuLog_log(LogType::Force, "Async compile: {}", GetConfig().async_compile.GetValue() ? "true" : "false");
cemuLog_log(LogType::Force, "Force mesh shaders: {}", GetConfig().force_mesh_shaders.GetValue() ? "true" : "false");
cemuLog_log(LogType::Force, "Fast math: {}", g_current_game_profile->GetFastMath() ? "true" : "false");
cemuLog_log(LogType::Force, "Buffer cache type: {}", g_current_game_profile->GetBufferCacheMode());
cemuLog_log(LogType::Force, "Position invariance: {}", g_current_game_profile->GetPositionInvariance());

View file

@ -510,7 +510,7 @@ void LatteSHRC_UpdateVSBaseHash(uint8* vertexShaderPtr, uint32 vertexShaderSize,
vsHash += tmp;
auto primitiveType = LatteGPUState.contextNew.VGT_PRIMITIVE_TYPE.get_PRIMITIVE_MODE();
// TODO: include always in the hash in case of geometry shader or rect shader
// TODO: include always in the hash in case of geometry shader or rect shader on Metal
if (primitiveType == Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE::RECTS)
{
vsHash += 13ULL;
@ -528,7 +528,9 @@ void LatteSHRC_UpdateVSBaseHash(uint8* vertexShaderPtr, uint32 vertexShaderSize,
#if ENABLE_METAL
if (g_renderer->GetType() == RendererAPI::Metal)
{
if (usesGeometryShader || _activeFetchShader->mtlFetchVertexManually)
bool isRectVertexShader = (primitiveType == Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE::RECTS);
if ((usesGeometryShader || isRectVertexShader) || _activeFetchShader->mtlFetchVertexManually)
{
for (sint32 g = 0; g < _activeFetchShader->bufferGroups.size(); g++)
{
@ -542,7 +544,7 @@ void LatteSHRC_UpdateVSBaseHash(uint8* vertexShaderPtr, uint32 vertexShaderSize,
}
}
if (!usesGeometryShader)
if (!(usesGeometryShader || isRectVertexShader))
{
if (LatteGPUState.contextNew.IsRasterizationEnabled())
vsHash += 51ULL;

View file

@ -10,6 +10,9 @@
#include "Cafe/HW/Latte/Renderer/Renderer.h"
#include "Common/MemPtr.h"
#include "HW/Latte/ISA/LatteReg.h"
#if ENABLE_METAL
#include "HW/Latte/Renderer/Metal/MetalCommon.h"
#endif
// Defined in LatteTextureLegacy.cpp
Latte::E_GX2SURFFMT LatteTexture_ReconstructGX2Format(const Latte::LATTE_SQ_TEX_RESOURCE_WORD1_N& texUnitWord1, const Latte::LATTE_SQ_TEX_RESOURCE_WORD4_N& texUnitWord4);
@ -557,9 +560,9 @@ namespace LatteDecompiler
}
if (g_renderer->GetType() == RendererAPI::Metal)
{
bool isRectVertexShader = (static_cast<LattePrimitiveMode>(decompilerContext->contextRegisters[mmVGT_PRIMITIVE_TYPE]) == LattePrimitiveMode::RECTS);
bool usesGeometryShader = UseGeometryShader(*decompilerContext->contextRegistersNew, decompilerContext->options->usesGeometryShader);
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex && (decompilerContext->options->usesGeometryShader || isRectVertexShader))
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex && usesGeometryShader)
decompilerContext->hasUniformVarBlock = true; // uf_verticesPerInstance
}
}

View file

@ -3933,8 +3933,8 @@ static void LatteDecompiler_emitAttributeImport(LatteDecompilerShaderContext* sh
void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader)
{
bool isRectVertexShader = (static_cast<LattePrimitiveMode>(shaderContext->contextRegisters[mmVGT_PRIMITIVE_TYPE]) == LattePrimitiveMode::RECTS);
bool usesGeometryShader = (shaderContext->options->usesGeometryShader || isRectVertexShader);
bool isRectVertexShader = UseRectEmulation(*shaderContext->contextRegistersNew);
bool usesGeometryShader = UseGeometryShader(*shaderContext->contextRegistersNew, shaderContext->options->usesGeometryShader);
bool fetchVertexManually = (usesGeometryShader || (shaderContext->fetchShader && shaderContext->fetchShader->mtlFetchVertexManually));
// Rasterization
@ -3953,7 +3953,7 @@ void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext,
src->add("#include <metal_stdlib>" _CRLF);
src->add("using namespace metal;" _CRLF);
// header part (definitions for inputs and outputs)
LatteDecompiler::emitHeader(shaderContext, isRectVertexShader, fetchVertexManually, rasterizationEnabled);
LatteDecompiler::emitHeader(shaderContext, isRectVertexShader, usesGeometryShader, fetchVertexManually, rasterizationEnabled);
// helper functions
LatteDecompiler_emitHelperFunctions(shaderContext, src);
const char* functionType = "";
@ -4131,7 +4131,7 @@ void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext,
}
// start of main
src->addFmt("{} {} main0(", functionType, outputTypeName);
LatteDecompiler::emitInputs(shaderContext, isRectVertexShader, fetchVertexManually);
LatteDecompiler::emitInputs(shaderContext, isRectVertexShader, usesGeometryShader, fetchVertexManually);
src->add(") {" _CRLF);
if (fetchVertexManually && (shader->shaderType == LatteConst::ShaderType::Vertex || shader->shaderType == LatteConst::ShaderType::Geometry))
{

View file

@ -6,7 +6,7 @@
namespace LatteDecompiler
{
static void _emitUniformVariables(LatteDecompilerShaderContext* decompilerContext, bool isRectVertexShader)
static void _emitUniformVariables(LatteDecompilerShaderContext* decompilerContext, bool usesGeometryShader)
{
auto src = decompilerContext->shaderSource;
@ -87,7 +87,7 @@ namespace LatteDecompiler
}
// define verticesPerInstance + streamoutBufferBaseX
if ((shader->shaderType == LatteConst::ShaderType::Vertex &&
(decompilerContext->options->usesGeometryShader || isRectVertexShader)) ||
usesGeometryShader) ||
(decompilerContext->analyzer.useSSBOForStreamout &&
(shader->shaderType == LatteConst::ShaderType::Vertex && !decompilerContext->options->usesGeometryShader) ||
(shader->shaderType == LatteConst::ShaderType::Geometry)))
@ -270,7 +270,7 @@ namespace LatteDecompiler
src->add("};" _CRLF _CRLF);
}
static void _emitInputsAndOutputs(LatteDecompilerShaderContext* decompilerContext, bool isRectVertexShader, bool fetchVertexManually, bool rasterizationEnabled)
static void _emitInputsAndOutputs(LatteDecompilerShaderContext* decompilerContext, bool isRectVertexShader, bool usesGeometryShader, bool fetchVertexManually, bool rasterizationEnabled)
{
auto src = decompilerContext->shaderSource;
@ -304,7 +304,7 @@ namespace LatteDecompiler
src->add("};" _CRLF _CRLF);
}
if (!decompilerContext->options->usesGeometryShader)
if (!usesGeometryShader || isRectVertexShader)
{
if (decompilerContext->shaderType == LatteConst::ShaderType::Vertex && rasterizationEnabled)
_emitVSOutputs(decompilerContext, isRectVertexShader);
@ -357,11 +357,11 @@ namespace LatteDecompiler
}
}
static void emitHeader(LatteDecompilerShaderContext* decompilerContext, bool isRectVertexShader, bool fetchVertexManually, bool rasterizationEnabled)
static void emitHeader(LatteDecompilerShaderContext* decompilerContext, bool isRectVertexShader, bool usesGeometryShader, bool fetchVertexManually, bool rasterizationEnabled)
{
auto src = decompilerContext->shaderSource;
if ((decompilerContext->options->usesGeometryShader || isRectVertexShader) && (decompilerContext->shaderType == LatteConst::ShaderType::Vertex || decompilerContext->shaderType == LatteConst::ShaderType::Geometry))
if (usesGeometryShader && (decompilerContext->shaderType == LatteConst::ShaderType::Vertex || decompilerContext->shaderType == LatteConst::ShaderType::Geometry))
{
LattePrimitiveMode vsOutPrimType = decompilerContext->contextRegistersNew->VGT_PRIMITIVE_TYPE.get_PRIMITIVE_MODE();
src->addFmt("#define VERTICES_PER_VERTEX_PRIMITIVE {}" _CRLF, GetVerticesPerPrimitive(vsOutPrimType));
@ -399,11 +399,11 @@ namespace LatteDecompiler
if(dump_shaders_enabled)
decompilerContext->shaderSource->add("// start of shader inputs/outputs, predetermined by Cemu. Do not touch" _CRLF);
// uniform variables
_emitUniformVariables(decompilerContext, isRectVertexShader);
_emitUniformVariables(decompilerContext, usesGeometryShader);
// uniform buffers
_emitUniformBuffers(decompilerContext);
// inputs and outputs
_emitInputsAndOutputs(decompilerContext, isRectVertexShader, fetchVertexManually, rasterizationEnabled);
_emitInputsAndOutputs(decompilerContext, isRectVertexShader, usesGeometryShader, fetchVertexManually, rasterizationEnabled);
if (dump_shaders_enabled)
decompilerContext->shaderSource->add("// end of shader inputs/outputs" _CRLF);
@ -491,14 +491,14 @@ namespace LatteDecompiler
}
}
static void emitInputs(LatteDecompilerShaderContext* decompilerContext, bool isRectVertexShader, bool fetchVertexManually)
static void emitInputs(LatteDecompilerShaderContext* decompilerContext, bool isRectVertexShader, bool usesGeometryShader, bool fetchVertexManually)
{
auto src = decompilerContext->shaderSource;
switch (decompilerContext->shaderType)
{
case LatteConst::ShaderType::Vertex:
if (decompilerContext->options->usesGeometryShader || isRectVertexShader)
if (usesGeometryShader)
{
src->add("object_data ObjectPayload& objectPayload [[payload]]");
src->add(", mesh_grid_properties meshGridProperties");

View file

@ -210,3 +210,12 @@ inline bool PrimitiveRequiresConnection(LattePrimitiveMode primitiveMode)
else
return false;
}
inline bool UseRectEmulation(const LatteContextRegister& lcr) {
const LattePrimitiveMode primitiveMode = lcr.VGT_PRIMITIVE_TYPE.get_PRIMITIVE_MODE();
return (primitiveMode == Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE::RECTS);
}
inline bool UseGeometryShader(const LatteContextRegister& lcr, bool hasGeometryShader) {
return hasGeometryShader || UseRectEmulation(lcr);
}

View file

@ -283,16 +283,15 @@ MetalPipelineCompiler::~MetalPipelineCompiler()
m_binaryArchiveURL->release();
*/
if (m_pipelineDescriptor)
m_pipelineDescriptor->release();
}
void MetalPipelineCompiler::InitFromState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* geometryShader, const LatteDecompilerShader* pixelShader, const MetalAttachmentsInfo& lastUsedAttachmentsInfo, const MetalAttachmentsInfo& activeAttachmentsInfo, const LatteContextRegister& lcr)
{
// Check if the pipeline uses a geometry shader
const LattePrimitiveMode primitiveMode = static_cast<LattePrimitiveMode>(lcr.VGT_PRIMITIVE_TYPE.get_PRIMITIVE_MODE());
bool isPrimitiveRect = (primitiveMode == Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE::RECTS);
m_usesGeometryShader = (geometryShader != nullptr || isPrimitiveRect);
m_usesGeometryShader = UseGeometryShader(lcr, geometryShader != nullptr);
if (m_usesGeometryShader && !m_mtlr->SupportsMeshShaders())
return;
// Rasterization
m_rasterizationEnabled = lcr.IsRasterizationEnabled();
@ -301,7 +300,7 @@ void MetalPipelineCompiler::InitFromState(const LatteFetchShader* fetchShader, c
m_vertexShaderMtl = static_cast<RendererShaderMtl*>(vertexShader->shader);
if (geometryShader)
m_geometryShaderMtl = static_cast<RendererShaderMtl*>(geometryShader->shader);
else if (isPrimitiveRect)
else if (UseRectEmulation(lcr))
m_geometryShaderMtl = rectsEmulationGS_generate(m_mtlr, vertexShader, lcr);
else
m_geometryShaderMtl = nullptr;
@ -315,6 +314,9 @@ void MetalPipelineCompiler::InitFromState(const LatteFetchShader* fetchShader, c
bool MetalPipelineCompiler::Compile(bool forceCompile, bool isRenderThread, bool showInOverlay)
{
if (m_usesGeometryShader && !m_mtlr->SupportsMeshShaders())
return false;
if (forceCompile)
{
// if some shader stages are not compiled yet, compile them now

View file

@ -30,7 +30,7 @@ private:
bool m_usesGeometryShader;
bool m_rasterizationEnabled;
NS::Object* m_pipelineDescriptor;
NS::Object* m_pipelineDescriptor = nullptr;
void InitFromStateRender(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const class MetalAttachmentsInfo& lastUsedAttachmentsInfo, const class MetalAttachmentsInfo& activeAttachmentsInfo, const LatteContextRegister& lcr);

View file

@ -21,7 +21,6 @@
#include "Cemu/Logging/CemuLogging.h"
#include "Cafe/HW/Latte/Core/FetchShader.h"
#include "Cafe/HW/Latte/Core/LatteConst.h"
#include "HW/Latte/Renderer/Metal/MetalCommon.h"
#include "config/CemuConfig.h"
#include "gui/guiWrapper.h"
@ -171,6 +170,7 @@ MetalRenderer::MetalRenderer()
m_supportsFramebufferFetch = GetConfig().framebuffer_fetch.GetValue() ? m_device->supportsFamily(MTL::GPUFamilyApple2) : false;
m_hasUnifiedMemory = m_device->hasUnifiedMemory();
m_supportsMetal3 = m_device->supportsFamily(MTL::GPUFamilyMetal3);
m_supportsMeshShaders = (m_supportsMetal3 && (m_vendor != GfxVendor::Intel || GetConfig().force_mesh_shaders.GetValue())); // Intel GPUs have issues with mesh shaders
m_recommendedMaxVRAMUsage = m_device->recommendedMaxWorkingSetSize();
m_pixelFormatSupport = MetalPixelFormatSupport(m_device);
@ -1134,9 +1134,11 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
// Primitive type
const LattePrimitiveMode primitiveMode = LatteGPUState.contextNew.VGT_PRIMITIVE_TYPE.get_PRIMITIVE_MODE();
auto mtlPrimitiveType = GetMtlPrimitiveType(primitiveMode);
bool isPrimitiveRect = (primitiveMode == Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE::RECTS);
bool usesGeometryShader = (geometryShader != nullptr || isPrimitiveRect);
bool usesGeometryShader = UseGeometryShader(LatteGPUState.contextNew, geometryShader != nullptr);
if (usesGeometryShader && !m_supportsMeshShaders)
return;
bool fetchVertexManually = (usesGeometryShader || fetchShader->mtlFetchVertexManually);
// Index buffer
@ -1293,7 +1295,7 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
// todo - how does culling behave with rects?
// right now we just assume that their winding is always CW
if (isPrimitiveRect)
if (primitiveMode == Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE::RECTS)
{
if (frontFace == Latte::LATTE_PA_SU_SC_MODE_CNTL::E_FRONTFACE::CW)
cullFront = cullBack;
@ -1380,7 +1382,7 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
// Uniform buffers, textures and samplers
BindStageResources(renderCommandEncoder, vertexShader, usesGeometryShader);
if (geometryShader)
if (usesGeometryShader && geometryShader)
BindStageResources(renderCommandEncoder, geometryShader, usesGeometryShader);
BindStageResources(renderCommandEncoder, pixelShader, usesGeometryShader);

View file

@ -6,7 +6,6 @@
#include "Cafe/HW/Latte/Renderer/Metal/MetalPerformanceMonitor.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalOutputShaderCache.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalAttachmentsInfo.h"
#include "Foundation/NSAutoreleasePool.hpp"
enum MetalGeneralShaderType
{
@ -385,6 +384,11 @@ public:
return m_supportsMetal3;
}
bool SupportsMeshShaders() const
{
return m_supportsMeshShaders;
}
//MTL::StorageMode GetOptimalTextureStorageMode() const
//{
// return (m_isAppleGPU ? MTL::StorageModeShared : MTL::StorageModePrivate);
@ -483,6 +487,7 @@ private:
bool m_supportsFramebufferFetch;
bool m_hasUnifiedMemory;
bool m_supportsMetal3;
bool m_supportsMeshShaders;
uint32 m_recommendedMaxVRAMUsage;
MetalPixelFormatSupport m_pixelFormatSupport;

View file

@ -222,6 +222,7 @@ void CemuConfig::Load(XMLConfigParser& parser)
fullscreen_scaling = graphic.get("FullscreenScaling", kKeepAspectRatio);
async_compile = graphic.get("AsyncCompile", async_compile);
vk_accurate_barriers = graphic.get("vkAccurateBarriers", true); // this used to be "VulkanAccurateBarriers" but because we changed the default to true in 1.27.1 the option name had to be changed
force_mesh_shaders = graphic.get("ForceMeshShaders", false);
auto overlay_node = graphic.get("Overlay");
if(overlay_node.valid())
@ -477,6 +478,7 @@ void CemuConfig::Save(XMLConfigParser& parser)
graphic.set("mtlDevice", mtl_graphic_device_uuid);
graphic.set("VSync", vsync);
graphic.set("GX2DrawdoneSync", gx2drawdone_sync);
graphic.set("ForceMeshShaders", force_mesh_shaders);
//graphic.set("PrecompiledShaders", precompiled_shaders.GetValue());
graphic.set("UpscaleFilter", upscale_filter);
graphic.set("DownscaleFilter", downscale_filter);

View file

@ -494,6 +494,7 @@ struct CemuConfig
ConfigValue<bool> gx2drawdone_sync { true };
ConfigValue<bool> render_upside_down{ false };
ConfigValue<bool> async_compile{ true };
ConfigValue<bool> force_mesh_shaders{ false };
ConfigValue<bool> vk_accurate_barriers{ true };

View file

@ -367,6 +367,10 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook)
m_gx2drawdone_sync->SetToolTip(_("If synchronization is requested by the game, the emulated CPU will wait for the GPU to finish all operations.\nThis is more accurate behavior, but may cause lower performance"));
graphic_misc_row->Add(m_gx2drawdone_sync, 0, wxALL, 5);
m_force_mesh_shaders = new wxCheckBox(box, wxID_ANY, _("Force mesh shaders"));
m_force_mesh_shaders->SetToolTip(_("Force mesh shaders on all GPUs that support them. Mesh shaders are disabled by default on Intel GPUs due to potential stability issues"));
graphic_misc_row->Add(m_force_mesh_shaders, 0, wxALL, 5);
box_sizer->Add(graphic_misc_row, 1, wxEXPAND, 5);
graphics_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
}
@ -1100,6 +1104,7 @@ void GeneralSettings2::StoreConfig()
config.vsync = m_vsync->GetSelection();
config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked();
config.force_mesh_shaders = m_force_mesh_shaders->IsChecked();
config.async_compile = m_async_compile->IsChecked();
config.upscale_filter = m_upscale_filter->GetSelection();
@ -1580,12 +1585,14 @@ void GeneralSettings2::HandleGraphicsApiSelection()
m_gx2drawdone_sync->Enable();
m_async_compile->Disable();
m_force_mesh_shaders->Disable();
}
else if (m_graphic_api->GetSelection() == 1)
{
// Vulkan
m_gx2drawdone_sync->Disable();
m_async_compile->Enable();
m_force_mesh_shaders->Disable();
m_vsync->AppendString(_("Off"));
m_vsync->AppendString(_("Double buffering"));
@ -1623,11 +1630,10 @@ void GeneralSettings2::HandleGraphicsApiSelection()
// Metal
m_gx2drawdone_sync->Disable();
m_async_compile->Enable();
m_force_mesh_shaders->Enable();
// TODO: vsync options
m_vsync->AppendString(_("Off"));
m_vsync->AppendString(_("Double buffering"));
m_vsync->AppendString(_("Triple buffering"));
m_vsync->AppendString(_("On"));
m_vsync->Select(selection);
@ -1708,6 +1714,7 @@ void GeneralSettings2::ApplyConfig()
m_vsync->SetSelection(config.vsync);
m_async_compile->SetValue(config.async_compile);
m_gx2drawdone_sync->SetValue(config.gx2drawdone_sync);
m_force_mesh_shaders->SetValue(config.force_mesh_shaders);
m_upscale_filter->SetSelection(config.upscale_filter);
m_downscale_filter->SetSelection(config.downscale_filter);
m_fullscreen_scaling->SetSelection(config.fullscreen_scaling);

View file

@ -53,7 +53,7 @@ private:
// Graphics
wxChoice* m_graphic_api, * m_graphic_device;
wxChoice* m_vsync;
wxCheckBox *m_async_compile, *m_gx2drawdone_sync;
wxCheckBox *m_async_compile, *m_gx2drawdone_sync, *m_force_mesh_shaders;
wxRadioBox* m_upscale_filter, *m_downscale_filter, *m_fullscreen_scaling;
wxChoice* m_overlay_position, *m_notification_position, *m_overlay_scale, *m_notification_scale;
wxCheckBox* m_controller_profile_name, *m_controller_low_battery, *m_shader_compiling, *m_friends_data;