VulkanRenderManager: Add deferred pipeline creation (to get it off the CPU thread)

This commit is contained in:
Henrik Rydgård 2019-06-16 20:29:38 +02:00
parent be152acf7b
commit 62f4875e24
7 changed files with 206 additions and 59 deletions

@ -841,9 +841,11 @@ void VulkanQueueRunner::LogRenderPass(const VKRStep &pass, bool verbose) {
case VKRRenderCommand::REMOVED:
INFO_LOG(G3D, " (Removed)");
break;
case VKRRenderCommand::BIND_PIPELINE:
INFO_LOG(G3D, " BindPipeline(%x)", (int)(intptr_t)cmd.pipeline.pipeline);
case VKRRenderCommand::BIND_GRAPHICS_PIPELINE:
INFO_LOG(G3D, " BindGraphicsPipeline(%x)", (int)(intptr_t)cmd.graphics_pipeline.pipeline);
break;
case VKRRenderCommand::BIND_COMPUTE_PIPELINE:
INFO_LOG(G3D, " BindComputePipeline(%x)", (int)(intptr_t)cmd.compute_pipeline.pipeline);
break;
case VKRRenderCommand::BLEND:
INFO_LOG(G3D, " BlendColor(%08x)", cmd.blendColor.color);
@ -1236,6 +1238,9 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
VKRFramebuffer *fb = step.render.framebuffer;
VkPipeline lastGraphicsPipeline = VK_NULL_HANDLE;
VkPipeline lastComputePipeline = VK_NULL_HANDLE;
auto &commands = step.commands;
// We can do a little bit of state tracking here to eliminate some calls into the driver.
@ -1251,16 +1256,54 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
case VKRRenderCommand::REMOVED:
break;
// Still here to support binding of non-async pipelines.
case VKRRenderCommand::BIND_PIPELINE:
if (c.pipeline.pipeline != lastPipeline) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, c.pipeline.pipeline);
lastPipeline = c.pipeline.pipeline;
{
VkPipeline pipeline = c.pipeline.pipeline;
if (pipeline != lastGraphicsPipeline) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
lastGraphicsPipeline = pipeline;
// Reset dynamic state so it gets refreshed with the new pipeline.
lastStencilWriteMask = -1;
lastStencilCompareMask = -1;
lastStencilReference = -1;
}
break;
}
case VKRRenderCommand::BIND_GRAPHICS_PIPELINE:
{
VKRGraphicsPipeline *pipeline = c.graphics_pipeline.pipeline;
if (!pipeline->pipeline) {
// Late! Compile it.
if (!pipeline->Create(vulkan_))
break;
}
if (pipeline->pipeline != lastGraphicsPipeline) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->pipeline);
lastGraphicsPipeline = pipeline->pipeline;
// Reset dynamic state so it gets refreshed with the new pipeline.
lastStencilWriteMask = -1;
lastStencilCompareMask = -1;
lastStencilReference = -1;
}
break;
}
case VKRRenderCommand::BIND_COMPUTE_PIPELINE:
{
VKRComputePipeline *pipeline = c.compute_pipeline.pipeline;
if (!pipeline->pipeline) {
// Late! Compile it.
if (!pipeline->Create(vulkan_))
break;
}
if (pipeline->pipeline != lastComputePipeline) {
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->pipeline);
lastComputePipeline = pipeline->pipeline;
}
break;
}
case VKRRenderCommand::VIEWPORT:
if (fb != nullptr) {

@ -9,6 +9,8 @@
#include "Common/GPU/DataFormat.h"
class VKRFramebuffer;
struct VKRGraphicsPipeline;
struct VKRComputePipeline;
struct VKRImage;
enum {
@ -20,7 +22,9 @@ enum {
enum class VKRRenderCommand : uint8_t {
REMOVED,
BIND_PIPELINE,
BIND_PIPELINE, // raw pipeline
BIND_GRAPHICS_PIPELINE, // async
BIND_COMPUTE_PIPELINE, // async
STENCIL,
BLEND,
VIEWPORT,
@ -45,6 +49,12 @@ struct VkRenderData {
struct {
VkPipeline pipeline;
} pipeline;
struct {
VKRGraphicsPipeline *pipeline;
} graphics_pipeline;
struct {
VKRComputePipeline *pipeline;
} compute_pipeline;
struct {
VkPipelineLayout pipelineLayout;
VkDescriptorSet ds;

@ -22,6 +22,43 @@
using namespace PPSSPP_VK;
bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) {
if (!desc) {
// Already failed to create this one.
return false;
}
VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &pipeline);
delete desc;
desc = nullptr;
if (result == VK_INCOMPLETE) {
// Bad return value seen on Adreno in Burnout :( Try to ignore?
// Create a placeholder to avoid creating over and over if something is broken.
pipeline = VK_NULL_HANDLE;
return true;
} else if (result != VK_SUCCESS) {
pipeline = VK_NULL_HANDLE;
ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result));
return false;
} else {
return true;
}
}
bool VKRComputePipeline::Create(VulkanContext *vulkan) {
if (!desc) {
// Already failed to create this one.
return false;
}
VkResult result = vkCreateComputePipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &pipeline);
delete desc;
desc = nullptr;
if (result != VK_SUCCESS) {
pipeline = VK_NULL_HANDLE;
return false;
}
return true;
}
VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, int _width, int _height, const char *tag) : vulkan_(vk) {
width = _width;
height = _height;

@ -109,6 +109,46 @@ struct BoundingRect {
}
};
// All the data needed to create a graphics pipeline.
struct VKRGraphicsPipelineDesc {
VkPipelineCache pipelineCache = VK_NULL_HANDLE;
VkPipelineColorBlendStateCreateInfo cbs{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
VkPipelineColorBlendAttachmentState blend0{};
VkPipelineDepthStencilStateCreateInfo dss{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
VkDynamicState dynamicStates[6]{};
VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
VkPipelineShaderStageCreateInfo shaderStageInfo[2]{};
VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
VkVertexInputAttributeDescription attrs[8]{};
VkVertexInputBindingDescription ibd{};
VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
};
// All the data needed to create a compute pipeline.
struct VKRComputePipelineDesc {
VkPipelineCache pipelineCache;
VkComputePipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
};
// Wrapped pipeline, which will later allow for background compilation while emulating the rest of the frame.
struct VKRGraphicsPipeline {
VKRGraphicsPipelineDesc *desc = nullptr; // While non-zero, is pending and pipeline isn't valid.
VkPipeline pipeline = VK_NULL_HANDLE;
bool Create(VulkanContext *vulkan);
};
struct VKRComputePipeline {
VKRComputePipelineDesc *desc = nullptr;
VkPipeline pipeline = VK_NULL_HANDLE;
bool Create(VulkanContext *vulkan);
};
class VulkanRenderManager {
public:
VulkanRenderManager(VulkanContext *vulkan);
@ -152,6 +192,14 @@ public:
void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, VkImageAspectFlags aspectMask, const char *tag);
void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, VkImageAspectFlags aspectMask, VkFilter filter, const char *tag);
// 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.
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc) {
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline();
pipeline->desc = desc;
return pipeline;
}
void BindPipeline(VkPipeline pipeline, PipelineFlags flags) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != VK_NULL_HANDLE);
@ -161,6 +209,24 @@ public:
curRenderStep_->commands.push_back(data);
}
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != nullptr);
VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE };
data.graphics_pipeline.pipeline = pipeline;
curPipelineFlags_ |= flags;
curRenderStep_->commands.push_back(data);
}
void BindPipeline(VKRComputePipeline *pipeline, PipelineFlags flags) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_(pipeline != nullptr);
VkRenderData data{ VKRRenderCommand::BIND_COMPUTE_PIPELINE };
data.compute_pipeline.pipeline = pipeline;
curPipelineFlags_ |= flags;
curRenderStep_->commands.push_back(data);
}
void SetViewport(const VkViewport &vp) {
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
_dbg_assert_((int)vp.width >= 0);

@ -826,7 +826,7 @@ void DrawEngineVulkan::DoFlush() {
Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS;
VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, true);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, true);
if (!pipeline || !pipeline->pipeline) {
// Already logged, let's bail out.
return;
@ -957,7 +957,7 @@ void DrawEngineVulkan::DoFlush() {
}
Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS;
VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, false);
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, false);
if (!pipeline || !pipeline->pipeline) {
// Already logged, let's bail out.
return;

@ -34,8 +34,10 @@ void PipelineManagerVulkan::Clear() {
// store the keys.
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
if (value->pipeline)
vulkan_->Delete().QueueDeletePipeline(value->pipeline);
if (value->pipeline) {
vulkan_->Delete().QueueDeletePipeline(value->pipeline->pipeline);
delete value->pipeline;
}
delete value;
});
@ -163,13 +165,16 @@ static std::string CutFromMain(std::string str) {
return rebuilt;
}
static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pipelineCache,
static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache,
VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key,
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
VKRGraphicsPipelineDesc *desc = new VKRGraphicsPipelineDesc();
desc->pipelineCache = pipelineCache;
PROFILE_THIS_SCOPE("pipelinebuild");
bool useBlendConstant = false;
VkPipelineColorBlendAttachmentState blend0{};
VkPipelineColorBlendAttachmentState &blend0 = desc->blend0;
blend0.blendEnable = key.blendEnable;
if (key.blendEnable) {
blend0.colorBlendOp = (VkBlendOp)key.blendOpColor;
@ -181,7 +186,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
}
blend0.colorWriteMask = key.colorWriteMask;
VkPipelineColorBlendStateCreateInfo cbs{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
VkPipelineColorBlendStateCreateInfo &cbs = desc->cbs;
cbs.flags = 0;
cbs.pAttachments = &blend0;
cbs.attachmentCount = 1;
@ -191,7 +196,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
else
cbs.logicOp = VK_LOGIC_OP_COPY;
VkPipelineDepthStencilStateCreateInfo dss{ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
VkPipelineDepthStencilStateCreateInfo &dss = desc->dss;
dss.depthBoundsTestEnable = false;
dss.stencilTestEnable = key.stencilTestEnable;
if (key.stencilTestEnable) {
@ -208,7 +213,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
dss.depthWriteEnable = key.depthWriteEnable;
}
VkDynamicState dynamicStates[8]{};
VkDynamicState *dynamicStates = &desc->dynamicStates[0];
int numDyn = 0;
if (key.blendEnable &&
(UsesBlendConstant(key.srcAlpha) || UsesBlendConstant(key.srcColor) || UsesBlendConstant(key.destAlpha) || UsesBlendConstant(key.destColor))) {
@ -223,12 +228,12 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
dynamicStates[numDyn++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
}
VkPipelineDynamicStateCreateInfo ds{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
VkPipelineDynamicStateCreateInfo &ds = desc->ds;
ds.flags = 0;
ds.pDynamicStates = dynamicStates;
ds.dynamicStateCount = numDyn;
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
VkPipelineRasterizationStateCreateInfo &rs = desc->rs;
rs.flags = 0;
rs.depthBiasEnable = false;
rs.cullMode = key.cullMode;
@ -238,11 +243,11 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
rs.polygonMode = VK_POLYGON_MODE_FILL;
rs.depthClampEnable = key.depthClampEnable;
VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
VkPipelineMultisampleStateCreateInfo &ms = desc->ms;
ms.pSampleMask = nullptr;
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineShaderStageCreateInfo ss[2]{};
VkPipelineShaderStageCreateInfo *ss = &desc->shaderStageInfo[0];
ss[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
ss[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
ss[0].pSpecializationInfo = nullptr;
@ -265,13 +270,14 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
return nullPipeline;
}
VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
VkPipelineInputAssemblyStateCreateInfo &inputAssembly = desc->inputAssembly;
inputAssembly.flags = 0;
inputAssembly.topology = (VkPrimitiveTopology)key.topology;
inputAssembly.primitiveRestartEnable = false;
int vertexStride = 0;
VkVertexInputAttributeDescription attrs[8];
VkVertexInputAttributeDescription *attrs = &desc->attrs[0];
int attributeCount;
if (useHwTransform) {
attributeCount = SetupVertexAttribs(attrs, *decFmt);
@ -284,66 +290,49 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
vertexStride = (int)sizeof(TransformedVertex);
}
VkVertexInputBindingDescription ibd{};
VkVertexInputBindingDescription &ibd = desc->ibd;
ibd.binding = 0;
ibd.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
ibd.stride = vertexStride;
VkPipelineVertexInputStateCreateInfo vis{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
VkPipelineVertexInputStateCreateInfo &vis = desc->vis;
vis.flags = 0;
vis.vertexBindingDescriptionCount = 1;
vis.pVertexBindingDescriptions = &ibd;
vis.pVertexBindingDescriptions = &desc->ibd;
vis.vertexAttributeDescriptionCount = attributeCount;
vis.pVertexAttributeDescriptions = attrs;
VkPipelineViewportStateCreateInfo views{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
VkPipelineViewportStateCreateInfo &views = desc->views;
views.flags = 0;
views.viewportCount = 1;
views.scissorCount = 1;
views.pViewports = nullptr; // dynamic
views.pScissors = nullptr; // dynamic
VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
VkGraphicsPipelineCreateInfo &pipe = desc->pipe;
pipe.flags = 0;
pipe.stageCount = 2;
pipe.pStages = ss;
pipe.basePipelineIndex = 0;
pipe.pColorBlendState = &cbs;
pipe.pDepthStencilState = &dss;
pipe.pRasterizationState = &rs;
pipe.pColorBlendState = &desc->cbs;
pipe.pDepthStencilState = &desc->dss;
pipe.pRasterizationState = &desc->rs;
// We will use dynamic viewport state.
pipe.pVertexInputState = &vis;
pipe.pViewportState = &views;
pipe.pVertexInputState = &desc->vis;
pipe.pViewportState = &desc->views;
pipe.pTessellationState = nullptr;
pipe.pDynamicState = &ds;
pipe.pInputAssemblyState = &inputAssembly;
pipe.pMultisampleState = &ms;
pipe.pDynamicState = &desc->ds;
pipe.pInputAssemblyState = &desc->inputAssembly;
pipe.pMultisampleState = &desc->ms;
pipe.layout = layout;
pipe.basePipelineHandle = VK_NULL_HANDLE;
pipe.basePipelineIndex = 0;
pipe.renderPass = renderPass;
pipe.subpass = 0;
VkPipeline pipeline;
VkResult result = vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipe, nullptr, &pipeline);
if (result != VK_SUCCESS) {
ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result));
if (result == VK_INCOMPLETE) {
// Typical Adreno return value. It'll usually also log something vague, like "Failed to link shaders".
// Let's log some stuff and try to stumble along.
ERROR_LOG(G3D, "VS source code:\n%s\n", CutFromMain(vs->GetShaderString(SHADER_STRING_SOURCE_CODE)).c_str());
ERROR_LOG(G3D, "FS source code:\n%s\n", CutFromMain(fs->GetShaderString(SHADER_STRING_SOURCE_CODE)).c_str());
} else {
_dbg_assert_msg_(false, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result));
}
// Create a placeholder to avoid creating over and over if something is broken.
VulkanPipeline *nullPipeline = new VulkanPipeline();
nullPipeline->pipeline = VK_NULL_HANDLE;
nullPipeline->flags = 0;
return nullPipeline;
}
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc);
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
vulkanPipeline->pipeline = pipeline;
@ -358,7 +347,7 @@ static VulkanPipeline *CreateVulkanPipeline(VkDevice device, VkPipelineCache pip
return vulkanPipeline;
}
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
if (!pipelineCache_) {
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
@ -380,7 +369,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VkPipelineLayout layo
return iter;
VulkanPipeline *pipeline = CreateVulkanPipeline(
vulkan_->GetDevice(), pipelineCache_, layout, renderPass,
renderManager, pipelineCache_, layout, renderPass,
rasterKey, decFmt, vs, fs, useHwTransform);
pipelines_.Insert(key, pipeline);
@ -772,7 +761,7 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha
DecVtxFormat fmt;
fmt.InitializeFromID(key.vtxFmtId);
VulkanPipeline *pipeline = GetOrCreatePipeline(layout, rp, key.raster,
VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, rp, key.raster,
key.useHWTransform ? &fmt : 0,
vs, fs, key.useHWTransform);
if (!pipeline) {

@ -25,9 +25,11 @@
#include "GPU/Common/ShaderCommon.h"
#include "GPU/Vulkan/VulkanUtil.h"
#include "GPU/Vulkan/StateMappingVulkan.h"
#include "GPU/Vulkan/VulkanQueueRunner.h"
struct VKRGraphicsPipeline;
class VulkanRenderManager;
struct VulkanPipelineKey {
VulkanPipelineRasterStateKey raster; // prim is included here
VkRenderPass renderPass;
@ -48,7 +50,7 @@ struct VulkanPipelineKey {
// Simply wraps a Vulkan pipeline, providing some metadata.
struct VulkanPipeline {
VkPipeline pipeline;
VKRGraphicsPipeline *pipeline;
int flags; // PipelineFlags enum above.
bool UsesBlendConstant() const { return (flags & PIPELINE_FLAG_USES_BLEND_CONSTANT) != 0; }
@ -67,7 +69,7 @@ public:
PipelineManagerVulkan(VulkanContext *ctx);
~PipelineManagerVulkan();
VulkanPipeline *GetOrCreatePipeline(VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform);
VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform);
int GetNumPipelines() const { return (int)pipelines_.size(); }
void Clear();