Merge pull request #10834 from hrydgard/vulkan-shader-cache-renderpasses

Include renderpass definition in Vulkan shader cache entries, should make it more effective again.
This commit is contained in:
Henrik Rydgård 2018-03-29 15:00:34 +02:00 committed by GitHub
commit c2d5ce6c3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 101 additions and 50 deletions

View file

@ -131,7 +131,7 @@ void GPU_Vulkan::LoadCache(std::string filename) {
VkRenderPass renderPass = g_Config.iRenderingMode == FB_BUFFERED_MODE ?
(VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::FRAMEBUFFER_RENDERPASS) :
(VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS);
result = pipelineManager_->LoadCache(f, false, shaderManagerVulkan_, &drawEngine_, drawEngine_.GetPipelineLayout(), renderPass);
result = pipelineManager_->LoadCache(f, false, shaderManagerVulkan_, draw_, drawEngine_.GetPipelineLayout(), renderPass);
}
fclose(f);
if (!result) {
@ -148,7 +148,7 @@ void GPU_Vulkan::SaveCache(std::string filename) {
if (!f)
return;
shaderManagerVulkan_->SaveCache(f);
pipelineManager_->SaveCache(f, false, shaderManagerVulkan_);
pipelineManager_->SaveCache(f, false, shaderManagerVulkan_, draw_);
INFO_LOG(G3D, "Saved Vulkan pipeline cache");
fclose(f);
}

View file

@ -11,6 +11,9 @@
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
#include "GPU/Common/DrawEngineCommon.h"
#include "ext/native/thin3d/thin3d.h"
#include "ext/native/thin3d/VulkanRenderManager.h"
#include "ext/native/thin3d/VulkanQueueRunner.h"
PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : vulkan_(vulkan), pipelines_(256) {
// The pipeline cache is created on demand (or explicitly through Load).
@ -540,7 +543,25 @@ struct VkPipelineCacheHeader {
uint8_t uuid[VK_UUID_SIZE];
};
void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager) {
struct StoredVulkanPipelineKey {
VulkanPipelineRasterStateKey raster;
VShaderID vShaderID;
FShaderID fShaderID;
uint32_t vtxFmtId;
bool useHWTransform;
bool backbufferPass;
VulkanQueueRunner::RPKey renderPassKey;
// For std::set. Better zero-initialize the struct properly for this to work.
bool operator < (const StoredVulkanPipelineKey &other) const {
return memcmp(this, &other, sizeof(*this)) < 0;
}
};
void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext) {
VulkanRenderManager *rm = (VulkanRenderManager *)drawContext->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
VulkanQueueRunner *queueRunner = rm->GetQueueRunner();
size_t dataSize = 0;
uint32_t size;
@ -569,6 +590,7 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha
// Make sure the set of pipelines we write is "unique".
std::set<StoredVulkanPipelineKey> keys;
// TODO: Use derivative pipelines when possible, helps Mali driver pipeline creation speed at least.
pipelines_.Iterate([&](const VulkanPipelineKey &pkey, VulkanPipeline *value) {
if (failed)
return;
@ -587,6 +609,14 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha
// NOTE: This is not a vtype, but a decoded vertex format.
key.vtxFmtId = pkey.vtxFmtId;
}
// Figure out what kind of renderpass this pipeline uses.
if (pkey.renderPass == queueRunner->GetBackbufferRenderPass()) {
key.backbufferPass = true;
key.renderPassKey = {};
} else {
key.backbufferPass = false;
queueRunner->GetRenderPassKey(pkey.renderPass, &key.renderPassKey);
}
keys.insert(key);
});
@ -610,7 +640,10 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha
NOTICE_LOG(G3D, "Saved Vulkan pipeline ID cache (%d unique pipelines/%d).", (int)keys.size(), (int)pipelines_.size());
}
bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, DrawEngineCommon *drawEngine, VkPipelineLayout layout, VkRenderPass renderPass) {
bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext, VkPipelineLayout layout, VkRenderPass renderPass) {
VulkanRenderManager *rm = (VulkanRenderManager *)drawContext->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
VulkanQueueRunner *queueRunner = rm->GetQueueRunner();
uint32_t size = 0;
if (loadRawPipelineCache) {
fread(&size, sizeof(size), 1, file);
@ -673,6 +706,14 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha
ERROR_LOG(G3D, "Failed to find vs or fs in of pipeline %d in cache", (int)i);
continue;
}
VkRenderPass rp;
if (key.backbufferPass) {
rp = queueRunner->GetBackbufferRenderPass();
} else {
rp = queueRunner->GetRenderPass(key.renderPassKey);
}
DecVtxFormat fmt;
fmt.InitializeFromID(key.vtxFmtId);
GetOrCreatePipeline(layout, renderPass, key.raster,

View file

@ -56,19 +56,6 @@ struct VulkanPipelineKey {
std::string GetDescription(DebugShaderStringType stringType) const;
};
struct StoredVulkanPipelineKey {
VulkanPipelineRasterStateKey raster;
VShaderID vShaderID;
FShaderID fShaderID;
uint32_t vtxFmtId;
bool useHWTransform;
// For std::set. Better zero-initialize the struct properly for this to work.
bool operator < (const StoredVulkanPipelineKey &other) const {
return memcmp(this, &other, sizeof(*this)) < 0;
}
};
enum PipelineFlags {
PIPELINE_FLAG_USES_LINES = (1 << 2),
PIPELINE_FLAG_USES_BLEND_CONSTANT = (1 << 3),
@ -109,8 +96,8 @@ public:
std::vector<std::string> DebugGetObjectIDs(DebugShaderType type);
// Saves data for faster creation next time.
void SaveCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager);
bool LoadCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, DrawEngineCommon *drawEngine, VkPipelineLayout layout, VkRenderPass renderPass);
void SaveCache(FILE *file, bool saveRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext);
bool LoadCache(FILE *file, bool loadRawPipelineCache, ShaderManagerVulkan *shaderManager, Draw::DrawContext *drawContext, VkPipelineLayout layout, VkRenderPass renderPass);
private:
DenseHashMap<VulkanPipelineKey, VulkanPipeline *, nullptr> pipelines_;

View file

@ -350,7 +350,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM
// instantaneous.
#define CACHE_HEADER_MAGIC 0xff51f420
#define CACHE_VERSION 6
#define CACHE_VERSION 9
struct VulkanCacheHeader {
uint32_t magic;
uint32_t version;

View file

@ -506,6 +506,8 @@ handleELF:
if (File::Exists(screenshotPath)) {
if (readFileToString(false, screenshotPath.c_str(), info_->icon.data)) {
info_->icon.dataLoaded = true;
} else {
ERROR_LOG(G3D, "Error loading screenshot data: '%s'", screenshotPath.c_str());
}
}
break;
@ -795,6 +797,8 @@ void GameInfoCache::SetupTexture(std::shared_ptr<GameInfo> &info, Draw::DrawCont
tex.texture = CreateTextureFromFileData(thin3d, (const uint8_t *)tex.data.data(), (int)tex.data.size(), ImageFileType::DETECT);
if (tex.texture) {
tex.timeLoaded = time_now_d();
} else {
ERROR_LOG(G3D, "Failed creating texture");
}
}
if ((info->wantFlags & GAMEINFO_WANTBGDATA) == 0) {

View file

@ -312,9 +312,7 @@ void SavedataBrowser::Refresh() {
if (!isState && File::Exists(path_ + fileInfo[i].name + "/PARAM.SFO"))
isSaveData = true;
if (isSaveData) {
savedataButtons.push_back(new SavedataButton(fileInfo[i].fullName, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT)));
} else if (isState) {
if (isSaveData || isState) {
savedataButtons.push_back(new SavedataButton(fileInfo[i].fullName, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT)));
}
}

View file

@ -153,8 +153,7 @@ void VulkanQueueRunner::InitBackbufferRenderPass() {
assert(res == VK_SUCCESS);
}
VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadAction, VKRRenderPassAction depthLoadAction, VKRRenderPassAction stencilLoadAction, VkImageLayout prevColorLayout, VkImageLayout prevDepthLayout, VkImageLayout finalColorLayout) {
RPKey key{ colorLoadAction, depthLoadAction, stencilLoadAction, prevColorLayout, prevDepthLayout, finalColorLayout };
VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) {
auto pass = renderPasses_.Get(key);
if (pass) {
return pass;
@ -163,7 +162,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio
VkAttachmentDescription attachments[2] = {};
attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
switch (colorLoadAction) {
switch (key.colorLoadAction) {
case VKRRenderPassAction::CLEAR:
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
break;
@ -182,14 +181,14 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio
attachments[0].initialLayout = VK_IMAGE_LAYOUT_GENERAL;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
#else
attachments[0].initialLayout = prevColorLayout;
attachments[0].finalLayout = finalColorLayout;
attachments[0].initialLayout = key.prevColorLayout;
attachments[0].finalLayout = key.finalColorLayout;
#endif
attachments[0].flags = 0;
attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
switch (depthLoadAction) {
switch (key.depthLoadAction) {
case VKRRenderPassAction::CLEAR:
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
break;
@ -200,7 +199,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
break;
}
switch (stencilLoadAction) {
switch (key.stencilLoadAction) {
case VKRRenderPassAction::CLEAR:
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
break;
@ -217,7 +216,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio
attachments[1].initialLayout = VK_IMAGE_LAYOUT_GENERAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
#else
attachments[1].initialLayout = prevDepthLayout;
attachments[1].initialLayout = key.prevDepthLayout;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
#endif
attachments[1].flags = 0;
@ -244,7 +243,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio
VkSubpassDependency deps[2]{};
int numDeps = 0;
switch (prevColorLayout) {
switch (key.prevColorLayout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
// Already the right color layout. Unclear that we need to do a lot here..
break;
@ -266,11 +265,11 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio
deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
default:
_dbg_assert_msg_(G3D, false, "GetRenderPass: Unexpected color layout %d", (int)prevColorLayout);
_dbg_assert_msg_(G3D, false, "GetRenderPass: Unexpected color layout %d", (int)key.prevColorLayout);
break;
}
switch (prevDepthLayout) {
switch (key.prevDepthLayout) {
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
// Already the right depth layout. Unclear that we need to do a lot here..
break;
@ -287,7 +286,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio
deps[numDeps].srcStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
default:
_dbg_assert_msg_(G3D, false, "PerformBindRT: Unexpected depth layout %d", (int)prevDepthLayout);
_dbg_assert_msg_(G3D, false, "PerformBindRT: Unexpected depth layout %d", (int)key.prevDepthLayout);
break;
}
@ -302,7 +301,7 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(VKRRenderPassAction colorLoadActio
// And the final transition.
// Don't need to transition it if VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.
switch (finalColorLayout) {
switch (key.finalColorLayout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
deps[numDeps].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
deps[numDeps].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;

View file

@ -174,11 +174,38 @@ public:
void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
private:
// Only call this from the render thread!
VkRenderPass GetRenderPass(VKRRenderPassAction colorLoadAction, VKRRenderPassAction depthLoadAction, VKRRenderPassAction stencilLoadAction,
VkImageLayout prevColorLayout, VkImageLayout prevDepthLayout, VkImageLayout finalColorLayout);
struct RPKey {
VKRRenderPassAction colorLoadAction;
VKRRenderPassAction depthLoadAction;
VKRRenderPassAction stencilLoadAction;
VkImageLayout prevColorLayout;
VkImageLayout prevDepthLayout;
VkImageLayout finalColorLayout;
// TODO: Also pre-transition depth, for copies etc.
};
// Only call this from the render thread! Also ok during initialization (LoadCache).
VkRenderPass GetRenderPass(
VKRRenderPassAction colorLoadAction, VKRRenderPassAction depthLoadAction, VKRRenderPassAction stencilLoadAction,
VkImageLayout prevColorLayout, VkImageLayout prevDepthLayout, VkImageLayout finalColorLayout) {
RPKey key{ colorLoadAction, depthLoadAction, stencilLoadAction, prevColorLayout, prevDepthLayout, finalColorLayout };
return GetRenderPass(key);
}
VkRenderPass GetRenderPass(const RPKey &key);
bool GetRenderPassKey(VkRenderPass passToFind, RPKey *outKey) const {
bool found = false;
renderPasses_.Iterate([passToFind, &found, outKey](const RPKey &rpkey, VkRenderPass pass) {
if (pass == passToFind) {
found = true;
*outKey = rpkey;
}
});
return found;
}
private:
void InitBackbufferRenderPass();
void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd);
@ -208,16 +235,6 @@ private:
VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE;
VkRenderPass framebufferRenderPass_ = VK_NULL_HANDLE;
struct RPKey {
VKRRenderPassAction colorAction;
VKRRenderPassAction depthAction;
VKRRenderPassAction stencilAction;
VkImageLayout prevColorLayout;
VkImageLayout prevDepthLayout;
VkImageLayout finalColorLayout;
// TODO: Also pre-transition depth, for copies etc.
};
// Renderpasses, all combinations of preserving or clearing or dont-care-ing fb contents.
// TODO: Create these on demand.
DenseHashMap<RPKey, VkRenderPass, (VkRenderPass)VK_NULL_HANDLE> renderPasses_;

View file

@ -234,6 +234,11 @@ public:
return vulkan_;
}
// Be careful with this. Only meant to be used for fetching render passes for shader cache initialization.
VulkanQueueRunner *GetQueueRunner() {
return &queueRunner_;
}
private:
bool InitBackbufferFramebuffers(int width, int height);
bool InitDepthStencilBuffer(VkCommandBuffer cmd); // Used for non-buffered rendering.