mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-30 19:24:23 +01:00
Fix shader caching.
This commit is contained in:
1 parent
7ab7440475
commit
befcfb470c
8 files changed
+91
-43
No files matched your search
@@ -693,6 +693,48 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() {
|
||||
return frameData_[curFrame].initCmd;
|
||||
}
|
||||
|
||||
VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, uint32_t variantBitmask) {
|
||||
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline();
|
||||
_dbg_assert_(desc->vertexShader);
|
||||
_dbg_assert_(desc->fragmentShader);
|
||||
pipeline->desc = desc;
|
||||
if (curRenderStep_) {
|
||||
// The common case
|
||||
pipelinesToCheck_.push_back(pipeline);
|
||||
} else {
|
||||
if (!variantBitmask) {
|
||||
WARN_LOG(G3D, "WARNING: Will not compile any variants of pipeline, not in renderpass and empty variantBitmask");
|
||||
}
|
||||
// Presumably we're in initialization, loading the shader cache.
|
||||
// Look at variantBitmask to see what variants we should queue up.
|
||||
RPKey key{
|
||||
VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR,
|
||||
VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE,
|
||||
};
|
||||
VKRRenderPass *compatibleRenderPass = queueRunner_.GetRenderPass(key);
|
||||
compileMutex_.lock();
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
if (!(variantBitmask & (1 << i)))
|
||||
continue;
|
||||
RenderPassType rpType = (RenderPassType)i;
|
||||
pipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline, compatibleRenderPass->Get(vulkan_, rpType), rpType));
|
||||
}
|
||||
compileMutex_.unlock();
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
VKRComputePipeline *VulkanRenderManager::CreateComputePipeline(VKRComputePipelineDesc *desc) {
|
||||
VKRComputePipeline *pipeline = new VKRComputePipeline();
|
||||
pipeline->desc = desc;
|
||||
compileMutex_.lock();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline));
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void VulkanRenderManager::EndCurRenderStep() {
|
||||
if (!curRenderStep_)
|
||||
return;
|
||||
@@ -713,13 +755,15 @@ void VulkanRenderManager::EndCurRenderStep() {
|
||||
// Save the accumulated pipeline flags so we can use that to configure the render pass.
|
||||
// We'll often be able to avoid loading/saving the depth/stencil buffer.
|
||||
compileMutex_.lock();
|
||||
for (VKRGraphicsPipeline *pipeline : pipelinesToCreate_) {
|
||||
pipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType), rpType));
|
||||
for (VKRGraphicsPipeline *pipeline : pipelinesToCheck_) {
|
||||
if (!pipeline->pipeline[rpType]) {
|
||||
pipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType), rpType));
|
||||
}
|
||||
}
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
pipelinesToCreate_.clear();
|
||||
pipelinesToCheck_.clear();
|
||||
|
||||
// We don't do this optimization for very small targets, probably not worth it.
|
||||
if (!curRenderArea_.Empty() && (curWidth_ > 32 && curHeight_ > 32)) {
|
||||
@@ -959,7 +1003,6 @@ bool VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) {
|
||||
// We share the same depth buffer but have multiple color buffers, see the loop below.
|
||||
VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view };
|
||||
|
||||
VLOG("InitFramebuffers: %dx%d", width, height);
|
||||
VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
fb_info.renderPass = queueRunner_.GetCompatibleRenderPass()->Get(vulkan_, RP_TYPE_BACKBUFFER);
|
||||
fb_info.attachmentCount = 2;
|
||||
|
||||
@@ -166,6 +166,16 @@ struct VKRGraphicsPipeline {
|
||||
delete desc;
|
||||
}
|
||||
|
||||
u32 GetVariantsBitmask() const {
|
||||
u32 bitmask = 0;
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
if (pipeline[i]) {
|
||||
bitmask |= 1 << i;
|
||||
}
|
||||
}
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
VKRGraphicsPipelineDesc *desc = nullptr;
|
||||
Promise<VkPipeline> *pipeline[RP_TYPE_COUNT];
|
||||
|
||||
@@ -248,30 +258,22 @@ public:
|
||||
// Deferred creation, like in GL. Unlike GL though, the purpose is to allow background creation and avoiding
|
||||
// stalling the emulation thread as much as possible.
|
||||
// We delay creating pipelines until the end of the current render pass, so we can create the right type immediately.
|
||||
// WARNING: desc must stick around! It's not enough to build it on the stack.
|
||||
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc) {
|
||||
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline();
|
||||
_dbg_assert_(desc->vertexShader);
|
||||
_dbg_assert_(desc->fragmentShader);
|
||||
pipeline->desc = desc;
|
||||
pipelinesToCreate_.push_back(pipeline);
|
||||
return pipeline;
|
||||
}
|
||||
// Unless a variantBitmask is passed in, in which case we can just go ahead.
|
||||
// WARNING: desc must stick around during the lifetime of the pipeline! It's not enough to build it on the stack and drop it.
|
||||
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, uint32_t variantBitmask);
|
||||
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc);
|
||||
|
||||
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc) {
|
||||
VKRComputePipeline *pipeline = new VKRComputePipeline();
|
||||
pipeline->desc = desc;
|
||||
void NudgeCompilerThread() {
|
||||
compileMutex_.lock();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline));
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(pipeline != nullptr);
|
||||
VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE };
|
||||
pipelinesToCheck_.push_back(pipeline);
|
||||
data.graphics_pipeline.pipeline = pipeline;
|
||||
data.graphics_pipeline.pipelineLayout = pipelineLayout;
|
||||
curPipelineFlags_ |= flags;
|
||||
@@ -577,8 +579,8 @@ private:
|
||||
std::mutex compileMutex_;
|
||||
std::vector<CompileQueueEntry> compileQueue_;
|
||||
|
||||
// pipelines to create at the end of the current render pass.
|
||||
std::vector<VKRGraphicsPipeline *> pipelinesToCreate_;
|
||||
// pipelines to check and possibly create at the end of the current render pass.
|
||||
std::vector<VKRGraphicsPipeline *> pipelinesToCheck_;
|
||||
|
||||
// Swap chain management
|
||||
struct SwapchainImageData {
|
||||
|
||||
@@ -1110,7 +1110,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
raster->ToVulkan(&gDesc.rs);
|
||||
|
||||
pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc);;
|
||||
pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, 1 << RP_TYPE_BACKBUFFER);
|
||||
|
||||
if (desc.uniformDesc) {
|
||||
pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize;
|
||||
|
||||
@@ -1866,10 +1866,9 @@ static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferForma
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat));
|
||||
return false;
|
||||
}
|
||||
WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat));
|
||||
return false;
|
||||
} else if (texFormat == GE_TFMT_5650 && bufferFormat == GE_FORMAT_DEPTH16) {
|
||||
// We can also "depal" 565 format, this is used to read depth buffers as 565 on occasion (#15491).
|
||||
return true;
|
||||
|
||||
@@ -768,7 +768,7 @@ void DrawEngineVulkan::DoFlush() {
|
||||
}
|
||||
_dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader");
|
||||
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, true);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, true, 0);
|
||||
if (!pipeline || !pipeline->pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
return;
|
||||
@@ -893,7 +893,7 @@ void DrawEngineVulkan::DoFlush() {
|
||||
}
|
||||
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform
|
||||
_dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader");
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, false);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, false, 0);
|
||||
if (!pipeline || !pipeline->pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
decodedVerts_ = 0;
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
|
||||
using namespace PPSSPP_VK;
|
||||
|
||||
u32 VulkanPipeline::GetVariantsBitmask() const {
|
||||
return pipeline->GetVariantsBitmask();
|
||||
}
|
||||
|
||||
PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : pipelines_(256), vulkan_(vulkan) {
|
||||
// The pipeline cache is created on demand (or explicitly through Load).
|
||||
}
|
||||
@@ -30,10 +34,6 @@ PipelineManagerVulkan::~PipelineManagerVulkan() {
|
||||
}
|
||||
|
||||
void PipelineManagerVulkan::Clear() {
|
||||
// This should kill off all the shaders at once.
|
||||
// This could also be an opportunity to store the whole cache to disk. Will need to also
|
||||
// store the keys.
|
||||
|
||||
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
if (value->pipeline) {
|
||||
@@ -180,7 +180,7 @@ static std::string CutFromMain(std::string str) {
|
||||
|
||||
static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache,
|
||||
VkPipelineLayout layout, const VulkanPipelineRasterStateKey &key,
|
||||
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
|
||||
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) {
|
||||
VKRGraphicsPipelineDesc *desc = new VKRGraphicsPipelineDesc();
|
||||
desc->pipelineCache = pipelineCache;
|
||||
|
||||
@@ -304,7 +304,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
|
||||
|
||||
desc->pipelineLayout = layout;
|
||||
|
||||
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc);
|
||||
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, variantBitmask);
|
||||
|
||||
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
|
||||
vulkanPipeline->pipeline = pipeline;
|
||||
@@ -319,7 +319,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
|
||||
return vulkanPipeline;
|
||||
}
|
||||
|
||||
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
|
||||
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) {
|
||||
if (!pipelineCache_) {
|
||||
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
||||
VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
|
||||
@@ -340,7 +340,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *
|
||||
|
||||
VulkanPipeline *pipeline = CreateVulkanPipeline(
|
||||
renderManager, pipelineCache_, layout,
|
||||
rasterKey, decFmt, vs, fs, useHwTransform);
|
||||
rasterKey, decFmt, vs, fs, useHwTransform, variantBitmask);
|
||||
pipelines_.Insert(key, pipeline);
|
||||
|
||||
// Don't return placeholder null pipelines.
|
||||
@@ -546,7 +546,8 @@ struct StoredVulkanPipelineKey {
|
||||
VShaderID vShaderID;
|
||||
FShaderID fShaderID;
|
||||
uint32_t vtxFmtId;
|
||||
bool useHWTransform;
|
||||
uint32_t variants;
|
||||
bool useHWTransform; // TODO: Still needed?
|
||||
|
||||
// For std::set. Better zero-initialize the struct properly for this to work.
|
||||
bool operator < (const StoredVulkanPipelineKey &other) const {
|
||||
@@ -603,6 +604,7 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha
|
||||
key.useHWTransform = pkey.useHWTransform;
|
||||
key.fShaderID = fshader->GetID();
|
||||
key.vShaderID = vshader->GetID();
|
||||
key.variants = value->GetVariantsBitmask();
|
||||
if (key.useHWTransform) {
|
||||
// NOTE: This is not a vtype, but a decoded vertex format.
|
||||
key.vtxFmtId = pkey.vtxFmtId;
|
||||
@@ -714,11 +716,14 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha
|
||||
|
||||
DecVtxFormat fmt;
|
||||
fmt.InitializeFromID(key.vtxFmtId);
|
||||
VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, key.useHWTransform);
|
||||
VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, key.useHWTransform, key.variants);
|
||||
if (!pipeline) {
|
||||
pipelineCreateFailCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
rm->NudgeCompilerThread();
|
||||
|
||||
NOTICE_LOG(G3D, "Recreated Vulkan pipeline cache (%d pipelines, %d failed).", (int)size, pipelineCreateFailCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ struct VulkanPipeline {
|
||||
bool UsesBlendConstant() const { return (flags & PIPELINE_FLAG_USES_BLEND_CONSTANT) != 0; }
|
||||
bool UsesLines() const { return (flags & PIPELINE_FLAG_USES_LINES) != 0; }
|
||||
bool UsesDepthStencil() const { return (flags & PIPELINE_FLAG_USES_DEPTH_STENCIL) != 0; }
|
||||
|
||||
u32 GetVariantsBitmask() const;
|
||||
};
|
||||
|
||||
class VulkanContext;
|
||||
@@ -71,7 +73,8 @@ public:
|
||||
PipelineManagerVulkan(VulkanContext *ctx);
|
||||
~PipelineManagerVulkan();
|
||||
|
||||
VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform);
|
||||
// variantMask is only used when loading pipelines from cache.
|
||||
VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantMask);
|
||||
int GetNumPipelines() const { return (int)pipelines_.size(); }
|
||||
|
||||
void Clear();
|
||||
|
||||
@@ -371,7 +371,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM
|
||||
// instantaneous.
|
||||
|
||||
#define CACHE_HEADER_MAGIC 0xff51f420
|
||||
#define CACHE_VERSION 24
|
||||
#define CACHE_VERSION 25
|
||||
struct VulkanCacheHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
@@ -391,10 +391,6 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) {
|
||||
if (header.featureFlags != gstate_c.featureFlags)
|
||||
return false;
|
||||
|
||||
// Temporarily disable shader cache.
|
||||
if (true)
|
||||
return false;
|
||||
|
||||
VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT);
|
||||
for (int i = 0; i < header.numVertexShaders; i++) {
|
||||
VShaderID id;
|
||||
|
||||
Reference in new issue
Block a user