mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
Split each renderpass/framebuffer into multiple "compatibility classes" (RenderPassType).
This commit is contained in:
parent
ad12aced6c
commit
e828df9f25
@ -127,6 +127,15 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void IterateMut(T func) {
|
||||
for (size_t i = 0; i < map.size(); i++) {
|
||||
if (state[i] == BucketState::TAKEN) {
|
||||
func(map[i].key, map[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
memset(state.data(), (int)BucketState::FREE, state.size());
|
||||
count_ = 0;
|
||||
|
@ -29,11 +29,12 @@ static void MergeRenderAreaRectInto(VkRect2D *dest, VkRect2D &src) {
|
||||
|
||||
void VulkanQueueRunner::CreateDeviceObjects() {
|
||||
INFO_LOG(G3D, "VulkanQueueRunner::CreateDeviceObjects");
|
||||
InitBackbufferRenderPass();
|
||||
|
||||
RPKey key{ VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR,
|
||||
VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE };
|
||||
framebufferRenderPass_ = GetRenderPass(key);
|
||||
RPKey key{
|
||||
VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR,
|
||||
VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE,
|
||||
};
|
||||
compatibleRenderPass_ = GetRenderPass(key);
|
||||
|
||||
#if 0
|
||||
// Just to check whether it makes sense to split some of these. drawidx is way bigger than the others...
|
||||
@ -117,78 +118,14 @@ void VulkanQueueRunner::DestroyDeviceObjects() {
|
||||
}
|
||||
readbackBufferSize_ = 0;
|
||||
|
||||
renderPasses_.Iterate([&](const RPKey &rpkey, VkRenderPass rp) {
|
||||
_assert_(rp != VK_NULL_HANDLE);
|
||||
vulkan_->Delete().QueueDeleteRenderPass(rp);
|
||||
renderPasses_.IterateMut([&](const RPKey &rpkey, VKRRenderPass *rp) {
|
||||
_assert_(rp);
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
vulkan_->Delete().QueueDeleteRenderPass(rp->pass[i]);
|
||||
}
|
||||
delete rp;
|
||||
});
|
||||
renderPasses_.Clear();
|
||||
|
||||
_assert_(backbufferRenderPass_ != VK_NULL_HANDLE);
|
||||
vulkan_->Delete().QueueDeleteRenderPass(backbufferRenderPass_);
|
||||
backbufferRenderPass_ = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::InitBackbufferRenderPass() {
|
||||
VkAttachmentDescription attachments[2];
|
||||
attachments[0].format = vulkan_->GetSwapchainFormat();
|
||||
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // We don't want to preserve the backbuffer between frames so we really don't care.
|
||||
attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; // We only render once to the backbuffer per frame so we can do this here.
|
||||
attachments[0].flags = 0;
|
||||
|
||||
attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat; // must use this same format later for the back depth buffer.
|
||||
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; // Don't care about storing backbuffer Z - we clear it anyway.
|
||||
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attachments[1].flags = 0;
|
||||
|
||||
VkAttachmentReference color_reference{};
|
||||
color_reference.attachment = 0;
|
||||
color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference depth_reference{};
|
||||
depth_reference.attachment = 1;
|
||||
depth_reference.layout = attachments[1].finalLayout;
|
||||
|
||||
VkSubpassDescription subpass{};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.flags = 0;
|
||||
subpass.inputAttachmentCount = 0;
|
||||
subpass.pInputAttachments = nullptr;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &color_reference;
|
||||
subpass.pResolveAttachments = nullptr;
|
||||
subpass.pDepthStencilAttachment = &depth_reference;
|
||||
subpass.preserveAttachmentCount = 0;
|
||||
subpass.pPreserveAttachments = nullptr;
|
||||
|
||||
// For the built-in layout transitions.
|
||||
VkSubpassDependency dep{};
|
||||
dep.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dep.dstSubpass = 0;
|
||||
dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep.srcAccessMask = 0;
|
||||
dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
VkRenderPassCreateInfo rp_info{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
|
||||
rp_info.attachmentCount = 2;
|
||||
rp_info.pAttachments = attachments;
|
||||
rp_info.subpassCount = 1;
|
||||
rp_info.pSubpasses = &subpass;
|
||||
rp_info.dependencyCount = 1;
|
||||
rp_info.pDependencies = &dep;
|
||||
|
||||
VkResult res = vkCreateRenderPass(vulkan_->GetDevice(), &rp_info, nullptr, &backbufferRenderPass_);
|
||||
_assert_(res == VK_SUCCESS);
|
||||
}
|
||||
|
||||
static VkAttachmentLoadOp ConvertLoadAction(VKRRenderPassLoadAction action) {
|
||||
@ -208,21 +145,16 @@ static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) {
|
||||
return VK_ATTACHMENT_STORE_OP_DONT_CARE; // avoid compiler warning
|
||||
}
|
||||
|
||||
VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) {
|
||||
auto pass = renderPasses_.Get(key);
|
||||
if (pass) {
|
||||
return pass;
|
||||
}
|
||||
|
||||
VkRenderPass VulkanQueueRunner::CreateRP(const RPKey &key, RenderPassType rpType) {
|
||||
VkAttachmentDescription attachments[2] = {};
|
||||
attachments[0].format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
attachments[0].format = rpType == RP_TYPE_BACKBUFFER ? vulkan_->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
|
||||
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[0].loadOp = ConvertLoadAction(key.colorLoadAction);
|
||||
attachments[0].storeOp = ConvertStoreAction(key.colorStoreAction);
|
||||
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].initialLayout = rpType == RP_TYPE_BACKBUFFER ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].finalLayout = rpType == RP_TYPE_BACKBUFFER ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments[0].flags = 0;
|
||||
|
||||
attachments[1].format = vulkan_->GetDeviceInfo().preferredDepthStencilFormat;
|
||||
@ -255,15 +187,48 @@ VkRenderPass VulkanQueueRunner::GetRenderPass(const RPKey &key) {
|
||||
subpass.preserveAttachmentCount = 0;
|
||||
subpass.pPreserveAttachments = nullptr;
|
||||
|
||||
// Not sure if this is really necessary.
|
||||
VkSubpassDependency dep{};
|
||||
dep.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dep.dstSubpass = 0;
|
||||
dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dep.srcAccessMask = 0;
|
||||
dep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
|
||||
rp.attachmentCount = 2;
|
||||
rp.pAttachments = attachments;
|
||||
rp.subpassCount = 1;
|
||||
rp.pSubpasses = &subpass;
|
||||
if (rpType == RP_TYPE_BACKBUFFER) {
|
||||
rp.dependencyCount = 1;
|
||||
rp.pDependencies = &dep;
|
||||
}
|
||||
|
||||
VkRenderPass pass;
|
||||
VkResult res = vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &pass);
|
||||
_assert_(res == VK_SUCCESS);
|
||||
_assert_(pass != VK_NULL_HANDLE);
|
||||
return pass;
|
||||
}
|
||||
|
||||
// Self-dependency: https://github.com/gpuweb/gpuweb/issues/442#issuecomment-547604827
|
||||
// Also see https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#synchronization-pipeline-barriers-subpass-self-dependencies
|
||||
VKRRenderPass *VulkanQueueRunner::GetRenderPass(const RPKey &key) {
|
||||
auto foundPass = renderPasses_.Get(key);
|
||||
if (foundPass) {
|
||||
return foundPass;
|
||||
}
|
||||
|
||||
VKRRenderPass *pass = new VKRRenderPass();
|
||||
// When we create a render pass, we create all "types" of it immediately,
|
||||
// practical later when referring to it. Could change to on-demand if it feels motivated
|
||||
// but I think the render pass objects are cheap.
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
pass->pass[i] = CreateRP(key, (RenderPassType)i);
|
||||
}
|
||||
|
||||
renderPasses_.Insert(key, pass);
|
||||
return pass;
|
||||
}
|
||||
@ -272,7 +237,8 @@ void VulkanQueueRunner::PreprocessSteps(std::vector<VKRStep *> &steps) {
|
||||
// Optimizes renderpasses, then sequences them.
|
||||
// Planned optimizations:
|
||||
// * Create copies of render target that are rendered to multiple times and textured from in sequence, and push those render passes
|
||||
// as early as possible in the frame (Wipeout billboards).
|
||||
// as early as possible in the frame (Wipeout billboards). This will require taking over more of descriptor management so we can
|
||||
// substitute descriptors, alternatively using texture array layers creatively.
|
||||
|
||||
for (int j = 0; j < (int)steps.size(); j++) {
|
||||
if (steps[j]->stepType == VKRStepType::RENDER &&
|
||||
@ -1159,6 +1125,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
int lastStencilCompareMask = -1;
|
||||
int lastStencilReference = -1;
|
||||
|
||||
const RenderPassType rpType = step.render.renderPassType;
|
||||
|
||||
for (const auto &c : commands) {
|
||||
switch (c.cmd) {
|
||||
case VKRRenderCommand::REMOVED:
|
||||
@ -1181,7 +1149,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
{
|
||||
VKRGraphicsPipeline *graphicsPipeline = c.graphics_pipeline.pipeline;
|
||||
if (graphicsPipeline != lastGraphicsPipeline) {
|
||||
VkPipeline pipeline = graphicsPipeline->pipeline->BlockUntilReady();
|
||||
_dbg_assert_(graphicsPipeline->pipeline[rpType]);
|
||||
VkPipeline pipeline = graphicsPipeline->pipeline[rpType]->BlockUntilReady();
|
||||
if (pipeline != VK_NULL_HANDLE) {
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
pipelineLayout = c.pipeline.pipelineLayout;
|
||||
@ -1338,7 +1307,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
VkRenderPass renderPass;
|
||||
VKRRenderPass *renderPass;
|
||||
int numClearVals = 0;
|
||||
VkClearValue clearVal[2]{};
|
||||
VkFramebuffer framebuf;
|
||||
@ -1349,7 +1318,7 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
|
||||
_dbg_assert_(step.render.finalDepthStencilLayout != VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
|
||||
VKRFramebuffer *fb = step.render.framebuffer;
|
||||
framebuf = fb->framebuf;
|
||||
framebuf = fb->framebuf[step.render.renderPassType];
|
||||
w = fb->width;
|
||||
h = fb->height;
|
||||
|
||||
@ -1395,7 +1364,13 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
|
||||
// Raw, rotated backbuffer size.
|
||||
w = vulkan_->GetBackbufferWidth();
|
||||
h = vulkan_->GetBackbufferHeight();
|
||||
renderPass = GetBackbufferRenderPass();
|
||||
|
||||
RPKey key{
|
||||
VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR,
|
||||
VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE,
|
||||
};
|
||||
renderPass = GetRenderPass(key);
|
||||
|
||||
Uint8x4ToFloat4(clearVal[0].color.float32, step.render.clearColor);
|
||||
numClearVals = 2; // We don't bother with a depth buffer here.
|
||||
clearVal[1].depthStencil.depth = 0.0f;
|
||||
@ -1403,7 +1378,7 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
|
||||
}
|
||||
|
||||
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
|
||||
rp_begin.renderPass = renderPass;
|
||||
rp_begin.renderPass = renderPass->pass[(int)step.render.renderPassType];
|
||||
rp_begin.framebuffer = framebuf;
|
||||
|
||||
VkRect2D rc = step.render.renderArea;
|
||||
|
@ -47,6 +47,14 @@ enum PipelineFlags {
|
||||
PIPELINE_FLAG_USES_DEPTH_STENCIL = (1 << 4), // Reads or writes the depth buffer.
|
||||
};
|
||||
|
||||
// Pipelines need to be created for the right type of render pass.
|
||||
enum RenderPassType {
|
||||
RP_TYPE_BACKBUFFER,
|
||||
RP_TYPE_COLOR_DEPTH,
|
||||
// Later will add pure-color render passes.
|
||||
RP_TYPE_COUNT,
|
||||
};
|
||||
|
||||
struct VkRenderData {
|
||||
VKRRenderCommand cmd;
|
||||
union {
|
||||
@ -147,6 +155,8 @@ struct QueueProfileContext {
|
||||
double cpuEndTime;
|
||||
};
|
||||
|
||||
struct VKRRenderPass;
|
||||
|
||||
struct VKRStep {
|
||||
VKRStep(VKRStepType _type) : stepType(_type) {}
|
||||
~VKRStep() {}
|
||||
@ -159,6 +169,7 @@ struct VKRStep {
|
||||
union {
|
||||
struct {
|
||||
VKRFramebuffer *framebuffer;
|
||||
// TODO: Look these up through renderPass?
|
||||
VKRRenderPassLoadAction colorLoad;
|
||||
VKRRenderPassLoadAction depthLoad;
|
||||
VKRRenderPassLoadAction stencilLoad;
|
||||
@ -175,6 +186,9 @@ struct VKRStep {
|
||||
VkImageLayout finalDepthStencilLayout;
|
||||
u32 pipelineFlags;
|
||||
VkRect2D renderArea;
|
||||
// Render pass type. Deduced after finishing recording the pass, from the used pipelines.
|
||||
// NOTE: Storing the render pass here doesn't do much good, we change the compatible parameters (load/store ops) during step optimization.
|
||||
RenderPassType renderPassType;
|
||||
} render;
|
||||
struct {
|
||||
VKRFramebuffer *src;
|
||||
@ -204,9 +218,24 @@ struct VKRStep {
|
||||
};
|
||||
};
|
||||
|
||||
struct VKRRenderPass {
|
||||
VkRenderPass pass[RP_TYPE_COUNT];
|
||||
};
|
||||
|
||||
struct RPKey {
|
||||
// Only render-pass-compatibility-volatile things can be here.
|
||||
VKRRenderPassLoadAction colorLoadAction;
|
||||
VKRRenderPassLoadAction depthLoadAction;
|
||||
VKRRenderPassLoadAction stencilLoadAction;
|
||||
VKRRenderPassStoreAction colorStoreAction;
|
||||
VKRRenderPassStoreAction depthStoreAction;
|
||||
VKRRenderPassStoreAction stencilStoreAction;
|
||||
};
|
||||
|
||||
class VulkanQueueRunner {
|
||||
public:
|
||||
VulkanQueueRunner(VulkanContext *vulkan) : vulkan_(vulkan), renderPasses_(16) {}
|
||||
|
||||
void SetBackbuffer(VkFramebuffer fb, VkImage img) {
|
||||
backbuffer_ = fb;
|
||||
backbufferImage_ = img;
|
||||
@ -221,14 +250,10 @@ public:
|
||||
void CreateDeviceObjects();
|
||||
void DestroyDeviceObjects();
|
||||
|
||||
VkRenderPass GetBackbufferRenderPass() const {
|
||||
return backbufferRenderPass_;
|
||||
}
|
||||
|
||||
// Get a render pass that's compatible with all our framebuffers.
|
||||
// Note that it's precached, cannot look up in the map as this might be on another thread.
|
||||
VkRenderPass GetFramebufferRenderPass() const {
|
||||
return framebufferRenderPass_;
|
||||
VKRRenderPass *GetCompatibleRenderPass() const {
|
||||
return compatibleRenderPass_;
|
||||
}
|
||||
|
||||
inline int RPIndex(VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth) {
|
||||
@ -237,20 +262,11 @@ public:
|
||||
|
||||
void CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
|
||||
|
||||
struct RPKey {
|
||||
VKRRenderPassLoadAction colorLoadAction;
|
||||
VKRRenderPassLoadAction depthLoadAction;
|
||||
VKRRenderPassLoadAction stencilLoadAction;
|
||||
VKRRenderPassStoreAction colorStoreAction;
|
||||
VKRRenderPassStoreAction depthStoreAction;
|
||||
VKRRenderPassStoreAction stencilStoreAction;
|
||||
};
|
||||
VKRRenderPass *GetRenderPass(const RPKey &key);
|
||||
|
||||
VkRenderPass GetRenderPass(const RPKey &key);
|
||||
|
||||
bool GetRenderPassKey(VkRenderPass passToFind, RPKey *outKey) const {
|
||||
bool GetRenderPassKey(VKRRenderPass *passToFind, RPKey *outKey) const {
|
||||
bool found = false;
|
||||
renderPasses_.Iterate([passToFind, &found, outKey](const RPKey &rpkey, VkRenderPass pass) {
|
||||
renderPasses_.Iterate([passToFind, &found, outKey](const RPKey &rpkey, const VKRRenderPass *pass) {
|
||||
if (pass == passToFind) {
|
||||
found = true;
|
||||
*outKey = rpkey;
|
||||
@ -273,7 +289,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void InitBackbufferRenderPass();
|
||||
VkRenderPass CreateRP(const RPKey &key, RenderPassType rpType);
|
||||
|
||||
void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
@ -302,14 +318,12 @@ private:
|
||||
VkFramebuffer backbuffer_ = VK_NULL_HANDLE;
|
||||
VkImage backbufferImage_ = VK_NULL_HANDLE;
|
||||
|
||||
VkRenderPass backbufferRenderPass_ = VK_NULL_HANDLE;
|
||||
|
||||
// The "Compatible" render pass. Used when creating pipelines that render to "normal" framebuffers.
|
||||
VkRenderPass framebufferRenderPass_ = VK_NULL_HANDLE;
|
||||
// The "Compatible" render pass. Should be able to get rid of this soon.
|
||||
VKRRenderPass *compatibleRenderPass_ = nullptr;
|
||||
|
||||
// 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_;
|
||||
// Each VKRRenderPass contains all compatibility classes (which attachments they have, etc).
|
||||
DenseHashMap<RPKey, VKRRenderPass *, nullptr> renderPasses_;
|
||||
|
||||
// Readback buffer. Currently we only support synchronous readback, so we only really need one.
|
||||
// We size it generously.
|
||||
|
@ -25,7 +25,8 @@
|
||||
|
||||
using namespace PPSSPP_VK;
|
||||
|
||||
bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) {
|
||||
// renderPass is an example of the "compatibility class" or RenderPassType type.
|
||||
bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType) {
|
||||
if (!desc) {
|
||||
// Already failed to create this one.
|
||||
return false;
|
||||
@ -55,12 +56,30 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) {
|
||||
ss[1].pName = "main";
|
||||
ss[1].flags = 0;
|
||||
|
||||
desc->pipe.pStages = ss;
|
||||
desc->pipe.stageCount = 2;
|
||||
VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
|
||||
pipe.pStages = ss;
|
||||
pipe.stageCount = 2;
|
||||
pipe.renderPass = compatibleRenderPass;
|
||||
pipe.basePipelineIndex = 0;
|
||||
pipe.pColorBlendState = &desc->cbs;
|
||||
pipe.pDepthStencilState = &desc->dss;
|
||||
pipe.pRasterizationState = &desc->rs;
|
||||
|
||||
// We will use dynamic viewport state.
|
||||
pipe.pVertexInputState = &desc->vis;
|
||||
pipe.pViewportState = &desc->views;
|
||||
pipe.pTessellationState = nullptr;
|
||||
pipe.pDynamicState = &desc->ds;
|
||||
pipe.pInputAssemblyState = &desc->inputAssembly;
|
||||
pipe.pMultisampleState = &desc->ms;
|
||||
pipe.layout = desc->pipelineLayout;
|
||||
pipe.basePipelineHandle = VK_NULL_HANDLE;
|
||||
pipe.basePipelineIndex = 0;
|
||||
pipe.subpass = 0;
|
||||
|
||||
double start = time_now_d();
|
||||
VkPipeline vkpipeline;
|
||||
VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &desc->pipe, nullptr, &vkpipeline);
|
||||
VkResult result = vkCreateGraphicsPipelines(vulkan->GetDevice(), desc->pipelineCache, 1, &pipe, nullptr, &vkpipeline);
|
||||
|
||||
INFO_LOG(G3D, "Pipeline creation time: %0.2f ms", (time_now_d() - start) * 1000.0);
|
||||
|
||||
@ -70,14 +89,14 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) {
|
||||
// Would really like to log more here, we could probably attach more info to desc.
|
||||
//
|
||||
// At least create a null placeholder to avoid creating over and over if something is broken.
|
||||
pipeline->Post(VK_NULL_HANDLE);
|
||||
pipeline[rpType]->Post(VK_NULL_HANDLE);
|
||||
success = false;
|
||||
} else if (result != VK_SUCCESS) {
|
||||
pipeline->Post(VK_NULL_HANDLE);
|
||||
pipeline[rpType]->Post(VK_NULL_HANDLE);
|
||||
ERROR_LOG(G3D, "Failed creating graphics pipeline! result='%s'", VulkanResultToString(result));
|
||||
success = false;
|
||||
} else {
|
||||
pipeline->Post(vkpipeline);
|
||||
pipeline[rpType]->Post(vkpipeline);
|
||||
}
|
||||
|
||||
// Having the desc stick around can be useful for debugging.
|
||||
@ -110,33 +129,35 @@ bool VKRComputePipeline::Create(VulkanContext *vulkan) {
|
||||
return success;
|
||||
}
|
||||
|
||||
VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, int _width, int _height, const char *tag) : vulkan_(vk) {
|
||||
VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, const char *tag) : vulkan_(vk) {
|
||||
width = _width;
|
||||
height = _height;
|
||||
|
||||
CreateImage(vulkan_, initCmd, color, width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);
|
||||
CreateImage(vulkan_, initCmd, depth, width, height, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);
|
||||
|
||||
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
VkImageView views[2]{};
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
VkImageView views[2]{};
|
||||
|
||||
fbci.renderPass = renderPass;
|
||||
fbci.attachmentCount = 2;
|
||||
fbci.pAttachments = views;
|
||||
views[0] = color.imageView;
|
||||
views[1] = depth.imageView;
|
||||
fbci.width = width;
|
||||
fbci.height = height;
|
||||
fbci.layers = 1;
|
||||
fbci.renderPass = compatibleRenderPass->pass[i];
|
||||
fbci.attachmentCount = 2;
|
||||
fbci.pAttachments = views;
|
||||
views[0] = color.imageView;
|
||||
views[1] = depth.imageView;
|
||||
fbci.width = width;
|
||||
fbci.height = height;
|
||||
fbci.layers = 1;
|
||||
|
||||
VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf);
|
||||
_assert_(res == VK_SUCCESS);
|
||||
VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[i]);
|
||||
_assert_(res == VK_SUCCESS);
|
||||
|
||||
if (tag && vk->Extensions().EXT_debug_utils) {
|
||||
vk->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_color_%s", tag).c_str());
|
||||
vk->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_depth_%s", tag).c_str());
|
||||
vk->SetDebugName(framebuf, VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag).c_str());
|
||||
this->tag = tag;
|
||||
if (tag && vk->Extensions().EXT_debug_utils) {
|
||||
vk->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_color_%s", tag).c_str());
|
||||
vk->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_depth_%s", tag).c_str());
|
||||
vk->SetDebugName(framebuf[i], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag).c_str());
|
||||
this->tag = tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,8 +176,10 @@ VKRFramebuffer::~VKRFramebuffer() {
|
||||
}
|
||||
if (depth.depthSampleView)
|
||||
vulkan_->Delete().QueueDeleteImageView(depth.depthSampleView);
|
||||
if (framebuf)
|
||||
vulkan_->Delete().QueueDeleteFramebuffer(framebuf);
|
||||
for (auto &fb : framebuf) {
|
||||
if (fb)
|
||||
vulkan_->Delete().QueueDeleteFramebuffer(fb);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag) {
|
||||
@ -493,7 +516,7 @@ void VulkanRenderManager::CompileThreadFunc() {
|
||||
for (auto &entry : toCompile) {
|
||||
switch (entry.type) {
|
||||
case CompileQueueEntry::Type::GRAPHICS:
|
||||
entry.graphics->Create(vulkan_);
|
||||
entry.graphics->Create(vulkan_, entry.compatibleRenderPass, entry.renderPassType);
|
||||
break;
|
||||
case CompileQueueEntry::Type::COMPUTE:
|
||||
entry.compute->Create(vulkan_);
|
||||
@ -664,23 +687,45 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() {
|
||||
}
|
||||
|
||||
void VulkanRenderManager::EndCurRenderStep() {
|
||||
if (!curRenderStep_)
|
||||
return;
|
||||
|
||||
RPKey key{
|
||||
curRenderStep_->render.colorLoad, curRenderStep_->render.depthLoad, curRenderStep_->render.stencilLoad,
|
||||
curRenderStep_->render.colorStore, curRenderStep_->render.depthStore, curRenderStep_->render.stencilStore,
|
||||
};
|
||||
RenderPassType rpType = RP_TYPE_COLOR_DEPTH;
|
||||
curRenderStep_->render.pipelineFlags = curPipelineFlags_;
|
||||
if (!curRenderStep_->render.framebuffer) {
|
||||
rpType = RP_TYPE_BACKBUFFER;
|
||||
}
|
||||
|
||||
VKRRenderPass *renderPass = queueRunner_.GetRenderPass(key);
|
||||
curRenderStep_->render.renderPassType = rpType;
|
||||
|
||||
// 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.
|
||||
if (curRenderStep_) {
|
||||
curRenderStep_->render.pipelineFlags = curPipelineFlags_;
|
||||
// We don't do this optimization for very small targets, probably not worth it.
|
||||
if (!curRenderArea_.Empty() && (curWidth_ > 32 && curHeight_ > 32)) {
|
||||
curRenderStep_->render.renderArea = curRenderArea_.ToVkRect2D();
|
||||
} else {
|
||||
curRenderStep_->render.renderArea.offset = {};
|
||||
curRenderStep_->render.renderArea.extent = { (uint32_t)curWidth_, (uint32_t)curHeight_ };
|
||||
}
|
||||
curRenderArea_.Reset();
|
||||
|
||||
// We no longer have a current render step.
|
||||
curRenderStep_ = nullptr;
|
||||
curPipelineFlags_ = 0;
|
||||
compileMutex_.lock();
|
||||
for (VKRGraphicsPipeline *pipeline : pipelinesToCreate_) {
|
||||
pipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->pass[rpType], rpType));
|
||||
}
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
pipelinesToCreate_.clear();
|
||||
|
||||
// We don't do this optimization for very small targets, probably not worth it.
|
||||
if (!curRenderArea_.Empty() && (curWidth_ > 32 && curHeight_ > 32)) {
|
||||
curRenderStep_->render.renderArea = curRenderArea_.ToVkRect2D();
|
||||
} else {
|
||||
curRenderStep_->render.renderArea.offset = {};
|
||||
curRenderStep_->render.renderArea.extent = { (uint32_t)curWidth_, (uint32_t)curHeight_ };
|
||||
}
|
||||
curRenderArea_.Reset();
|
||||
|
||||
// We no longer have a current render step.
|
||||
curRenderStep_ = nullptr;
|
||||
curPipelineFlags_ = 0;
|
||||
}
|
||||
|
||||
void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassLoadAction color, VKRRenderPassLoadAction depth, VKRRenderPassLoadAction stencil, uint32_t clearColor, float clearDepth, uint8_t clearStencil, const char *tag) {
|
||||
@ -909,7 +954,7 @@ bool VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) {
|
||||
|
||||
VLOG("InitFramebuffers: %dx%d", width, height);
|
||||
VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
fb_info.renderPass = queueRunner_.GetBackbufferRenderPass();
|
||||
fb_info.renderPass = queueRunner_.GetCompatibleRenderPass()->pass[RP_TYPE_BACKBUFFER];
|
||||
fb_info.attachmentCount = 2;
|
||||
fb_info.pAttachments = attachments;
|
||||
fb_info.width = width;
|
||||
|
@ -43,10 +43,10 @@ void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int
|
||||
|
||||
class VKRFramebuffer {
|
||||
public:
|
||||
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRenderPass renderPass, int _width, int _height, const char *tag);
|
||||
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, const char *tag);
|
||||
~VKRFramebuffer();
|
||||
|
||||
VkFramebuffer framebuf = VK_NULL_HANDLE;
|
||||
VkFramebuffer framebuf[RP_TYPE_COUNT]{};
|
||||
VKRImage color{};
|
||||
VKRImage depth{};
|
||||
int width = 0;
|
||||
@ -132,7 +132,12 @@ struct VKRGraphicsPipelineDesc {
|
||||
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 };
|
||||
|
||||
VkPipelineLayout pipelineLayout;
|
||||
|
||||
// Does not include the render pass type, it's passed in separately since the
|
||||
// desc is persistent.
|
||||
RPKey rpKey;
|
||||
};
|
||||
|
||||
// All the data needed to create a compute pipeline.
|
||||
@ -144,16 +149,18 @@ struct VKRComputePipelineDesc {
|
||||
// Wrapped pipeline.
|
||||
struct VKRGraphicsPipeline {
|
||||
VKRGraphicsPipeline() {
|
||||
pipeline = Promise<VkPipeline>::CreateEmpty();
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
pipeline[i] = nullptr;
|
||||
}
|
||||
}
|
||||
~VKRGraphicsPipeline() {
|
||||
delete desc;
|
||||
}
|
||||
|
||||
VKRGraphicsPipelineDesc *desc = nullptr;
|
||||
Promise<VkPipeline> *pipeline;
|
||||
Promise<VkPipeline> *pipeline[RP_TYPE_COUNT];
|
||||
|
||||
bool Create(VulkanContext *vulkan);
|
||||
bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType);
|
||||
};
|
||||
|
||||
struct VKRComputePipeline {
|
||||
@ -170,13 +177,16 @@ struct VKRComputePipeline {
|
||||
};
|
||||
|
||||
struct CompileQueueEntry {
|
||||
CompileQueueEntry(VKRGraphicsPipeline *p) : type(Type::GRAPHICS), graphics(p) {}
|
||||
CompileQueueEntry(VKRComputePipeline *p) : type(Type::COMPUTE), compute(p) {}
|
||||
CompileQueueEntry(VKRGraphicsPipeline *p, VkRenderPass _compatibleRenderPass, RenderPassType _renderPassType)
|
||||
: type(Type::GRAPHICS), graphics(p), compatibleRenderPass(_compatibleRenderPass), renderPassType(_renderPassType) {}
|
||||
CompileQueueEntry(VKRComputePipeline *p) : type(Type::COMPUTE), compute(p), renderPassType(RP_TYPE_COLOR_DEPTH) {}
|
||||
enum class Type {
|
||||
GRAPHICS,
|
||||
COMPUTE,
|
||||
};
|
||||
Type type;
|
||||
VkRenderPass compatibleRenderPass;
|
||||
RenderPassType renderPassType;
|
||||
VKRGraphicsPipeline *graphics = nullptr;
|
||||
VKRComputePipeline *compute = nullptr;
|
||||
};
|
||||
@ -228,13 +238,13 @@ 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.
|
||||
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc) {
|
||||
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline();
|
||||
_dbg_assert_(desc->vertexShader);
|
||||
_dbg_assert_(desc->fragmentShader);
|
||||
pipeline->desc = desc;
|
||||
compileMutex_.lock();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline));
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
pipelinesToCreate_.push_back(pipeline);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@ -248,16 +258,6 @@ public:
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void BindPipeline(VkPipeline pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(pipeline != VK_NULL_HANDLE);
|
||||
VkRenderData data{ VKRRenderCommand::BIND_PIPELINE };
|
||||
data.pipeline.pipeline = pipeline;
|
||||
data.pipeline.pipelineLayout = pipelineLayout;
|
||||
curPipelineFlags_ |= flags;
|
||||
curRenderStep_->commands.push_back(data);
|
||||
}
|
||||
|
||||
void BindPipeline(VKRGraphicsPipeline *pipeline, PipelineFlags flags, VkPipelineLayout pipelineLayout) {
|
||||
_dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER);
|
||||
_dbg_assert_(pipeline != nullptr);
|
||||
@ -431,20 +431,6 @@ public:
|
||||
|
||||
VkCommandBuffer GetInitCmd();
|
||||
|
||||
VkRenderPass GetBackbufferRenderPass() {
|
||||
return queueRunner_.GetBackbufferRenderPass();
|
||||
}
|
||||
VkRenderPass GetFramebufferRenderPass() {
|
||||
return queueRunner_.GetFramebufferRenderPass();
|
||||
}
|
||||
VkRenderPass GetCompatibleRenderPass() {
|
||||
if (curRenderStep_ && curRenderStep_->render.framebuffer != nullptr) {
|
||||
return queueRunner_.GetFramebufferRenderPass();
|
||||
} else {
|
||||
return queueRunner_.GetBackbufferRenderPass();
|
||||
}
|
||||
}
|
||||
|
||||
// Gets a frame-unique ID of the current step being recorded. Can be used to figure out
|
||||
// when the current step has changed, which means the caller will need to re-record its state.
|
||||
int GetCurrentStepId() const {
|
||||
@ -581,6 +567,9 @@ private:
|
||||
std::mutex compileMutex_;
|
||||
std::vector<CompileQueueEntry> compileQueue_;
|
||||
|
||||
// pipelines to create at the end of the current render pass.
|
||||
std::vector<VKRGraphicsPipeline *> pipelinesToCreate_;
|
||||
|
||||
// Swap chain management
|
||||
struct SwapchainImageData {
|
||||
VkImage image;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "Common/GPU/Vulkan/VulkanContext.h"
|
||||
#include "Common/GPU/Vulkan/VulkanImage.h"
|
||||
#include "Common/GPU/Vulkan/VulkanMemory.h"
|
||||
#include "Common/Thread/Promise.h"
|
||||
|
||||
#include "Core/Config.h"
|
||||
|
||||
@ -248,13 +249,12 @@ public:
|
||||
|
||||
class VKPipeline : public Pipeline {
|
||||
public:
|
||||
VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags) : flags(_flags), vulkan_(vulkan) {
|
||||
VKPipeline(VulkanRenderManager *rm, size_t size, PipelineFlags _flags) : rm_(rm), flags(_flags) {
|
||||
uboSize_ = (int)size;
|
||||
ubo_ = new uint8_t[uboSize_];
|
||||
}
|
||||
~VKPipeline() {
|
||||
vulkan_->Delete().QueueDeletePipeline(backbufferPipeline);
|
||||
vulkan_->Delete().QueueDeletePipeline(framebufferPipeline);
|
||||
// TODO: Right now we just leak the pipeline! This is not good.
|
||||
delete[] ubo_;
|
||||
}
|
||||
|
||||
@ -272,17 +272,16 @@ public:
|
||||
return uboSize_;
|
||||
}
|
||||
|
||||
VkPipeline backbufferPipeline = VK_NULL_HANDLE;
|
||||
VkPipeline framebufferPipeline = VK_NULL_HANDLE;
|
||||
|
||||
VKRGraphicsPipeline *pipeline = nullptr;
|
||||
PipelineFlags flags;
|
||||
|
||||
int stride[4]{};
|
||||
int dynamicUniformSize = 0;
|
||||
|
||||
bool usesStencil = false;
|
||||
|
||||
private:
|
||||
VulkanContext *vulkan_;
|
||||
VulkanRenderManager *rm_;
|
||||
uint8_t *ubo_;
|
||||
int uboSize_;
|
||||
};
|
||||
@ -429,7 +428,7 @@ public:
|
||||
void DrawIndexed(int vertexCount, int offset) override;
|
||||
void DrawUP(const void *vdata, int vertexCount) override;
|
||||
|
||||
void BindCompatiblePipeline();
|
||||
void BindCurrentPipeline();
|
||||
void ApplyDynamicState();
|
||||
|
||||
void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override;
|
||||
@ -466,13 +465,6 @@ public:
|
||||
switch (obj) {
|
||||
case NativeObject::CONTEXT:
|
||||
return (uint64_t)vulkan_;
|
||||
case NativeObject::FRAMEBUFFER_RENDERPASS:
|
||||
// Return a representative renderpass.
|
||||
return (uint64_t)renderManager_.GetFramebufferRenderPass();
|
||||
case NativeObject::BACKBUFFER_RENDERPASS:
|
||||
return (uint64_t)renderManager_.GetBackbufferRenderPass();
|
||||
case NativeObject::COMPATIBLE_RENDERPASS:
|
||||
return (uint64_t)renderManager_.GetCompatibleRenderPass();
|
||||
case NativeObject::INIT_COMMANDBUFFER:
|
||||
return (uint64_t)renderManager_.GetInitCmd();
|
||||
case NativeObject::BOUND_TEXTURE0_IMAGEVIEW:
|
||||
@ -1047,7 +1039,24 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
pipelineFlags |= PIPELINE_FLAG_USES_DEPTH_STENCIL;
|
||||
}
|
||||
|
||||
VKPipeline *pipeline = new VKPipeline(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags);
|
||||
VKPipeline *pipeline = new VKPipeline(&renderManager_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags);
|
||||
|
||||
VKRGraphicsPipelineDesc gDesc{};
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> stages;
|
||||
stages.resize(desc.shaders.size());
|
||||
|
||||
for (auto &iter : desc.shaders) {
|
||||
VKShaderModule *vkshader = (VKShaderModule *)iter;
|
||||
if (vkshader->GetStage() == ShaderStage::Vertex) {
|
||||
gDesc.vertexShader = Promise<VkShaderModule>::AlreadyDone(vkshader->Get());
|
||||
} else if (vkshader->GetStage() == ShaderStage::Fragment) {
|
||||
gDesc.fragmentShader = Promise<VkShaderModule>::AlreadyDone(vkshader->Get());
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Bad stage");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (input) {
|
||||
for (int i = 0; i < (int)input->bindings.size(); i++) {
|
||||
@ -1056,84 +1065,50 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
} else {
|
||||
pipeline->stride[0] = 0;
|
||||
}
|
||||
_dbg_assert_(input->bindings.size() == 1);
|
||||
_dbg_assert_((int)input->attributes.size() == (int)input->visc.vertexAttributeDescriptionCount);
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> stages;
|
||||
stages.resize(desc.shaders.size());
|
||||
int i = 0;
|
||||
for (auto &iter : desc.shaders) {
|
||||
VKShaderModule *vkshader = (VKShaderModule *)iter;
|
||||
if (!vkshader) {
|
||||
ERROR_LOG(G3D, "CreateGraphicsPipeline got passed a null shader");
|
||||
return nullptr;
|
||||
}
|
||||
VkPipelineShaderStageCreateInfo &stage = stages[i++];
|
||||
stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
stage.pNext = nullptr;
|
||||
stage.pSpecializationInfo = nullptr;
|
||||
stage.stage = StageToVulkan(vkshader->GetStage());
|
||||
stage.module = vkshader->Get();
|
||||
stage.pName = "main";
|
||||
stage.flags = 0;
|
||||
gDesc.ibd = input->bindings[0];
|
||||
for (int i = 0; i < input->attributes.size(); i++) {
|
||||
gDesc.attrs[i] = input->attributes[i];
|
||||
}
|
||||
gDesc.vis.vertexAttributeDescriptionCount = input->visc.vertexAttributeDescriptionCount;
|
||||
gDesc.vis.vertexBindingDescriptionCount = input->visc.vertexBindingDescriptionCount;
|
||||
gDesc.vis.pVertexBindingDescriptions = &gDesc.ibd;
|
||||
gDesc.vis.pVertexAttributeDescriptions = gDesc.attrs;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssembly = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
||||
inputAssembly.topology = primToVK[(int)desc.prim];
|
||||
inputAssembly.primitiveRestartEnable = false;
|
||||
gDesc.blend0 = blend->attachments[0];
|
||||
gDesc.cbs = blend->info;
|
||||
|
||||
gDesc.dss = depth->info;
|
||||
|
||||
raster->ToVulkan(&gDesc.rs);
|
||||
|
||||
// Copy bindings from input layout.
|
||||
gDesc.inputAssembly.topology = primToVK[(int)desc.prim];
|
||||
|
||||
// We treat the three stencil states as a unit in other places, so let's do that here too.
|
||||
VkDynamicState dynamics[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK };
|
||||
VkPipelineDynamicStateCreateInfo dynamicInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
||||
dynamicInfo.dynamicStateCount = depth->info.stencilTestEnable ? ARRAY_SIZE(dynamics) : 2;
|
||||
dynamicInfo.pDynamicStates = dynamics;
|
||||
const VkDynamicState dynamics[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK };
|
||||
gDesc.ds.dynamicStateCount = depth->info.stencilTestEnable ? ARRAY_SIZE(dynamics) : 2;
|
||||
for (int i = 0; i < gDesc.ds.dynamicStateCount; i++) {
|
||||
gDesc.dynamicStates[i] = dynamics[i];
|
||||
}
|
||||
gDesc.ds.pDynamicStates = gDesc.dynamicStates;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
||||
ms.pSampleMask = nullptr;
|
||||
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
gDesc.ms.pSampleMask = nullptr;
|
||||
gDesc.ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkPipelineViewportStateCreateInfo vs{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
||||
vs.viewportCount = 1;
|
||||
vs.scissorCount = 1;
|
||||
vs.pViewports = nullptr; // dynamic
|
||||
vs.pScissors = nullptr; // dynamic
|
||||
gDesc.views.viewportCount = 1;
|
||||
gDesc.views.scissorCount = 1;
|
||||
gDesc.views.pViewports = nullptr; // dynamic
|
||||
gDesc.views.pScissors = nullptr; // dynamic
|
||||
|
||||
gDesc.pipelineLayout = pipelineLayout_;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
||||
raster->ToVulkan(&rs);
|
||||
raster->ToVulkan(&gDesc.rs);
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo emptyVisc{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
||||
|
||||
VkGraphicsPipelineCreateInfo createInfo[2]{};
|
||||
for (auto &info : createInfo) {
|
||||
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
info.flags = 0;
|
||||
info.stageCount = (uint32_t)stages.size();
|
||||
info.pStages = stages.data();
|
||||
info.pColorBlendState = &blend->info;
|
||||
info.pDepthStencilState = &depth->info;
|
||||
info.pDynamicState = &dynamicInfo;
|
||||
info.pInputAssemblyState = &inputAssembly;
|
||||
info.pTessellationState = nullptr;
|
||||
info.pMultisampleState = &ms;
|
||||
info.pVertexInputState = input ? &input->visc : &emptyVisc;
|
||||
info.pRasterizationState = &rs;
|
||||
info.pViewportState = &vs; // Must set viewport and scissor counts even if we set the actual state dynamically.
|
||||
info.layout = pipelineLayout_;
|
||||
info.subpass = 0;
|
||||
}
|
||||
|
||||
createInfo[0].renderPass = renderManager_.GetBackbufferRenderPass();
|
||||
createInfo[1].renderPass = renderManager_.GetFramebufferRenderPass();
|
||||
|
||||
// OK, need to create new pipelines.
|
||||
VkPipeline pipelines[2]{};
|
||||
VkResult result = vkCreateGraphicsPipelines(device_, pipelineCache_, 2, createInfo, nullptr, pipelines);
|
||||
if (result != VK_SUCCESS) {
|
||||
ERROR_LOG(G3D, "Failed to create graphics pipeline");
|
||||
delete pipeline;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pipeline->backbufferPipeline = pipelines[0];
|
||||
pipeline->framebufferPipeline = pipelines[1];
|
||||
pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc);;
|
||||
|
||||
if (desc.uniformDesc) {
|
||||
pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize;
|
||||
@ -1338,7 +1313,7 @@ void VKContext::Draw(int vertexCount, int offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
BindCompatiblePipeline();
|
||||
BindCurrentPipeline();
|
||||
ApplyDynamicState();
|
||||
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount, offset);
|
||||
}
|
||||
@ -1358,7 +1333,7 @@ void VKContext::DrawIndexed(int vertexCount, int offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
BindCompatiblePipeline();
|
||||
BindCurrentPipeline();
|
||||
ApplyDynamicState();
|
||||
renderManager_.DrawIndexed(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16);
|
||||
}
|
||||
@ -1374,18 +1349,13 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
BindCompatiblePipeline();
|
||||
BindCurrentPipeline();
|
||||
ApplyDynamicState();
|
||||
renderManager_.Draw(descSet, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffsets_[0], vertexCount);
|
||||
}
|
||||
|
||||
void VKContext::BindCompatiblePipeline() {
|
||||
VkRenderPass renderPass = renderManager_.GetCompatibleRenderPass();
|
||||
if (renderPass == renderManager_.GetBackbufferRenderPass()) {
|
||||
renderManager_.BindPipeline(curPipeline_->backbufferPipeline, curPipeline_->flags, pipelineLayout_);
|
||||
} else {
|
||||
renderManager_.BindPipeline(curPipeline_->framebufferPipeline, curPipeline_->flags, pipelineLayout_);
|
||||
}
|
||||
void VKContext::BindCurrentPipeline() {
|
||||
renderManager_.BindPipeline(curPipeline_->pipeline, curPipeline_->flags, pipelineLayout_);
|
||||
}
|
||||
|
||||
void VKContext::Clear(int clearMask, uint32_t colorval, float depthVal, int stencilVal) {
|
||||
@ -1497,7 +1467,7 @@ Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) {
|
||||
VkCommandBuffer cmd = renderManager_.GetInitCmd();
|
||||
// TODO: We always create with depth here, even when it's not needed (such as color temp FBOs).
|
||||
// Should optimize those away.
|
||||
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetFramebufferRenderPass(), desc.width, desc.height, desc.tag);
|
||||
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.tag);
|
||||
return new VKFramebuffer(vkrfb);
|
||||
}
|
||||
|
||||
|
@ -239,9 +239,6 @@ enum class NativeObject {
|
||||
BACKBUFFER_COLOR_TEX,
|
||||
BACKBUFFER_DEPTH_TEX,
|
||||
FEATURE_LEVEL,
|
||||
COMPATIBLE_RENDERPASS,
|
||||
BACKBUFFER_RENDERPASS,
|
||||
FRAMEBUFFER_RENDERPASS,
|
||||
INIT_COMMANDBUFFER,
|
||||
BOUND_TEXTURE0_IMAGEVIEW,
|
||||
BOUND_TEXTURE1_IMAGEVIEW,
|
||||
|
@ -79,6 +79,7 @@ public:
|
||||
|
||||
// Returns T if the data is ready, nullptr if it's not.
|
||||
T Poll() {
|
||||
_dbg_assert_(this != nullptr);
|
||||
std::lock_guard<std::mutex> guard(readyMutex_);
|
||||
if (ready_) {
|
||||
return data_;
|
||||
@ -95,6 +96,7 @@ public:
|
||||
}
|
||||
|
||||
T BlockUntilReady() {
|
||||
_dbg_assert_(this != nullptr);
|
||||
std::lock_guard<std::mutex> guard(readyMutex_);
|
||||
if (ready_) {
|
||||
return data_;
|
||||
|
@ -36,8 +36,11 @@
|
||||
|
||||
#define WRITE(p, ...) p.F(__VA_ARGS__)
|
||||
|
||||
bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, std::string *errorString) {
|
||||
bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, FragmentShaderFlags *fragmentShaderFlags, std::string *errorString) {
|
||||
*uniformMask = 0;
|
||||
if (fragmentShaderFlags) {
|
||||
*fragmentShaderFlags = (FragmentShaderFlags)0;
|
||||
}
|
||||
errorString->clear();
|
||||
|
||||
bool highpFog = false;
|
||||
|
@ -39,4 +39,9 @@ struct FShaderID;
|
||||
// For stencil upload
|
||||
#define CONST_PS_STENCILVALUE 11
|
||||
|
||||
bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, std::string *errorString);
|
||||
// Can technically be deduced from the fragment shader ID, but this is safer.
|
||||
enum FragmentShaderFlags {
|
||||
FS_FLAG_INPUT_ATTACHMENT,
|
||||
};
|
||||
|
||||
bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, FragmentShaderFlags *fragmentShaderFlags, std::string *errorString);
|
||||
|
@ -224,7 +224,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertType, D3D11VertexShader **
|
||||
// Fragment shader not in cache. Let's compile it.
|
||||
std::string genErrorString;
|
||||
uint64_t uniformMask;
|
||||
GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, &genErrorString);
|
||||
GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, nullptr, &genErrorString);
|
||||
fs = new D3D11FragmentShader(device_, featureLevel_, FSID, codeBuffer_, useHWTransform);
|
||||
fsCache_[FSID] = fs;
|
||||
} else {
|
||||
|
@ -623,7 +623,7 @@ VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellat
|
||||
// Fragment shader not in cache. Let's compile it.
|
||||
std::string errorString;
|
||||
uint64_t uniformMask;
|
||||
bool success = GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, &errorString);
|
||||
bool success = GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, nullptr, &errorString);
|
||||
// We're supposed to handle all possible cases.
|
||||
_assert_(success);
|
||||
fs = new PSShader(device_, FSID, codeBuffer_);
|
||||
|
@ -709,7 +709,7 @@ void ShaderManagerGLES::DirtyLastShader() {
|
||||
Shader *ShaderManagerGLES::CompileFragmentShader(FShaderID FSID) {
|
||||
uint64_t uniformMask;
|
||||
std::string errorString;
|
||||
if (!GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, &errorString)) {
|
||||
if (!GenerateFragmentShader(FSID, codeBuffer_, draw_->GetShaderLanguageDesc(), draw_->GetBugs(), &uniformMask, nullptr, &errorString)) {
|
||||
ERROR_LOG(G3D, "Shader gen error: %s", errorString.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -768,9 +768,7 @@ void DrawEngineVulkan::DoFlush() {
|
||||
}
|
||||
_dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader");
|
||||
|
||||
Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS;
|
||||
VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, true);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, true);
|
||||
if (!pipeline || !pipeline->pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
return;
|
||||
@ -895,9 +893,7 @@ void DrawEngineVulkan::DoFlush() {
|
||||
}
|
||||
shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform
|
||||
_dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader");
|
||||
Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS;
|
||||
VkRenderPass renderPass = (VkRenderPass)draw_->GetNativeObject(object);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, renderPass, pipelineKey_, &dec_->decFmt, vshader, fshader, false);
|
||||
VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, false);
|
||||
if (!pipeline || !pipeline->pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
decodedVerts_ = 0;
|
||||
|
@ -35,16 +35,20 @@ void PipelineManagerVulkan::Clear() {
|
||||
// store the keys.
|
||||
|
||||
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
|
||||
if (value->pipeline) {
|
||||
VkPipeline pipeline = value->pipeline->pipeline->BlockUntilReady();
|
||||
vulkan_->Delete().QueueDeletePipeline(pipeline);
|
||||
vulkan_->Delete().QueueCallback([](void *p) {
|
||||
VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p;
|
||||
delete pipeline;
|
||||
}, value->pipeline);
|
||||
} else {
|
||||
// Something went wrong.
|
||||
ERROR_LOG(G3D, "Null pipeline found in PipelineManagerVulkan::Clear - didn't wait for asyncs?");
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
if (value->pipeline) {
|
||||
if (value->pipeline->pipeline[i]) {
|
||||
VkPipeline pipeline = value->pipeline->pipeline[i]->BlockUntilReady();
|
||||
vulkan_->Delete().QueueDeletePipeline(pipeline);
|
||||
vulkan_->Delete().QueueCallback([](void *p) {
|
||||
VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p;
|
||||
delete pipeline;
|
||||
}, value->pipeline);
|
||||
}
|
||||
} else {
|
||||
// Something went wrong.
|
||||
ERROR_LOG(G3D, "Null pipeline found in PipelineManagerVulkan::Clear - didn't wait for asyncs?");
|
||||
}
|
||||
}
|
||||
delete value;
|
||||
});
|
||||
@ -175,7 +179,7 @@ static std::string CutFromMain(std::string str) {
|
||||
}
|
||||
|
||||
static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache,
|
||||
VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &key,
|
||||
VkPipelineLayout layout, const VulkanPipelineRasterStateKey &key,
|
||||
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform) {
|
||||
VKRGraphicsPipelineDesc *desc = new VKRGraphicsPipelineDesc();
|
||||
desc->pipelineCache = pipelineCache;
|
||||
@ -298,27 +302,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
|
||||
views.pViewports = nullptr; // dynamic
|
||||
views.pScissors = nullptr; // dynamic
|
||||
|
||||
VkGraphicsPipelineCreateInfo &pipe = desc->pipe;
|
||||
pipe.flags = 0;
|
||||
pipe.stageCount = 2;
|
||||
pipe.basePipelineIndex = 0;
|
||||
|
||||
pipe.pColorBlendState = &desc->cbs;
|
||||
pipe.pDepthStencilState = &desc->dss;
|
||||
pipe.pRasterizationState = &desc->rs;
|
||||
|
||||
// We will use dynamic viewport state.
|
||||
pipe.pVertexInputState = &desc->vis;
|
||||
pipe.pViewportState = &desc->views;
|
||||
pipe.pTessellationState = nullptr;
|
||||
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;
|
||||
desc->pipelineLayout = layout;
|
||||
|
||||
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc);
|
||||
|
||||
@ -335,7 +319,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
|
||||
return vulkanPipeline;
|
||||
}
|
||||
|
||||
VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, VkRenderPass renderPass, 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) {
|
||||
if (!pipelineCache_) {
|
||||
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
||||
VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
|
||||
@ -343,10 +327,8 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *
|
||||
}
|
||||
|
||||
VulkanPipelineKey key{};
|
||||
_assert_msg_(renderPass, "Can't create a pipeline with a null renderpass");
|
||||
|
||||
key.raster = rasterKey;
|
||||
key.renderPass = renderPass;
|
||||
key.useHWTransform = useHwTransform;
|
||||
key.vShader = vs->GetModule();
|
||||
key.fShader = fs->GetModule();
|
||||
@ -357,7 +339,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *
|
||||
return iter;
|
||||
|
||||
VulkanPipeline *pipeline = CreateVulkanPipeline(
|
||||
renderManager, pipelineCache_, layout, renderPass,
|
||||
renderManager, pipelineCache_, layout,
|
||||
rasterKey, decFmt, vs, fs, useHwTransform);
|
||||
pipelines_.Insert(key, pipeline);
|
||||
|
||||
@ -565,8 +547,6 @@ struct StoredVulkanPipelineKey {
|
||||
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 {
|
||||
@ -627,14 +607,6 @@ 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);
|
||||
});
|
||||
|
||||
@ -740,18 +712,9 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha
|
||||
continue;
|
||||
}
|
||||
|
||||
VkRenderPass rp;
|
||||
if (key.backbufferPass) {
|
||||
rp = queueRunner->GetBackbufferRenderPass();
|
||||
} else {
|
||||
rp = queueRunner->GetRenderPass(key.renderPassKey);
|
||||
}
|
||||
|
||||
DecVtxFormat fmt;
|
||||
fmt.InitializeFromID(key.vtxFmtId);
|
||||
VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, rp, 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);
|
||||
if (!pipeline) {
|
||||
pipelineCreateFailCount += 1;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class VulkanRenderManager;
|
||||
|
||||
struct VulkanPipelineKey {
|
||||
VulkanPipelineRasterStateKey raster; // prim is included here
|
||||
VkRenderPass renderPass;
|
||||
VKRRenderPass *renderPass;
|
||||
Promise<VkShaderModule> *vShader;
|
||||
Promise<VkShaderModule> *fShader;
|
||||
uint32_t vtxFmtId;
|
||||
@ -71,7 +71,7 @@ public:
|
||||
PipelineManagerVulkan(VulkanContext *ctx);
|
||||
~PipelineManagerVulkan();
|
||||
|
||||
VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, VkRenderPass renderPass, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform);
|
||||
VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform);
|
||||
int GetNumPipelines() const { return (int)pipelines_.size(); }
|
||||
|
||||
void Clear();
|
||||
|
@ -89,8 +89,8 @@ Promise<VkShaderModule> *CompileShaderModuleAsync(VulkanContext *vulkan, VkShade
|
||||
}
|
||||
|
||||
|
||||
VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, const char *code)
|
||||
: vulkan_(vulkan), id_(id) {
|
||||
VulkanFragmentShader::VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, FragmentShaderFlags flags, const char *code)
|
||||
: vulkan_(vulkan), id_(id), flags_(flags) {
|
||||
source_ = code;
|
||||
module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_FRAGMENT_BIT, source_.c_str());
|
||||
if (!module_) {
|
||||
@ -276,9 +276,10 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader
|
||||
// Fragment shader not in cache. Let's compile it.
|
||||
std::string genErrorString;
|
||||
uint64_t uniformMask = 0; // Not used
|
||||
bool success = GenerateFragmentShader(FSID, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &genErrorString);
|
||||
FragmentShaderFlags flags;
|
||||
bool success = GenerateFragmentShader(FSID, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &flags, &genErrorString);
|
||||
_assert_msg_(success, "FS gen error: %s", genErrorString.c_str());
|
||||
fs = new VulkanFragmentShader(vulkan, FSID, codeBuffer_);
|
||||
fs = new VulkanFragmentShader(vulkan, FSID, flags, codeBuffer_);
|
||||
fsCache_.Insert(FSID, fs);
|
||||
}
|
||||
|
||||
@ -370,7 +371,7 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM
|
||||
// instantaneous.
|
||||
|
||||
#define CACHE_HEADER_MAGIC 0xff51f420
|
||||
#define CACHE_VERSION 22
|
||||
#define CACHE_VERSION 24
|
||||
struct VulkanCacheHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
@ -390,6 +391,10 @@ 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;
|
||||
@ -417,10 +422,11 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) {
|
||||
}
|
||||
std::string genErrorString;
|
||||
uint64_t uniformMask = 0;
|
||||
if (!GenerateFragmentShader(id, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &genErrorString)) {
|
||||
FragmentShaderFlags flags;
|
||||
if (!GenerateFragmentShader(id, codeBuffer_, compat_, draw_->GetBugs(), &uniformMask, &flags, &genErrorString)) {
|
||||
return false;
|
||||
}
|
||||
VulkanFragmentShader *fs = new VulkanFragmentShader(vulkan, id, codeBuffer_);
|
||||
VulkanFragmentShader *fs = new VulkanFragmentShader(vulkan, id, flags, codeBuffer_);
|
||||
fsCache_.Insert(id, fs);
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ class VulkanPushBuffer;
|
||||
|
||||
class VulkanFragmentShader {
|
||||
public:
|
||||
VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, const char *code);
|
||||
VulkanFragmentShader(VulkanContext *vulkan, FShaderID id, FragmentShaderFlags flags, const char *code);
|
||||
~VulkanFragmentShader();
|
||||
|
||||
const std::string &source() const { return source_; }
|
||||
@ -45,7 +45,9 @@ public:
|
||||
|
||||
std::string GetShaderString(DebugShaderStringType type) const;
|
||||
Promise<VkShaderModule> *GetModule() { return module_; }
|
||||
const FShaderID &GetID() { return id_; }
|
||||
const FShaderID &GetID() const { return id_; }
|
||||
|
||||
FragmentShaderFlags Flags() const { return flags_; }
|
||||
|
||||
protected:
|
||||
Promise<VkShaderModule> *module_ = nullptr;
|
||||
@ -54,6 +56,7 @@ protected:
|
||||
std::string source_;
|
||||
bool failed_ = false;
|
||||
FShaderID id_;
|
||||
FragmentShaderFlags flags_;
|
||||
};
|
||||
|
||||
class VulkanVertexShader {
|
||||
@ -68,7 +71,7 @@ public:
|
||||
|
||||
std::string GetShaderString(DebugShaderStringType type) const;
|
||||
Promise<VkShaderModule> *GetModule() { return module_; }
|
||||
const VShaderID &GetID() { return id_; }
|
||||
const VShaderID &GetID() const { return id_; }
|
||||
|
||||
protected:
|
||||
Promise<VkShaderModule> *module_ = nullptr;
|
||||
|
@ -30,27 +30,27 @@ bool GenerateFShader(FShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs
|
||||
case ShaderLanguage::GLSL_VULKAN:
|
||||
{
|
||||
ShaderLanguageDesc compat(ShaderLanguage::GLSL_VULKAN);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString);
|
||||
}
|
||||
case ShaderLanguage::GLSL_1xx:
|
||||
{
|
||||
ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString);
|
||||
}
|
||||
case ShaderLanguage::GLSL_3xx:
|
||||
{
|
||||
ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString);
|
||||
}
|
||||
case ShaderLanguage::HLSL_D3D9:
|
||||
{
|
||||
ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D9);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString);
|
||||
}
|
||||
case ShaderLanguage::HLSL_D3D11:
|
||||
{
|
||||
ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D11);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
|
||||
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, nullptr, errorString);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user