mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
feat: 实现效果缓存
This commit is contained in:
parent
4be6613482
commit
379982086c
6 changed files with 453 additions and 90 deletions
|
|
@ -1,17 +1,22 @@
|
|||
#include "pch.h"
|
||||
#include "AppFolderManager.h"
|
||||
#include "CommonSharedConstants.h"
|
||||
#include "EffectsService.h"
|
||||
#include "Logger.h"
|
||||
#include "ShaderEffectParser.h"
|
||||
#include "StrHelper.h"
|
||||
#include "Win32Helper.h"
|
||||
#include "YasHelper.h"
|
||||
#include <d3dcompiler.h>
|
||||
#include <dxcapi.h>
|
||||
#include <rapidhash.h>
|
||||
|
||||
namespace Magpie {
|
||||
|
||||
static constexpr uint32_t MAX_MEM_CACHE_COUNT = 63;
|
||||
|
||||
// 缓存版本。当缓存文件结构有更改时更新它,使旧缓存失效
|
||||
static constexpr uint32_t EFFECT_CACHE_VERSION = 16;
|
||||
|
||||
static void ListEffects(std::vector<std::wstring>& result, std::wstring_view prefix = {}) {
|
||||
result.reserve(80);
|
||||
|
||||
|
|
@ -58,28 +63,30 @@ winrt::fire_and_forget EffectsService::Initialize() {
|
|||
|
||||
std::filesystem::path effectsDir = AppFolderManager::Get().GetBuiltInShaderEffectsDir();
|
||||
|
||||
// 用于同步 _effectsMap 和 _effects 的初始化
|
||||
wil::srwlock srwLock;
|
||||
{
|
||||
// 用于同步 _effectsMap 和 _effects 的初始化
|
||||
wil::srwlock srwLock;
|
||||
|
||||
// 并行解析效果
|
||||
Win32Helper::RunParallel([&](uint32_t id) {
|
||||
std::wstring fileName = StrHelper::Concat(
|
||||
effectsDir.native(), L"\\", effectNames[id], L".hlsl");
|
||||
std::string source;
|
||||
Win32Helper::ReadTextFile(fileName.c_str(), source);
|
||||
EffectInfo effectInfo;
|
||||
std::string errorMsg = ShaderEffectParser::ParseForInfo(
|
||||
StrHelper::UTF16ToUTF8(effectNames[id]), std::move(source), effectInfo);
|
||||
if (!errorMsg.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = srwLock.lock_exclusive();
|
||||
uint32_t effectIdx = (uint32_t)_effects.size();
|
||||
EffectInfo& movedEffectInfo = _effects.emplace_back(std::move(effectInfo));
|
||||
_effectsMap.emplace(movedEffectInfo.name, effectIdx);
|
||||
}, nEffect);
|
||||
// 并行解析效果
|
||||
Win32Helper::RunParallel([&](uint32_t id) {
|
||||
std::wstring fileName = StrHelper::Concat(
|
||||
effectsDir.native(), L"\\", effectNames[id], L".hlsl");
|
||||
std::string source;
|
||||
Win32Helper::ReadTextFile(fileName.c_str(), source);
|
||||
EffectInfo effectInfo;
|
||||
std::string errorMsg = ShaderEffectParser::ParseForInfo(
|
||||
StrHelper::UTF16ToUTF8(effectNames[id]), std::move(source), effectInfo);
|
||||
if (!errorMsg.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = srwLock.lock_exclusive();
|
||||
uint32_t effectIdx = (uint32_t)_effects.size();
|
||||
EffectInfo& movedEffectInfo = _effects.emplace_back(std::move(effectInfo));
|
||||
_effectsMap.emplace(movedEffectInfo.name, effectIdx);
|
||||
}, nEffect);
|
||||
}
|
||||
|
||||
_initialized.store(true, std::memory_order_release);
|
||||
_initialized.notify_one();
|
||||
}
|
||||
|
|
@ -87,6 +94,9 @@ winrt::fire_and_forget EffectsService::Initialize() {
|
|||
void EffectsService::Uninitialize() {
|
||||
// 等待解析完成,防止退出时崩溃
|
||||
_WaitForInitialize();
|
||||
|
||||
auto lock = _stopSource->lock.lock_exclusive();
|
||||
_stopSource->isUninitialized = true;
|
||||
}
|
||||
|
||||
const std::vector<EffectInfo>& EffectsService::GetEffects() noexcept {
|
||||
|
|
@ -114,9 +124,9 @@ static std::string GetCacheFileName(
|
|||
}
|
||||
}
|
||||
|
||||
// 缓存文件的命名: {效果名}_{shader model(2)}{标志位(4)}{哈希(16))}
|
||||
// 缓存文件的命名: {效果名}_{shader model|2}{标志位|4}{哈希|16}
|
||||
return fmt::format("{}_{:02x}{:04x}{:016x}",
|
||||
linearEffectName, (uint16_t)shaderModel, (uint32_t)flags, hash);
|
||||
linearEffectName, (uint8_t)shaderModel, (uint16_t)flags, hash);
|
||||
}
|
||||
|
||||
std::string EffectsService::SubmitCompileShaderEffectTask(
|
||||
|
|
@ -127,7 +137,8 @@ std::string EffectsService::SubmitCompileShaderEffectTask(
|
|||
bool isNative16BitSupported,
|
||||
bool isAdvancedColorSupported,
|
||||
bool saveSources,
|
||||
bool warningsAreErrors
|
||||
bool warningsAreErrors,
|
||||
bool disableCache
|
||||
) noexcept {
|
||||
_WaitForInitialize();
|
||||
|
||||
|
|
@ -187,11 +198,45 @@ std::string EffectsService::SubmitCompileShaderEffectTask(
|
|||
{
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
|
||||
if (_shaderEffectCache.contains(cacheKey)) {
|
||||
return cacheKey;
|
||||
auto it1 = _shaderEffectCache.find(cacheKey);
|
||||
if (it1 != _shaderEffectCache.end()) {
|
||||
_ShaderEffectMemCacheItem& cacheItem = it1->second;
|
||||
|
||||
// 禁用缓存时总是重新编译,除非有多个相同的效果
|
||||
if (disableCache && cacheItem.refCount == 0) {
|
||||
_shaderEffectCache.erase(it1);
|
||||
} else {
|
||||
cacheItem.lastAccess = _nextLastAccess++;
|
||||
++cacheItem.refCount;
|
||||
return cacheKey;
|
||||
}
|
||||
}
|
||||
|
||||
_shaderEffectCache.emplace(cacheKey, _ShaderEffectMemCacheItem{});
|
||||
_shaderEffectCache.emplace(cacheKey, _ShaderEffectMemCacheItem{
|
||||
.lastAccess = _nextLastAccess++,
|
||||
.refCount = 1
|
||||
});
|
||||
|
||||
// 超过限制则清理一半较旧的内存缓存
|
||||
if (_shaderEffectCache.size() > MAX_MEM_CACHE_COUNT) {
|
||||
assert(_shaderEffectCache.size() == MAX_MEM_CACHE_COUNT + 1);
|
||||
std::array<uint32_t, MAX_MEM_CACHE_COUNT + 1> allLastAccess{};
|
||||
std::transform(_shaderEffectCache.begin(), _shaderEffectCache.end(), allLastAccess.begin(),
|
||||
[](const auto& pair) { return pair.second.lastAccess; });
|
||||
|
||||
auto midIt = allLastAccess.begin() + allLastAccess.size() / 2;
|
||||
std::nth_element(allLastAccess.begin(), midIt, allLastAccess.end());
|
||||
uint32_t midLastAccess = *midIt;
|
||||
|
||||
for (it1 = _shaderEffectCache.begin(); it1 != _shaderEffectCache.end();) {
|
||||
// 未被使用时才能删除
|
||||
if (it1->second.lastAccess < midLastAccess && it1->second.refCount == 0) {
|
||||
it1 = _shaderEffectCache.erase(it1);
|
||||
} else {
|
||||
++it1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_CompileShaderEffectAsync(
|
||||
|
|
@ -202,7 +247,8 @@ std::string EffectsService::SubmitCompileShaderEffectTask(
|
|||
cacheKey,
|
||||
(uint32_t)parserFlags,
|
||||
saveSources,
|
||||
warningsAreErrors
|
||||
warningsAreErrors,
|
||||
disableCache
|
||||
);
|
||||
|
||||
return cacheKey;
|
||||
|
|
@ -238,7 +284,8 @@ void EffectsService::ReleaseTask(const std::string& taskKey) noexcept {
|
|||
return;
|
||||
}
|
||||
|
||||
it->second.lastAccess = _nextLastAccess++;
|
||||
assert(it->second.refCount >= 1);
|
||||
--it->second.refCount;
|
||||
}
|
||||
|
||||
void EffectsService::_WaitForInitialize() noexcept {
|
||||
|
|
@ -289,6 +336,114 @@ private:
|
|||
std::filesystem::path _localDir;
|
||||
};
|
||||
|
||||
template <typename Archive>
|
||||
void serialize(Archive& ar, ShaderEffectTextureDesc& o) {
|
||||
ar& o.name& o.format& o.widthExpr& o.heightExpr& o.source;
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
void serialize(Archive& ar, ShaderEffectSamplerDesc& o) {
|
||||
ar& o.name& o.filterType& o.addressType;
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
void serialize(Archive& ar, ShaderEffectPassDesc& o) {
|
||||
ar& o.desc& o.byteCode& o.inputs& o.outputs& o.numThreads & o.blockSize& o.flags;
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
void serialize(Archive& ar, ShaderEffectDrawInfo& o) {
|
||||
ar& o.textures& o.samplers& o.passes;
|
||||
}
|
||||
|
||||
static bool ReadFileCache(const std::string& key, ShaderEffectDrawInfo& drawInfo) noexcept {
|
||||
const wchar_t* cacheDir = AppFolderManager::Get().GetCacheDir();
|
||||
std::wstring cacheFilePath = StrHelper::Concat(cacheDir, L"\\", StrHelper::UTF8ToUTF16(key));
|
||||
if (!Win32Helper::FileExists(cacheFilePath.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> buffer;
|
||||
if (!Win32Helper::ReadFile(cacheFilePath.c_str(), buffer) || buffer.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
yas::mem_istream mi(buffer.data(), buffer.size());
|
||||
yas::binary_iarchive<yas::mem_istream, yas::binary> ia(mi);
|
||||
|
||||
uint32_t version;
|
||||
ia.read(version);
|
||||
if (version != EFFECT_CACHE_VERSION) {
|
||||
Logger::Get().Info("缓存版本不匹配");
|
||||
return false;
|
||||
}
|
||||
|
||||
ia& drawInfo;
|
||||
return true;
|
||||
} catch (...) {
|
||||
Logger::Get().Error("反序列化失败");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool WriteFileCache(const std::string& key, const ShaderEffectDrawInfo& drawInfo) noexcept {
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.reserve(4096);
|
||||
|
||||
// 序列化
|
||||
try {
|
||||
yas::vector_ostream os(buffer);
|
||||
yas::binary_oarchive<yas::vector_ostream<uint8_t>, yas::binary> oa(os);
|
||||
|
||||
oa.write(EFFECT_CACHE_VERSION);
|
||||
oa& drawInfo;
|
||||
} catch (...) {
|
||||
Logger::Get().Error("序列化 ShaderEffectDrawInfo 失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
const wchar_t* cacheDir = AppFolderManager::Get().GetCacheDir();
|
||||
if (!Win32Helper::CreateDir(cacheDir, true)) {
|
||||
Logger::Get().Error("创建缓存文件夹失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
WIN32_FIND_DATA findData{};
|
||||
wil::unique_hfind hFind(FindFirstFileEx(
|
||||
StrHelper::Concat(cacheDir, L"\\*").c_str(),
|
||||
FindExInfoBasic, &findData, FindExSearchNameMatch, nullptr, FIND_FIRST_EX_LARGE_FETCH));
|
||||
if (hFind) {
|
||||
// 缓存文件的命名: {效果名}_{shader model|2}{标志位|4}{哈希|16}
|
||||
assert(key.size() >= 24);
|
||||
// 只有哈希不同则删除,否则保留。也就是说:
|
||||
// 1. 效果源代码修改后删除旧缓存
|
||||
// 2. 启用内联效果参数时删除参数不同的缓存
|
||||
std::wstring prefix = StrHelper::UTF8ToUTF16(std::string_view(key.c_str(), key.size() - 16));
|
||||
|
||||
do {
|
||||
std::wstring_view fileName(findData.cFileName);
|
||||
if (fileName.size() == key.size() && fileName.starts_with(prefix)) {
|
||||
if (!DeleteFile(StrHelper::Concat(cacheDir, L"\\", findData.cFileName).c_str())) {
|
||||
Logger::Get().Win32Error(StrHelper::Concat("删除缓存文件 ",
|
||||
StrHelper::UTF16ToUTF8(findData.cFileName), " 失败"));
|
||||
}
|
||||
}
|
||||
} while (FindNextFile(hFind.get(), &findData));
|
||||
} else {
|
||||
Logger::Get().Win32Error("查找缓存文件失败");
|
||||
}
|
||||
|
||||
std::wstring cacheFilePath = StrHelper::Concat(cacheDir, L"\\", StrHelper::UTF8ToUTF16(key));
|
||||
if (!Win32Helper::WriteFile(cacheFilePath.c_str(), buffer)) {
|
||||
Logger::Get().Error("保存缓存失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
winrt::fire_and_forget EffectsService::_CompileShaderEffectAsync(
|
||||
std::string effectName,
|
||||
std::string source,
|
||||
|
|
@ -297,51 +452,105 @@ winrt::fire_and_forget EffectsService::_CompileShaderEffectAsync(
|
|||
std::string cacheKey,
|
||||
uint32_t parserFlags,
|
||||
bool saveSources,
|
||||
bool warningsAreErrors
|
||||
bool warningsAreErrors,
|
||||
bool disableCache
|
||||
) noexcept {
|
||||
// 允许在编译中途退出,因此访问成员甚至全局变量时必须小心,确保加锁后再访问!
|
||||
std::shared_ptr<_StopSource> stopSource(_stopSource);
|
||||
|
||||
co_await winrt::resume_background();
|
||||
|
||||
// 如果以后 _effectsMap 会变,这里应加锁
|
||||
auto it = _effectsMap.find(effectName);
|
||||
if (it == _effectsMap.end()) {
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
_shaderEffectCache.erase(cacheKey);
|
||||
co_return;
|
||||
}
|
||||
const EffectInfo& effectInfo = _effects[it->second];
|
||||
|
||||
ShaderEffectParserOptions options = {
|
||||
.inlineParams = inlineParams,
|
||||
.shaderModel = shaderModel,
|
||||
.flags = (ShaderEffectParserFlags)parserFlags
|
||||
};
|
||||
|
||||
ShaderEffectParserOptions options;
|
||||
ShaderEffectDrawInfo effectDrawInfo;
|
||||
SmallVector<ShaderEffectSource, 0> effectSources;
|
||||
std::string errorMsg = ShaderEffectParser::ParseForDesc(
|
||||
effectInfo,
|
||||
std::move(source),
|
||||
options,
|
||||
effectDrawInfo,
|
||||
effectSources
|
||||
);
|
||||
if (!errorMsg.empty()) {
|
||||
// 解析失败
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
_shaderEffectCache.erase(cacheKey);
|
||||
}
|
||||
|
||||
std::wstring sourcesPath;
|
||||
if (saveSources) {
|
||||
sourcesPath = StrHelper::Concat(AppFolderManager::Get().GetSourcesDir(),
|
||||
L"\\", StrHelper::UTF8ToUTF16(effectInfo.name));
|
||||
|
||||
std::wstring sourcesDir = sourcesPath.substr(0, sourcesPath.find_last_of(L'\\'));
|
||||
if (!Win32Helper::CreateDir(sourcesDir, true)) {
|
||||
Logger::Get().Error("Win32Helper::CreateDir 失败");
|
||||
{
|
||||
auto stopSourceLock = stopSource->lock.lock_shared();
|
||||
if (stopSource->isUninitialized) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
if (!disableCache) {
|
||||
// 尝试读取文件缓存
|
||||
if (ReadFileCache(cacheKey, effectDrawInfo)) {
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
|
||||
auto it = _shaderEffectCache.find(cacheKey);
|
||||
if (it == _shaderEffectCache.end()) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
it->second.drawInfo = std::move(effectDrawInfo);
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果以后 _effectsMap 会变,这里应加锁
|
||||
auto it = _effectsMap.find(effectName);
|
||||
if (it == _effectsMap.end()) {
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
_shaderEffectCache.erase(cacheKey);
|
||||
co_return;
|
||||
}
|
||||
const EffectInfo& effectInfo = _effects[it->second];
|
||||
|
||||
options = ShaderEffectParserOptions{
|
||||
.inlineParams = inlineParams,
|
||||
.shaderModel = shaderModel,
|
||||
.flags = (ShaderEffectParserFlags)parserFlags
|
||||
};
|
||||
|
||||
std::string errorMsg = ShaderEffectParser::ParseForDesc(
|
||||
effectInfo,
|
||||
std::move(source),
|
||||
options,
|
||||
effectDrawInfo,
|
||||
effectSources
|
||||
);
|
||||
if (!errorMsg.empty()) {
|
||||
// 解析失败
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
_shaderEffectCache.erase(cacheKey);
|
||||
}
|
||||
|
||||
if (saveSources) {
|
||||
sourcesPath = StrHelper::Concat(AppFolderManager::Get().GetSourcesDir(),
|
||||
L"\\", StrHelper::UTF8ToUTF16(effectInfo.name));
|
||||
|
||||
std::wstring sourcesDir = sourcesPath.substr(0, sourcesPath.find_last_of(L'\\'));
|
||||
if (!Win32Helper::CreateDir(sourcesDir, true)) {
|
||||
Logger::Get().Error("Win32Helper::CreateDir 失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 由于允许在编译中途退出,访问 Logger 要加锁!
|
||||
auto logComError = [&](std::string_view msg, HRESULT hr,
|
||||
const SourceLocation& location = SourceLocation::Current())
|
||||
{
|
||||
auto stopSourceLock = stopSource->lock.lock_shared();
|
||||
if (!stopSource->isUninitialized) {
|
||||
Logger::Get().ComError(msg, hr, location);
|
||||
}
|
||||
};
|
||||
|
||||
auto logWarn = [&](std::string_view msg,
|
||||
const SourceLocation& location = SourceLocation::Current()) {
|
||||
auto stopSourceLock = stopSource->lock.lock_shared();
|
||||
if (!stopSource->isUninitialized) {
|
||||
Logger::Get().Warn(msg, location);
|
||||
}
|
||||
};
|
||||
|
||||
auto logError = [&](std::string_view msg,
|
||||
const SourceLocation& location = SourceLocation::Current()) {
|
||||
auto stopSourceLock = stopSource->lock.lock_shared();
|
||||
if (!stopSource->isUninitialized) {
|
||||
Logger::Get().Error(msg, location);
|
||||
}
|
||||
};
|
||||
|
||||
std::filesystem::path includeDir = AppFolderManager::Get().GetBuiltInShaderEffectsDir();
|
||||
size_t delimPos = effectName.find_last_of('\\');
|
||||
if (delimPos != std::string::npos) {
|
||||
|
|
@ -357,7 +566,7 @@ winrt::fire_and_forget EffectsService::_CompileShaderEffectAsync(
|
|||
: fmt::format(L"{}_Pass{}.hlsl", sourcesPath, id + 1);
|
||||
|
||||
if (!Win32Helper::WriteTextFile(fileName.c_str(), source)) {
|
||||
Logger::Get().Error(fmt::format("保存 Pass{} 源码失败", id + 1));
|
||||
logError(fmt::format("保存 Pass{} 源码失败", id + 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -368,13 +577,13 @@ winrt::fire_and_forget EffectsService::_CompileShaderEffectAsync(
|
|||
|
||||
HRESULT hr = DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&dxcUtils));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("DxcCreateInstance 失败", hr);
|
||||
logComError("DxcCreateInstance 失败", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
hr = DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&dxcCompiler));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("DxcCreateInstance 失败", hr);
|
||||
logComError("DxcCreateInstance 失败", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -461,7 +670,7 @@ winrt::fire_and_forget EffectsService::_CompileShaderEffectAsync(
|
|||
winrt::com_ptr<IDxcIncludeHandler> includeHandler;
|
||||
hr = dxcUtils->CreateDefaultIncludeHandler(includeHandler.put());
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("IDxcUtils::CreateDefaultIncludeHandler 失败", hr);
|
||||
logComError("IDxcUtils::CreateDefaultIncludeHandler 失败", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -474,27 +683,27 @@ winrt::fire_and_forget EffectsService::_CompileShaderEffectAsync(
|
|||
IID_PPV_ARGS(&dxcResult)
|
||||
);
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("IDxcCompiler3::Compile 失败", hr);
|
||||
logComError("IDxcCompiler3::Compile 失败", hr);
|
||||
return;
|
||||
}
|
||||
|
||||
winrt::com_ptr<IDxcBlobUtf8> messages;
|
||||
dxcResult->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(&messages), nullptr);
|
||||
if (messages && messages->GetStringLength() > 0) {
|
||||
Logger::Get().Warn(StrHelper::Concat("编译着色器输出: ", messages->GetStringPointer()));
|
||||
logWarn(StrHelper::Concat("编译着色器输出: ", messages->GetStringPointer()));
|
||||
return;
|
||||
}
|
||||
|
||||
HRESULT compileStatus;
|
||||
dxcResult->GetStatus(&compileStatus);
|
||||
if (FAILED(compileStatus)) {
|
||||
Logger::Get().ComError("编译着色器失败", compileStatus);
|
||||
logComError("编译着色器失败", compileStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
hr = dxcResult->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(&effectDrawInfo.passes[id].byteCode), nullptr);
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("IDxcResult::GetOutput 失败", hr);
|
||||
logComError("IDxcResult::GetOutput 失败", hr);
|
||||
}
|
||||
} else {
|
||||
winrt::com_ptr<ID3DBlob> errorMsg;
|
||||
|
|
@ -532,32 +741,54 @@ winrt::fire_and_forget EffectsService::_CompileShaderEffectAsync(
|
|||
);
|
||||
if (FAILED(hr)) {
|
||||
if (errorMsg) {
|
||||
Logger::Get().ComError(StrHelper::Concat("编译着色器失败: ", (const char*)errorMsg->GetBufferPointer()), hr);
|
||||
logComError(StrHelper::Concat("编译着色器失败: ", (const char*)errorMsg->GetBufferPointer()), hr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 警告消息
|
||||
if (errorMsg) {
|
||||
Logger::Get().Warn(StrHelper::Concat("编译着色器时产生警告: ", (const char*)errorMsg->GetBufferPointer()));
|
||||
logWarn(StrHelper::Concat("编译着色器时产生警告: ", (const char*)errorMsg->GetBufferPointer()));
|
||||
}
|
||||
}
|
||||
}, (uint32_t)effectDrawInfo.passes.size());
|
||||
|
||||
for (const ShaderEffectPassDesc& passDesc : effectDrawInfo.passes) {
|
||||
if (!passDesc.byteCode) {
|
||||
// 编译失败
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
_shaderEffectCache.erase(cacheKey);
|
||||
{
|
||||
auto stopSourceLock = stopSource->lock.lock_shared();
|
||||
if (stopSource->isUninitialized) {
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
for (const ShaderEffectPassDesc& passDesc : effectDrawInfo.passes) {
|
||||
if (!passDesc.byteCode) {
|
||||
// 编译失败
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
_shaderEffectCache.erase(cacheKey);
|
||||
co_return;
|
||||
}
|
||||
}
|
||||
|
||||
auto it1 = _shaderEffectCache.find(cacheKey);
|
||||
if (it1 != _shaderEffectCache.end()) {
|
||||
it1->second.drawInfo = std::move(effectDrawInfo);
|
||||
{
|
||||
auto lk = _shaderEffectCacheLock.lock_exclusive();
|
||||
|
||||
auto it = _shaderEffectCache.find(cacheKey);
|
||||
if (it == _shaderEffectCache.end()) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
// 需要写入文件缓存时应复制而不是移动以避免加锁
|
||||
if (disableCache) {
|
||||
it->second.drawInfo = std::move(effectDrawInfo);
|
||||
co_return;
|
||||
} else {
|
||||
it->second.drawInfo = effectDrawInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建文件缓存
|
||||
if (!WriteFileCache(cacheKey, effectDrawInfo)) {
|
||||
Logger::Get().Error("WriteFileCache 失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@
|
|||
<ClInclude Include="SrcTracker.h" />
|
||||
<ClInclude Include="StepTimer.h" />
|
||||
<ClInclude Include="SwapChainPresenter.h" />
|
||||
<ClInclude Include="YasHelper.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AppFolderManager.cpp" />
|
||||
|
|
|
|||
|
|
@ -135,6 +135,9 @@
|
|||
<ClInclude Include="include\AppFolderManager.h">
|
||||
<Filter>Include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="YasHelper.h">
|
||||
<Filter>Helpers</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ScalingRuntime.cpp" />
|
||||
|
|
|
|||
|
|
@ -96,7 +96,8 @@ void ShaderEffectDrawer::Bind(SizeU inputSize, SizeU outputSize, const ColorInfo
|
|||
_d3d12Context->IsNative16BitSupported(),
|
||||
colorInfo.kind != winrt::AdvancedColorKind::StandardDynamicRange,
|
||||
options.IsSaveEffectSources(),
|
||||
options.IsWarningsAreErrors()
|
||||
options.IsWarningsAreErrors(),
|
||||
options.IsEffectCacheDisabled()
|
||||
);
|
||||
if (_compilationTaskId.empty()) {
|
||||
_errorMsg = "编译失败";
|
||||
|
|
|
|||
117
src/Magpie.Core/YasHelper.h
Normal file
117
src/Magpie.Core/YasHelper.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#pragma once
|
||||
#include "ByteBuffer.h"
|
||||
#include "SmallVector.h"
|
||||
// YAS 暂不支持 ARM64
|
||||
// https://github.com/niXman/yas/pull/121
|
||||
#ifdef _M_ARM64
|
||||
#define _LITTLE_ENDIAN
|
||||
#endif
|
||||
#pragma warning(push)
|
||||
// C4458: “size”的声明隐藏了类成员
|
||||
// C4127: 条件表达式是常量
|
||||
#pragma warning(disable: 4458 4127)
|
||||
#include <yas/mem_streams.hpp>
|
||||
#include <yas/binary_oarchive.hpp>
|
||||
#include <yas/binary_iarchive.hpp>
|
||||
#include <yas/types/std/pair.hpp>
|
||||
#include <yas/types/std/string.hpp>
|
||||
#include <yas/types/std/string_view.hpp>
|
||||
#include <yas/types/std/vector.hpp>
|
||||
#include <yas/types/std/variant.hpp>
|
||||
#pragma warning(pop)
|
||||
|
||||
namespace yas::detail {
|
||||
|
||||
// 可平凡复制类型,注意不检查指针成员
|
||||
template <size_t F, typename T>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
T
|
||||
> {
|
||||
template <typename Archive, typename = std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T>>
|
||||
static Archive& save(Archive& ar, const T& o) noexcept {
|
||||
ar.write(&o, sizeof(T));
|
||||
return ar;
|
||||
}
|
||||
|
||||
template <typename Archive, typename = std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T>>
|
||||
static Archive& load(Archive& ar, T& o) noexcept {
|
||||
ar.read(&o, sizeof(T));
|
||||
return ar;
|
||||
}
|
||||
};
|
||||
|
||||
// SmallVector
|
||||
template <size_t F, typename T, unsigned N>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
Magpie::SmallVector<T, N>
|
||||
> {
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const Magpie::SmallVectorImpl<T>& vector) noexcept {
|
||||
return concepts::array::save<F>(ar, vector);
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, Magpie::SmallVectorImpl<T>& vector) noexcept {
|
||||
return concepts::array::load<F>(ar, vector);
|
||||
}
|
||||
};
|
||||
|
||||
// 避免依赖 D3DCompiler_47.dll
|
||||
struct SimpleBlob : winrt::implements<SimpleBlob, ID3DBlob> {
|
||||
public:
|
||||
SimpleBlob(uint32_t size) : _buffer(size), _size(size) {}
|
||||
|
||||
void* STDMETHODCALLTYPE GetBufferPointer() noexcept override {
|
||||
return _buffer.Data();
|
||||
}
|
||||
|
||||
size_t STDMETHODCALLTYPE GetBufferSize() noexcept override {
|
||||
return _size;
|
||||
}
|
||||
|
||||
private:
|
||||
Magpie::ByteBuffer _buffer;
|
||||
uint32_t _size;
|
||||
};
|
||||
|
||||
// winrt::com_ptr<ID3DBlob>
|
||||
template <std::size_t F>
|
||||
struct serializer<
|
||||
type_prop::not_a_fundamental,
|
||||
ser_case::use_internal_serializer,
|
||||
F,
|
||||
winrt::com_ptr<ID3DBlob>
|
||||
> {
|
||||
template <typename Archive>
|
||||
static Archive& save(Archive& ar, const winrt::com_ptr<ID3DBlob>& blob) noexcept {
|
||||
uint32_t size = (uint32_t)blob->GetBufferSize();
|
||||
ar& size;
|
||||
|
||||
ar.write(blob->GetBufferPointer(), size);
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
template <typename Archive>
|
||||
static Archive& load(Archive& ar, winrt::com_ptr<ID3DBlob>& blob) noexcept {
|
||||
uint32_t size = 0;
|
||||
ar& size;
|
||||
|
||||
blob = winrt::make<SimpleBlob>(size);
|
||||
ar.read(blob->GetBufferPointer(), size);
|
||||
|
||||
return ar;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#ifdef _M_ARM64
|
||||
#undef _LITTLE_ENDIAN
|
||||
#endif
|
||||
|
|
@ -31,7 +31,8 @@ public:
|
|||
bool isNative16BitSupported,
|
||||
bool isAdvancedColorSupported,
|
||||
bool saveSources,
|
||||
bool warningsAreErrors
|
||||
bool warningsAreErrors,
|
||||
bool disableCache
|
||||
) noexcept;
|
||||
|
||||
bool GetTaskResult(const std::string& taskKey, const ShaderEffectDrawInfo** drawInfo) noexcept;
|
||||
|
|
@ -51,7 +52,8 @@ private:
|
|||
std::string cacheKey,
|
||||
uint32_t parserFlags,
|
||||
bool saveSources,
|
||||
bool warningsAreErrors
|
||||
bool warningsAreErrors,
|
||||
bool disableCache
|
||||
) noexcept;
|
||||
|
||||
std::vector<EffectInfo> _effects;
|
||||
|
|
@ -59,13 +61,21 @@ private:
|
|||
|
||||
struct _ShaderEffectMemCacheItem {
|
||||
ShaderEffectDrawInfo drawInfo;
|
||||
// UINT_MAX 表示正在使用中
|
||||
uint32_t lastAccess = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t lastAccess;
|
||||
// 使用计数,归零才能删除
|
||||
uint32_t refCount;
|
||||
};
|
||||
// 需确保 drawInfo 地址稳定
|
||||
phmap::node_hash_map<std::string, _ShaderEffectMemCacheItem> _shaderEffectCache;
|
||||
wil::srwlock _shaderEffectCacheLock;
|
||||
uint32_t _nextLastAccess = 0;
|
||||
|
||||
// 用于后台线程检查 Uninitialize 是否已被调用
|
||||
struct _StopSource {
|
||||
wil::srwlock lock;
|
||||
bool isUninitialized = false;
|
||||
};
|
||||
std::shared_ptr<_StopSource> _stopSource = std::make_shared<_StopSource>();
|
||||
|
||||
std::atomic<bool> _initialized = false;
|
||||
bool _initializedCache = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue