Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binary archives #8

Open
wants to merge 5 commits into
base: metal-pipeline-cache
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Cafe/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,8 @@ if(ENABLE_METAL)
HW/Latte/Renderer/Metal/MetalOutputShaderCache.h
HW/Latte/Renderer/Metal/MetalPipelineCompiler.cpp
HW/Latte/Renderer/Metal/MetalPipelineCompiler.h
HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp
HW/Latte/Renderer/Metal/MetalBinaryArchive.h
HW/Latte/Renderer/Metal/MetalPipelineCache.cpp
HW/Latte/Renderer/Metal/MetalPipelineCache.h
HW/Latte/Renderer/Metal/MetalDepthStencilCache.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "Cafe/HW/Latte/Renderer/Renderer.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h"
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"

#include "config/ActiveSettings.h"
#include "util/helpers/StringBuf.h"

Expand Down
5 changes: 1 addition & 4 deletions src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ LatteTextureMtl::LatteTextureMtl(class MetalRenderer* mtlRenderer, Latte::E_DIM
}
else if (textureType == MTL::TextureTypeCube)
{
// Do notjing
// Do nothing
}
else if (textureType == MTL::TextureTypeCubeArray)
{
Expand All @@ -81,13 +81,10 @@ LatteTextureMtl::LatteTextureMtl(class MetalRenderer* mtlRenderer, Latte::E_DIM

MTL::TextureUsage usage = MTL::TextureUsageShaderRead | MTL::TextureUsagePixelFormatView;
if (!Latte::IsCompressedFormat(format))
{
usage |= MTL::TextureUsageRenderTarget;
}
desc->setUsage(usage);

m_texture = mtlRenderer->GetDevice()->newTexture(desc);

desc->release();
}

Expand Down
201 changes: 201 additions & 0 deletions src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#include "Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"

#include "config/ActiveSettings.h"

MetalBinaryArchive::MetalBinaryArchive(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer}
{
const char* deviceNameSrc = m_mtlr->GetDevice()->name()->utf8String();
std::string deviceName;
deviceName.assign(deviceNameSrc);

// Replace spaces with underscores
for (auto& c : deviceName)
{
if (c == ' ')
c = '_';
}

// OS version
auto osVersion = NS::ProcessInfo::processInfo()->operatingSystemVersion();

// Precompiled binaries cannot be shared between different devices or OS versions
m_archiveDir = ActiveSettings::GetCachePath("shaderCache/precompiled/{}/{}-{}-{}/", deviceName, osVersion.majorVersion, osVersion.minorVersion, osVersion.patchVersion);
fs::create_directories(m_archiveDir);

// Directory for temporary archives
m_tmpArchiveDir = m_archiveDir / "tmp";
fs::create_directories(m_tmpArchiveDir);
}

MetalBinaryArchive::~MetalBinaryArchive()
{
SerializeOldSaveArchive();

// TODO: combine new archives into a single one
}

void MetalBinaryArchive::SetTitleId(uint64 titleId)
{
m_titleId = titleId;

const std::string archiveFilename = fmt::format("{:016x}_mtl_pipelines.bin", titleId);
m_finalArchivePath = m_archiveDir / archiveFilename;
}

void MetalBinaryArchive::LoadSerializedArchive()
{
m_isLoading = true;

if (!std::filesystem::exists(m_finalArchivePath))
{
CreateSaveArchive();
return;
}

// Load the binary archive
MTL::BinaryArchiveDescriptor* desc = MTL::BinaryArchiveDescriptor::alloc()->init();
NS::URL* url = ToNSURL(m_finalArchivePath);
desc->setUrl(url);
url->release();

NS::Error* error = nullptr;
m_loadArchive = m_mtlr->GetDevice()->newBinaryArchive(desc, &error);
if (error)
{
cemuLog_log(LogType::Force, "failed to load binary archive: {}", error->localizedDescription()->utf8String());
error->release();
}
desc->release();

// Create an array for the archive
NS::Object* binArchives[] = {m_loadArchive};
m_loadArchiveArray = NS::Array::alloc()->init(binArchives, 1);
}

void MetalBinaryArchive::CloseSerializedArchive()
{
if (m_loadArchive)
{
m_loadArchive->release();
m_loadArchive = nullptr;
}
if (m_loadArchiveArray)
{
m_loadArchiveArray->release();
m_loadArchiveArray = nullptr;
}

m_lock.lock();
if (m_saveArchive)
{
SerializeArchive(m_saveArchive, m_finalArchivePath);
m_saveArchive = nullptr;
}
m_lock.unlock();

m_isLoading = false;
}

void MetalBinaryArchive::LoadPipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor) {
if (m_loadArchiveArray)
renderPipelineDescriptor->setBinaryArchives(m_loadArchiveArray);
}

// TODO: should be available since macOS 15.0
/*
void MetalBinaryArchive::LoadPipeline(MTL::MeshRenderPipelineDescriptor* renderPipelineDescriptor) {
if (m_loadArchiveArray)
renderPipelineDescriptor->setBinaryArchives(m_loadArchiveArray);
}
*/

void MetalBinaryArchive::SavePipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor) {
m_lock.lock();

LoadSaveArchive();

if (m_saveArchive)
{
NS::Error* error = nullptr;
m_saveArchive->addRenderPipelineFunctions(renderPipelineDescriptor, &error);
if (error)
{
cemuLog_log(LogType::Force, "error saving render pipeline functions: {}", error->localizedDescription()->utf8String());
error->release();
}
m_pipelinesSerialized++;
}

m_lock.unlock();
}

// TODO: should be available since macOS 15.0
/*
void MetalBinaryArchive::SavePipeline(MTL::MeshRenderPipelineDescriptor* renderPipelineDescriptor) {
LoadSaveArchive();

if (m_saveArchive)
{
NS::Error* error = nullptr;
m_saveArchive->addMeshRenderPipelineFunctions(renderPipelineDescriptor, &error);
if (error)
{
cemuLog_log(LogType::Force, "error saving mesh pipeline functions: {}", error->localizedDescription()->utf8String());
error->release();
}
m_pipelinesSerialized++;
}
}
*/

void MetalBinaryArchive::SerializeArchive(MTL::BinaryArchive* archive, const fs::path& path)
{
NS::Error* error = nullptr;
NS::URL* url = ToNSURL(path);
archive->serializeToURL(url, &error);
url->release();
if (error)
{
cemuLog_log(LogType::Force, "failed to serialize binary archive: {}", error->localizedDescription()->utf8String());
error->release();
}
archive->release();
}

void MetalBinaryArchive::SerializeOldSaveArchive()
{
if (!m_saveArchive)
return;

SerializeArchive(m_saveArchive, GetTmpArchivePath(m_archiveIndex));

m_archiveIndex++;
m_pipelinesSerialized = 0;
}

void MetalBinaryArchive::CreateSaveArchive()
{
MTL::BinaryArchiveDescriptor* desc = MTL::BinaryArchiveDescriptor::alloc()->init();

NS::Error* error = nullptr;
m_saveArchive = m_mtlr->GetDevice()->newBinaryArchive(desc, &error);
if (error)
{
cemuLog_log(LogType::Force, "failed to create save binary archive: {}", error->localizedDescription()->utf8String());
error->release();
}
desc->release();
}

void MetalBinaryArchive::LoadSaveArchive()
{
if (m_isLoading || (m_saveArchive && m_pipelinesSerialized < SERIALIZE_TRESHOLD))
return;

// First, save the old archive to disk
SerializeOldSaveArchive();

// Create a new archive
CreateSaveArchive();
}
56 changes: 56 additions & 0 deletions src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#pragma once

#include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h"
#include "util/helpers/fspinlock.h"

class MetalBinaryArchive
{
public:
static constexpr uint32 SERIALIZE_TRESHOLD = 64;

MetalBinaryArchive(class MetalRenderer* metalRenderer);
~MetalBinaryArchive();

void SetTitleId(uint64 titleId);

void LoadSerializedArchive();
void CloseSerializedArchive();

// For pipeline compiler
void LoadPipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor);
//void LoadPipeline(MTL::MeshRenderPipelineDescriptor* renderPipelineDescriptor);
void SavePipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor);
//void SavePipeline(MTL::MeshRenderPipelineDescriptor* renderPipelineDescriptor);

private:
class MetalRenderer* m_mtlr;

uint64 m_titleId;

bool m_isLoading = false;

// Paths
fs::path m_archiveDir;
fs::path m_tmpArchiveDir;
fs::path m_finalArchivePath;

// Binary archives
MTL::BinaryArchive* m_loadArchive = nullptr;
NS::Array* m_loadArchiveArray = nullptr;
MTL::BinaryArchive* m_saveArchive = nullptr;

uint32 m_pipelinesSerialized = 0;
uint32 m_archiveIndex = 0;

FSpinlock m_lock;

void SerializeArchive(MTL::BinaryArchive* archive, const fs::path& path);
void SerializeOldSaveArchive();
void CreateSaveArchive();
void LoadSaveArchive();

fs::path GetTmpArchivePath(uint32 index)
{
return m_tmpArchiveDir / fs::path("archive" + std::to_string(index) + ".bin");
}
};
9 changes: 9 additions & 0 deletions src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,20 @@ inline NS::String* ToNSString(const std::string& str)
return ToNSString(str.c_str());
}

// Cast from fs::path to NS::URL*
inline NS::URL* ToNSURL(const fs::path& path)
{
return NS::URL::fileURLWithPath(ToNSString(_pathToUtf8(path)));
}

// Get a label for a resource
inline NS::String* GetLabel(const std::string& label, const void* identifier)
{
return ToNSString(label + " (" + std::to_string(reinterpret_cast<uintptr_t>(identifier)) + ")");
}

// TODO: make a helper function to set the label for a resource

constexpr MTL::RenderStages ALL_MTL_RENDER_STAGES = MTL::RenderStageVertex | MTL::RenderStageObject | MTL::RenderStageMesh | MTL::RenderStageFragment;

inline bool IsValidDepthTextureType(Latte::E_DIM dim)
Expand Down
14 changes: 11 additions & 3 deletions src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ MetalPipelineCache& MetalPipelineCache::GetInstance()
return *g_mtlPipelineCache;
}

MetalPipelineCache::MetalPipelineCache(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer}
MetalPipelineCache::MetalPipelineCache(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer}, m_binaryArchive(metalRenderer)
{
g_mtlPipelineCache = this;
}
Expand All @@ -51,7 +51,7 @@ MTL::RenderPipelineState* MetalPipelineCache::GetRenderPipelineState(const Latte
bool fbosMatch;
compiler.InitFromState(fetchShader, vertexShader, geometryShader, pixelShader, lastUsedAttachmentsInfo, activeAttachmentsInfo, lcr, fbosMatch);
bool attemptedCompilation = false;
MTL::RenderPipelineState* pipeline = compiler.Compile(false, true, true, attemptedCompilation);
MTL::RenderPipelineState* pipeline = compiler.Compile(m_binaryArchive, false, true, true, attemptedCompilation);

// If FBOs don't match, it wouldn't be possible to reconstruct the pipeline from the cache
if (pipeline && fbosMatch)
Expand Down Expand Up @@ -176,6 +176,10 @@ struct

uint32 MetalPipelineCache::BeginLoading(uint64 cacheTitleId)
{
// Notify the binary archive
m_binaryArchive.SetTitleId(cacheTitleId);
m_binaryArchive.LoadSerializedArchive();

std::error_code ec;
fs::create_directories(ActiveSettings::GetCachePath("shaderCache/transferable"), ec);
const auto pathCacheFile = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_mtlpipeline.bin", cacheTitleId);
Expand Down Expand Up @@ -216,6 +220,7 @@ uint32 MetalPipelineCache::BeginLoading(uint64 cacheTitleId)
s_cache->UseCompression(false);
g_mtlCacheState.pipelineMaxFileIndex = s_cache->GetMaximumFileIndex();
}

return s_cache->GetFileCount();
}

Expand Down Expand Up @@ -253,6 +258,9 @@ bool MetalPipelineCache::UpdateLoading(uint32& pipelinesLoadedTotal, uint32& pip

void MetalPipelineCache::EndLoading()
{
// Notify the binary archive
m_binaryArchive.CloseSerializedArchive();

// shut down compilation threads
uint32 threadCount = m_numCompilationThreads;
m_numCompilationThreads = 0; // signal thread shutdown
Expand Down Expand Up @@ -375,7 +383,7 @@ void MetalPipelineCache::LoadPipelineFromCache(std::span<uint8> fileData)
// return;
//}
bool attemptedCompilation = false;
pipeline = pp.Compile(true, true, false, attemptedCompilation);
pipeline = pp.Compile(m_binaryArchive, true, true, false, attemptedCompilation);
cemu_assert_debug(attemptedCompilation);
// destroy pp early
}
Expand Down
4 changes: 3 additions & 1 deletion src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#pragma once

#include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h"
#include "util/helpers/ConcurrentQueue.h"
#include "util/helpers/fspinlock.h"

// TODO: binary archives
class MetalPipelineCache
{
public:
Expand All @@ -28,6 +28,8 @@ class MetalPipelineCache
private:
class MetalRenderer* m_mtlr;

MetalBinaryArchive m_binaryArchive;

std::map<uint64, MTL::RenderPipelineState*> m_pipelineCache;
FSpinlock m_pipelineCacheLock;

Expand Down
Loading
Loading