Permalink
Browse files

Implement pipeline/shader cache for Vulkan, to avoid shader compile s…

…tutters on second and subsequent runs.

The raw pipeline cache got pretty large. Instead, store IDs like GL.

There's still a disabled option to store the pipeline cache objects.
  • Loading branch information...
hrydgard committed Mar 13, 2018
1 parent 2d33d52 commit 614cabb115e61f758fb16f732e162283a34c3932
@@ -937,17 +937,6 @@ void VulkanContext::DestroyDevice() {
device_ = nullptr;
}
VkPipelineCache VulkanContext::CreatePipelineCache() {
VkPipelineCache cache;
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
pc.pInitialData = nullptr;
pc.initialDataSize = 0;
pc.flags = 0;
VkResult res = vkCreatePipelineCache(device_, &pc, nullptr, &cache);
assert(VK_SUCCESS == res);
return cache;
}
bool VulkanContext::CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule) {
VkShaderModuleCreateInfo sm{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
sm.pCode = spirv.data();
@@ -142,8 +142,6 @@ class VulkanContext {
VulkanDeleteList &Delete() { return globalDeleteList_; }
VkPipelineCache CreatePipelineCache();
// The parameters are whatever the chosen window system wants.
void InitSurface(WindowSystem winsys, void *data1, void *data2, int width = -1, int height = -1);
void ReinitSurface(int width = -1, int height = -1);
@@ -88,6 +88,8 @@ class DrawEngineCommon {
return numDrawCalls;
}
VertexDecoder *GetVertexDecoder(u32 vtype);
protected:
virtual void ClearTrackedVertexArrays() {}
@@ -106,8 +108,6 @@ class DrawEngineCommon {
bool ApplyShaderBlending();
VertexDecoder *GetVertexDecoder(u32 vtype);
inline int IndexSize(u32 vtype) const {
const u32 indexType = (vtype & GE_VTYPE_IDX_MASK);
if (indexType == GE_VTYPE_IDX_16BIT) {
@@ -75,6 +75,21 @@ void DecVtxFormat::ComputeID() {
id = uvfmt | (c0fmt << 4) | (c1fmt << 8) | (nrmfmt << 12) | (posfmt << 16);
}
void DecVtxFormat::InitializeFromID(uint32_t id) {
this->id = id;
uvfmt = (id & 0xF);
c0fmt = ((id >> 4) & 0xF);
c1fmt = ((id >> 8) & 0xF);
nrmfmt = ((id >> 12) & 0xF);
posfmt = ((id >> 16) & 0xF);
uvoff = 0;
c0off = uvoff + DecFmtSize(uvfmt);
c1off = c0off + DecFmtSize(c0fmt);
nrmoff = c1off + DecFmtSize(c1fmt);
posoff = nrmoff + DecFmtSize(nrmfmt);
stride = posoff + DecFmtSize(posfmt);
}
void GetIndexBounds(const void *inds, int count, u32 vertType, u16 *indexLowerBound, u16 *indexUpperBound) {
// Find index bounds. Could cache this in display lists.
// Also, this could be greatly sped up with SSE2/NEON, although rarely a bottleneck.
@@ -72,10 +72,11 @@ struct DecVtxFormat {
u8 c1fmt; u8 c1off;
u8 nrmfmt; u8 nrmoff;
u8 posfmt; u8 posoff;
short stride;
u8 stride;
uint32_t id;
void ComputeID();
void InitializeFromID(uint32_t id);
};
void GetIndexBounds(const void *inds, int count, u32 vertType, u16 *indexLowerBound, u16 *indexUpperBound);
@@ -109,7 +109,7 @@ void DrawEngineGLES::DeviceRestore() {
void DrawEngineGLES::InitDeviceObjects() {
for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) {
frameData_[i].pushVertex = new GLPushBuffer(render_, GL_ARRAY_BUFFER, 1024 * 1024);
frameData_[i].pushVertex = new GLPushBuffer(render_, GL_ARRAY_BUFFER, 256 * 1024);
frameData_[i].pushIndex = new GLPushBuffer(render_, GL_ELEMENT_ARRAY_BUFFER, 256 * 1024);
render_->RegisterPushBuffer(i, frameData_[i].pushVertex);
@@ -419,7 +419,7 @@ void GPU_GLES::BeginFrame() {
GPUCommon::BeginFrame();
// Save the cache from time to time. TODO: How often?
// Save the cache from time to time. TODO: How often? We save on exit, so shouldn't need to do this all that often.
if (!shaderCachePath_.empty() && (gpuStats.numFlips & 4095) == 0) {
shaderManagerGL_->Save(shaderCachePath_);
}
@@ -255,9 +255,7 @@ void DrawEngineVulkan::BeginFrame() {
// TODO: How can we make this nicer...
((TessellationDataTransferVulkan *)tessDataTransfer)->SetPushBuffer(frame->pushUBO);
// TODO : Find a better place to do this.
if (!nullTexture_) {
ILOG("INIT : Creating null texture");
VkCommandBuffer cmdInit = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
nullTexture_ = new VulkanTexture(vulkan_, textureCache_->GetAllocator());
int w = 8;
@@ -29,6 +29,7 @@
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "Core/System.h"
#include "Core/ELF/ParamSFO.h"
#include "GPU/GPUState.h"
#include "GPU/ge_constants.h"
@@ -94,9 +95,57 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
if (vulkan_->GetFeaturesEnabled().wideLines) {
drawEngine_.SetLineWidth(PSP_CoreParameter().renderWidth / 480.0f);
}
// Load shader cache.
std::string discID = g_paramSFO.GetDiscID();
if (discID.size()) {
File::CreateFullPath(GetSysDirectory(DIRECTORY_APP_CACHE));
shaderCachePath_ = GetSysDirectory(DIRECTORY_APP_CACHE) + "/" + discID + ".vkshadercache";
LoadCache(shaderCachePath_);
}
}
void GPU_Vulkan::LoadCache(std::string filename) {
PSP_SetLoading("Loading shader cache...");
// Actually precompiled by IsReady() since we're single-threaded.
FILE *f = File::OpenCFile(filename, "rb");
if (!f)
return;
// First compile shaders to SPIR-V, then load the pipeline cache and recreate the pipelines.
// It's when recreating the pipelines that the pipeline cache is useful - in the ideal case,
// it can just memcpy the finished shader binaries out of the pipeline cache file.
bool result = shaderManagerVulkan_->LoadCache(f);
if (result) {
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);
}
fclose(f);
if (!result) {
WARN_LOG(G3D, "Bad Vulkan pipeline cache");
// Bad cache file for this GPU/Driver/etc. Delete it.
File::Delete(filename);
} else {
INFO_LOG(G3D, "Loaded Vulkan pipeline cache.");
}
}
void GPU_Vulkan::SaveCache(std::string filename) {
FILE *f = File::OpenCFile(filename, "wb");
if (!f)
return;
shaderManagerVulkan_->SaveCache(f);
pipelineManager_->SaveCache(f, false, shaderManagerVulkan_);
INFO_LOG(G3D, "Saved Vulkan pipeline cache");
fclose(f);
}
GPU_Vulkan::~GPU_Vulkan() {
SaveCache(shaderCachePath_);
// Note: We save the cache in DeviceLost
DestroyDeviceObjects();
framebufferManagerVulkan_->DestroyAllFBOs();
vulkan2D_.Shutdown();
@@ -407,6 +456,9 @@ void GPU_Vulkan::DestroyDeviceObjects() {
}
void GPU_Vulkan::DeviceLost() {
if (!shaderCachePath_.empty()) {
SaveCache(shaderCachePath_);
}
DestroyDeviceObjects();
framebufferManagerVulkan_->DeviceLost();
vulkan2D_.DeviceLost();
@@ -80,6 +80,9 @@ class GPU_Vulkan : public GPUCommon {
void InitDeviceObjects();
void DestroyDeviceObjects();
void LoadCache(std::string filename);
void SaveCache(std::string filename);
VulkanContext *vulkan_;
FramebufferManagerVulkan *framebufferManagerVulkan_;
TextureCacheVulkan *textureCacheVulkan_;
@@ -100,4 +103,6 @@ class GPU_Vulkan : public GPUCommon {
};
FrameData frameData_[VulkanContext::MAX_INFLIGHT_FRAMES]{};
std::string shaderCachePath_;
};
Oops, something went wrong.

0 comments on commit 614cabb

Please sign in to comment.