mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-24 14:00:03 +00:00
Merge branch 'hrydgard:master' into compat_openxr_gta
This commit is contained in:
commit
5be9455bf5
@ -127,6 +127,16 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note! Does NOT delete any pointed-to data (in case you stored pointers in the map).
|
||||
void Clear() {
|
||||
memset(state.data(), (int)BucketState::FREE, state.size());
|
||||
count_ = 0;
|
||||
|
@ -82,9 +82,9 @@ public:
|
||||
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
||||
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
||||
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;
|
||||
Texture *CreateTexture(const TextureDesc &desc) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;
|
||||
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
|
||||
|
||||
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
|
||||
@ -920,7 +920,7 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
|
||||
return tex;
|
||||
}
|
||||
|
||||
ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) {
|
||||
ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) {
|
||||
if (language != ShaderLanguage::HLSL_D3D11) {
|
||||
ERROR_LOG(G3D, "Unsupported shader language");
|
||||
return nullptr;
|
||||
@ -965,7 +965,7 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang
|
||||
}
|
||||
if (errorMsgs) {
|
||||
errors = std::string((const char *)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize());
|
||||
ERROR_LOG(G3D, "Failed compiling:\n%s\n%s", data, errors.c_str());
|
||||
ERROR_LOG(G3D, "Failed compiling %s:\n%s\n%s", tag, data, errors.c_str());
|
||||
errorMsgs->Release();
|
||||
}
|
||||
|
||||
@ -1003,7 +1003,7 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
|
||||
D3D11Pipeline *dPipeline = new D3D11Pipeline();
|
||||
dPipeline->blend = (D3D11BlendState *)desc.blend;
|
||||
dPipeline->depthStencil = (D3D11DepthStencilState *)desc.depthStencil;
|
||||
|
@ -489,13 +489,13 @@ public:
|
||||
}
|
||||
uint32_t GetDataFormatSupport(DataFormat fmt) const override;
|
||||
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;
|
||||
DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override;
|
||||
BlendState *CreateBlendState(const BlendStateDesc &desc) override;
|
||||
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
||||
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
||||
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;
|
||||
InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;
|
||||
Texture *CreateTexture(const TextureDesc &desc) override;
|
||||
|
||||
@ -692,7 +692,7 @@ D3D9Context::D3D9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, ID
|
||||
D3D9Context::~D3D9Context() {
|
||||
}
|
||||
|
||||
ShaderModule *D3D9Context::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const std::string &tag) {
|
||||
ShaderModule *D3D9Context::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const char *tag) {
|
||||
D3D9ShaderModule *shader = new D3D9ShaderModule(stage, tag);
|
||||
if (shader->Compile(device_, data, size)) {
|
||||
return shader;
|
||||
@ -702,15 +702,15 @@ ShaderModule *D3D9Context::CreateShaderModule(ShaderStage stage, ShaderLanguage
|
||||
}
|
||||
}
|
||||
|
||||
Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
|
||||
if (!desc.shaders.size()) {
|
||||
ERROR_LOG(G3D, "Pipeline requires at least one shader");
|
||||
ERROR_LOG(G3D, "Pipeline %s requires at least one shader", tag);
|
||||
return NULL;
|
||||
}
|
||||
D3D9Pipeline *pipeline = new D3D9Pipeline();
|
||||
for (auto iter : desc.shaders) {
|
||||
if (!iter) {
|
||||
ERROR_LOG(G3D, "NULL shader passed to CreateGraphicsPipeline");
|
||||
ERROR_LOG(G3D, "NULL shader passed to CreateGraphicsPipeline(%s)", tag);
|
||||
delete pipeline;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -336,9 +336,9 @@ public:
|
||||
BlendState *CreateBlendState(const BlendStateDesc &desc) override;
|
||||
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
||||
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;
|
||||
InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;
|
||||
|
||||
Texture *CreateTexture(const TextureDesc &desc) override;
|
||||
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
||||
@ -1079,7 +1079,7 @@ void OpenGLContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t off
|
||||
renderManager_.BufferSubdata(buf->buffer_, offset, size, dataCopy);
|
||||
}
|
||||
|
||||
Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
|
||||
if (!desc.shaders.size()) {
|
||||
ERROR_LOG(G3D, "Pipeline requires at least one shader");
|
||||
return nullptr;
|
||||
@ -1099,7 +1099,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
iter->AddRef();
|
||||
pipeline->shaders.push_back(static_cast<OpenGLShaderModule *>(iter));
|
||||
} else {
|
||||
ERROR_LOG(G3D, "ERROR: Tried to create graphics pipeline with a null shader module");
|
||||
ERROR_LOG(G3D, "ERROR: Tried to create graphics pipeline %s with a null shader module", tag);
|
||||
delete pipeline;
|
||||
return nullptr;
|
||||
}
|
||||
@ -1118,7 +1118,7 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
pipeline->inputLayout = (OpenGLInputLayout *)desc.inputLayout;
|
||||
return pipeline;
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Failed to create pipeline - shaders failed to link");
|
||||
ERROR_LOG(G3D, "Failed to create pipeline %s - shaders failed to link", tag);
|
||||
delete pipeline;
|
||||
return nullptr;
|
||||
}
|
||||
@ -1163,7 +1163,7 @@ void OpenGLContext::ApplySamplers() {
|
||||
}
|
||||
}
|
||||
|
||||
ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) {
|
||||
ShaderModule *OpenGLContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) {
|
||||
OpenGLShaderModule *shader = new OpenGLShaderModule(&renderManager_, stage, tag);
|
||||
if (shader->Compile(&renderManager_, language, data, dataSize)) {
|
||||
return shader;
|
||||
|
@ -1196,12 +1196,15 @@ void VulkanContext::DestroyDevice() {
|
||||
device_ = nullptr;
|
||||
}
|
||||
|
||||
bool VulkanContext::CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule) {
|
||||
bool VulkanContext::CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule, const char *tag) {
|
||||
VkShaderModuleCreateInfo sm{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
||||
sm.pCode = spirv.data();
|
||||
sm.codeSize = spirv.size() * sizeof(uint32_t);
|
||||
sm.flags = 0;
|
||||
VkResult result = vkCreateShaderModule(device_, &sm, nullptr, shaderModule);
|
||||
if (tag) {
|
||||
SetDebugName(*shaderModule, VK_OBJECT_TYPE_SHADER_MODULE, tag);
|
||||
}
|
||||
if (result != VK_SUCCESS) {
|
||||
return false;
|
||||
} else {
|
||||
|
@ -201,7 +201,7 @@ public:
|
||||
|
||||
// Utility functions for shorter code
|
||||
VkFence CreateFence(bool presignalled);
|
||||
bool CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule);
|
||||
bool CreateShaderModule(const std::vector<uint32_t> &spirv, VkShaderModule *shaderModule, const char *tag);
|
||||
|
||||
int GetBackbufferWidth() { return (int)swapChainExtent_.width; }
|
||||
int GetBackbufferHeight() { return (int)swapChainExtent_.height; }
|
||||
|
@ -59,6 +59,8 @@ bool VulkanPushBuffer::AddBuffer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
vulkan_->SetDebugName(info.buffer, VK_OBJECT_TYPE_BUFFER, name_);
|
||||
|
||||
buffers_.push_back(info);
|
||||
buf_ = buffers_.size() - 1;
|
||||
return true;
|
||||
@ -222,5 +224,9 @@ VkResult VulkanDescSetPool::Recreate(bool grow) {
|
||||
info_.pPoolSizes = &sizes_[0];
|
||||
info_.poolSizeCount = (uint32_t)sizes_.size();
|
||||
|
||||
return vkCreateDescriptorPool(vulkan_->GetDevice(), &info_, nullptr, &descPool_);
|
||||
VkResult result = vkCreateDescriptorPool(vulkan_->GetDevice(), &info_, nullptr, &descPool_);
|
||||
if (result == VK_SUCCESS) {
|
||||
vulkan_->SetDebugName(descPool_, VK_OBJECT_TYPE_DESCRIPTOR_POOL, tag_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -143,8 +143,7 @@ private:
|
||||
// Only appropriate for use in a per-frame pool.
|
||||
class VulkanDescSetPool {
|
||||
public:
|
||||
VulkanDescSetPool(const char *tag, bool grow) : tag_(tag), grow_(grow) {
|
||||
}
|
||||
VulkanDescSetPool(const char *tag, bool grow) : tag_(tag), grow_(grow) {}
|
||||
~VulkanDescSetPool();
|
||||
|
||||
// Must call this before use: defines how to clear cache of ANY returned values from Allocate().
|
||||
|
@ -27,13 +27,26 @@ static void MergeRenderAreaRectInto(VkRect2D *dest, VkRect2D &src) {
|
||||
}
|
||||
}
|
||||
|
||||
// We need to take the "max" of the features used in the two render passes.
|
||||
RenderPassType MergeRPTypes(RenderPassType a, RenderPassType b) {
|
||||
// Either both are backbuffer type, or neither are.
|
||||
_dbg_assert_((a == RP_TYPE_BACKBUFFER) == (b == RP_TYPE_BACKBUFFER));
|
||||
if (a == b) {
|
||||
// Trivial merging case.
|
||||
return a;
|
||||
}
|
||||
// More cases to be added later.
|
||||
return a;
|
||||
}
|
||||
|
||||
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 +130,12 @@ 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);
|
||||
rp->Destroy(vulkan_);
|
||||
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,24 +155,19 @@ 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 CreateRP(VulkanContext *vulkan, 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;
|
||||
attachments[1].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
|
||||
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attachments[1].loadOp = ConvertLoadAction(key.depthLoadAction);
|
||||
attachments[1].storeOp = ConvertStoreAction(key.depthStoreAction);
|
||||
@ -255,15 +197,51 @@ 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;
|
||||
}
|
||||
|
||||
VkResult res = vkCreateRenderPass(vulkan_->GetDevice(), &rp, nullptr, &pass);
|
||||
VkRenderPass pass;
|
||||
VkResult res = vkCreateRenderPass(vulkan->GetDevice(), &rp, nullptr, &pass);
|
||||
_assert_(res == VK_SUCCESS);
|
||||
_assert_(pass != VK_NULL_HANDLE);
|
||||
return pass;
|
||||
}
|
||||
|
||||
VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType) {
|
||||
// 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.
|
||||
if (!pass[(int)rpType]) {
|
||||
pass[(int)rpType] = CreateRP(vulkan, key_, (RenderPassType)rpType);
|
||||
}
|
||||
return pass[(int)rpType];
|
||||
}
|
||||
|
||||
// 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(key);
|
||||
renderPasses_.Insert(key, pass);
|
||||
return pass;
|
||||
}
|
||||
@ -272,7 +250,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 &&
|
||||
@ -312,7 +291,7 @@ void VulkanQueueRunner::PreprocessSteps(std::vector<VKRStep *> &steps) {
|
||||
steps[i]->render.clearStencil = steps[j]->render.clearStencil;
|
||||
}
|
||||
MergeRenderAreaRectInto(&steps[i]->render.renderArea, steps[j]->render.renderArea);
|
||||
|
||||
steps[i]->render.renderPassType = MergeRPTypes(steps[i]->render.renderPassType, steps[j]->render.renderPassType);
|
||||
// Cheaply skip the first step.
|
||||
steps[j]->stepType = VKRStepType::RENDER_SKIP;
|
||||
break;
|
||||
@ -645,17 +624,23 @@ std::string VulkanQueueRunner::StepToString(const VKRStep &step) const {
|
||||
int h = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight();
|
||||
int actual_w = step.render.renderArea.extent.width;
|
||||
int actual_h = step.render.renderArea.extent.height;
|
||||
snprintf(buffer, sizeof(buffer), "RENDER %s (draws: %d, %dx%d/%dx%d, fb: %p, )", step.tag, step.render.numDraws, actual_w, actual_h, w, h, step.render.framebuffer);
|
||||
const char *renderCmd;
|
||||
switch (step.render.renderPassType) {
|
||||
case RP_TYPE_BACKBUFFER: renderCmd = "BACKBUF"; break;
|
||||
case RP_TYPE_COLOR_DEPTH: renderCmd = "RENDER"; break;
|
||||
default: renderCmd = "N/A";
|
||||
}
|
||||
snprintf(buffer, sizeof(buffer), "%s %s (draws: %d, %dx%d/%dx%d, fb: %p, )", renderCmd, step.tag, step.render.numDraws, actual_w, actual_h, w, h, step.render.framebuffer);
|
||||
break;
|
||||
}
|
||||
case VKRStepType::COPY:
|
||||
snprintf(buffer, sizeof(buffer), "COPY '%s' %s -> %s (%dx%d, %s)", step.tag, step.copy.src->tag.c_str(), step.copy.dst->tag.c_str(), step.copy.srcRect.extent.width, step.copy.srcRect.extent.height, AspectToString(step.copy.aspectMask));
|
||||
snprintf(buffer, sizeof(buffer), "COPY '%s' %s -> %s (%dx%d, %s)", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.copy.srcRect.extent.width, step.copy.srcRect.extent.height, AspectToString(step.copy.aspectMask));
|
||||
break;
|
||||
case VKRStepType::BLIT:
|
||||
snprintf(buffer, sizeof(buffer), "BLIT '%s' %s -> %s (%dx%d->%dx%d, %s)", step.tag, step.copy.src->tag.c_str(), step.copy.dst->tag.c_str(), step.blit.srcRect.extent.width, step.blit.srcRect.extent.height, step.blit.dstRect.extent.width, step.blit.dstRect.extent.height, AspectToString(step.blit.aspectMask));
|
||||
snprintf(buffer, sizeof(buffer), "BLIT '%s' %s -> %s (%dx%d->%dx%d, %s)", step.tag, step.copy.src->Tag(), step.copy.dst->Tag(), step.blit.srcRect.extent.width, step.blit.srcRect.extent.height, step.blit.dstRect.extent.width, step.blit.dstRect.extent.height, AspectToString(step.blit.aspectMask));
|
||||
break;
|
||||
case VKRStepType::READBACK:
|
||||
snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)", step.tag, step.readback.src->tag.c_str(), step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, AspectToString(step.readback.aspectMask));
|
||||
snprintf(buffer, sizeof(buffer), "READBACK '%s' %s (%dx%d, %s)", step.tag, step.readback.src->Tag(), step.readback.srcRect.extent.width, step.readback.srcRect.extent.height, AspectToString(step.readback.aspectMask));
|
||||
break;
|
||||
case VKRStepType::READBACK_IMAGE:
|
||||
snprintf(buffer, sizeof(buffer), "READBACK_IMAGE '%s' (%dx%d)", step.tag, step.readback_image.srcRect.extent.width, step.readback_image.srcRect.extent.height);
|
||||
@ -692,6 +677,8 @@ void VulkanQueueRunner::ApplyRenderPassMerge(std::vector<VKRStep *> &steps) {
|
||||
// So we don't consider it for other things, maybe doesn't matter.
|
||||
src->dependencies.clear();
|
||||
src->stepType = VKRStepType::RENDER_SKIP;
|
||||
dst->render.pipelineFlags |= src->render.pipelineFlags;
|
||||
dst->render.renderPassType = MergeRPTypes(dst->render.renderPassType, src->render.renderPassType);
|
||||
};
|
||||
auto renderHasClear = [](const VKRStep *step) {
|
||||
const auto &r = step->render;
|
||||
@ -814,14 +801,14 @@ const char *ImageLayoutToString(VkImageLayout layout) {
|
||||
|
||||
void VulkanQueueRunner::LogRenderPass(const VKRStep &pass, bool verbose) {
|
||||
const auto &r = pass.render;
|
||||
const char *framebuf = r.framebuffer ? r.framebuffer->tag.c_str() : "backbuffer";
|
||||
const char *framebuf = r.framebuffer ? r.framebuffer->Tag() : "backbuffer";
|
||||
int w = r.framebuffer ? r.framebuffer->width : vulkan_->GetBackbufferWidth();
|
||||
int h = r.framebuffer ? r.framebuffer->height : vulkan_->GetBackbufferHeight();
|
||||
|
||||
INFO_LOG(G3D, "RENDER %s Begin(%s, draws: %d, %dx%d, %s, %s, %s)", pass.tag, framebuf, r.numDraws, w, h, RenderPassActionName(r.colorLoad), RenderPassActionName(r.depthLoad), RenderPassActionName(r.stencilLoad));
|
||||
// TODO: Log these in detail.
|
||||
for (int i = 0; i < (int)pass.preTransitions.size(); i++) {
|
||||
INFO_LOG(G3D, " PRETRANSITION: %s %s -> %s", pass.preTransitions[i].fb->tag.c_str(), AspectToString(pass.preTransitions[i].aspect), ImageLayoutToString(pass.preTransitions[i].targetLayout));
|
||||
INFO_LOG(G3D, " PRETRANSITION: %s %s -> %s", pass.preTransitions[i].fb->Tag(), AspectToString(pass.preTransitions[i].aspect), ImageLayoutToString(pass.preTransitions[i].targetLayout));
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
@ -830,9 +817,6 @@ 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);
|
||||
break;
|
||||
case VKRRenderCommand::BIND_GRAPHICS_PIPELINE:
|
||||
INFO_LOG(G3D, " BindGraphicsPipeline(%x)", (int)(intptr_t)cmd.graphics_pipeline.pipeline);
|
||||
break;
|
||||
@ -1137,7 +1121,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
// This reads the layout of the color and depth images, and chooses a render pass using them that
|
||||
// will transition to the desired final layout.
|
||||
// NOTE: Flushes recordBarrier_.
|
||||
PerformBindFramebufferAsRenderTarget(step, cmd);
|
||||
VKRRenderPass *renderPass = PerformBindFramebufferAsRenderTarget(step, cmd);
|
||||
|
||||
int curWidth = step.render.framebuffer ? step.render.framebuffer->width : vulkan_->GetBackbufferWidth();
|
||||
int curHeight = step.render.framebuffer ? step.render.framebuffer->height : vulkan_->GetBackbufferHeight();
|
||||
@ -1159,29 +1143,28 @@ 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:
|
||||
break;
|
||||
|
||||
// Still here to support binding of non-async pipelines.
|
||||
case VKRRenderCommand::BIND_PIPELINE:
|
||||
{
|
||||
VkPipeline pipeline = c.pipeline.pipeline;
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
pipelineLayout = c.pipeline.pipelineLayout;
|
||||
// Reset dynamic state so it gets refreshed with the new pipeline.
|
||||
lastStencilWriteMask = -1;
|
||||
lastStencilCompareMask = -1;
|
||||
lastStencilReference = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
case VKRRenderCommand::BIND_GRAPHICS_PIPELINE:
|
||||
{
|
||||
VKRGraphicsPipeline *graphicsPipeline = c.graphics_pipeline.pipeline;
|
||||
if (graphicsPipeline != lastGraphicsPipeline) {
|
||||
VkPipeline pipeline = graphicsPipeline->pipeline->BlockUntilReady();
|
||||
if (!graphicsPipeline->pipeline[rpType]) {
|
||||
// NOTE: If render steps got merged, it can happen that, as they ended during recording,
|
||||
// they didn't know their final render pass type so they created the wrong pipelines in EndCurRenderStep().
|
||||
// Unfortunately I don't know if we can fix it in any more sensible place than here.
|
||||
// Maybe a middle pass. But let's try to just block and compile here for now, this doesn't
|
||||
// happen all that much.
|
||||
graphicsPipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
|
||||
graphicsPipeline->Create(vulkan_, renderPass->Get(vulkan_, rpType), rpType);
|
||||
}
|
||||
|
||||
VkPipeline pipeline = graphicsPipeline->pipeline[rpType]->BlockUntilReady();
|
||||
if (pipeline != VK_NULL_HANDLE) {
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
pipelineLayout = c.pipeline.pipelineLayout;
|
||||
@ -1337,8 +1320,8 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
VkRenderPass renderPass;
|
||||
VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
VKRRenderPass *renderPass;
|
||||
int numClearVals = 0;
|
||||
VkClearValue clearVal[2]{};
|
||||
VkFramebuffer framebuf;
|
||||
@ -1348,8 +1331,14 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
|
||||
_dbg_assert_(step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
_dbg_assert_(step.render.finalDepthStencilLayout != VK_IMAGE_LAYOUT_UNDEFINED);
|
||||
|
||||
RPKey key{
|
||||
step.render.colorLoad, step.render.depthLoad, step.render.stencilLoad,
|
||||
step.render.colorStore, step.render.depthStore, step.render.stencilStore,
|
||||
};
|
||||
renderPass = GetRenderPass(key);
|
||||
|
||||
VKRFramebuffer *fb = step.render.framebuffer;
|
||||
framebuf = fb->framebuf;
|
||||
framebuf = fb->Get(renderPass, step.render.renderPassType);
|
||||
w = fb->width;
|
||||
h = fb->height;
|
||||
|
||||
@ -1371,12 +1360,6 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
|
||||
|
||||
TransitionToOptimal(cmd, fb->color.image, fb->color.layout, fb->depth.image, fb->depth.layout, &recordBarrier_);
|
||||
|
||||
RPKey key{
|
||||
step.render.colorLoad, step.render.depthLoad, step.render.stencilLoad,
|
||||
step.render.colorStore, step.render.depthStore, step.render.stencilStore,
|
||||
};
|
||||
renderPass = GetRenderPass(key);
|
||||
|
||||
// The transition from the optimal format happens after EndRenderPass, now that we don't
|
||||
// do it as part of the renderpass itself anymore.
|
||||
|
||||
@ -1390,12 +1373,18 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
|
||||
numClearVals = 2;
|
||||
}
|
||||
} else {
|
||||
RPKey key{
|
||||
VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR,
|
||||
VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE,
|
||||
};
|
||||
renderPass = GetRenderPass(key);
|
||||
|
||||
framebuf = backbuffer_;
|
||||
|
||||
// Raw, rotated backbuffer size.
|
||||
w = vulkan_->GetBackbufferWidth();
|
||||
h = vulkan_->GetBackbufferHeight();
|
||||
renderPass = GetBackbufferRenderPass();
|
||||
|
||||
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 +1392,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->Get(vulkan_, step.render.renderPassType);
|
||||
rp_begin.framebuffer = framebuf;
|
||||
|
||||
VkRect2D rc = step.render.renderArea;
|
||||
@ -1424,6 +1413,8 @@ void VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKRStep &step
|
||||
rp_begin.clearValueCount = numClearVals;
|
||||
rp_begin.pClearValues = numClearVals ? clearVal : nullptr;
|
||||
vkCmdBeginRenderPass(cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
return renderPass;
|
||||
}
|
||||
|
||||
void VulkanQueueRunner::PerformCopy(const VKRStep &step, VkCommandBuffer cmd) {
|
||||
|
@ -26,7 +26,6 @@ enum {
|
||||
|
||||
enum class VKRRenderCommand : uint8_t {
|
||||
REMOVED,
|
||||
BIND_PIPELINE, // raw pipeline
|
||||
BIND_GRAPHICS_PIPELINE, // async
|
||||
BIND_COMPUTE_PIPELINE, // async
|
||||
STENCIL,
|
||||
@ -47,6 +46,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 +154,8 @@ struct QueueProfileContext {
|
||||
double cpuEndTime;
|
||||
};
|
||||
|
||||
class VKRRenderPass;
|
||||
|
||||
struct VKRStep {
|
||||
VKRStep(VKRStepType _type) : stepType(_type) {}
|
||||
~VKRStep() {}
|
||||
@ -159,6 +168,7 @@ struct VKRStep {
|
||||
union {
|
||||
struct {
|
||||
VKRFramebuffer *framebuffer;
|
||||
// TODO: Look these up through renderPass?
|
||||
VKRRenderPassLoadAction colorLoad;
|
||||
VKRRenderPassLoadAction depthLoad;
|
||||
VKRRenderPassLoadAction stencilLoad;
|
||||
@ -175,6 +185,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 +217,38 @@ struct VKRStep {
|
||||
};
|
||||
};
|
||||
|
||||
struct RPKey {
|
||||
// Only render-pass-compatibility-volatile things can be here.
|
||||
VKRRenderPassLoadAction colorLoadAction;
|
||||
VKRRenderPassLoadAction depthLoadAction;
|
||||
VKRRenderPassLoadAction stencilLoadAction;
|
||||
VKRRenderPassStoreAction colorStoreAction;
|
||||
VKRRenderPassStoreAction depthStoreAction;
|
||||
VKRRenderPassStoreAction stencilStoreAction;
|
||||
};
|
||||
|
||||
class VKRRenderPass {
|
||||
public:
|
||||
VKRRenderPass(const RPKey &key) : key_(key) {}
|
||||
|
||||
VkRenderPass Get(VulkanContext *vulkan, RenderPassType rpType);
|
||||
void Destroy(VulkanContext *vulkan) {
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
if (pass[i]) {
|
||||
vulkan->Delete().QueueDeleteRenderPass(pass[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
VkRenderPass pass[RP_TYPE_COUNT]{};
|
||||
RPKey key_;
|
||||
};
|
||||
|
||||
class VulkanQueueRunner {
|
||||
public:
|
||||
VulkanQueueRunner(VulkanContext *vulkan) : vulkan_(vulkan), renderPasses_(16) {}
|
||||
|
||||
void SetBackbuffer(VkFramebuffer fb, VkImage img) {
|
||||
backbuffer_ = fb;
|
||||
backbufferImage_ = img;
|
||||
@ -221,14 +263,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 +275,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,9 +302,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void InitBackbufferRenderPass();
|
||||
|
||||
void PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
VKRRenderPass *PerformBindFramebufferAsRenderTarget(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformRenderPass(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformCopy(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
void PerformBlit(const VKRStep &pass, VkCommandBuffer cmd);
|
||||
@ -302,14 +329,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,12 +25,8 @@
|
||||
|
||||
using namespace PPSSPP_VK;
|
||||
|
||||
bool VKRGraphicsPipeline::Create(VulkanContext *vulkan) {
|
||||
if (!desc) {
|
||||
// Already failed to create this one.
|
||||
return false;
|
||||
}
|
||||
|
||||
// renderPass is an example of the "compatibility class" or RenderPassType type.
|
||||
bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType) {
|
||||
// Fill in the last part of the desc since now it's time to block.
|
||||
VkShaderModule vs = desc->vertexShader->BlockUntilReady();
|
||||
VkShaderModule fs = desc->fragmentShader->BlockUntilReady();
|
||||
@ -55,12 +51,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,24 +84,46 @@ 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);
|
||||
// Success!
|
||||
if (!tag.empty()) {
|
||||
vulkan->SetDebugName(vkpipeline, VK_OBJECT_TYPE_PIPELINE, tag.c_str());
|
||||
}
|
||||
pipeline[rpType]->Post(vkpipeline);
|
||||
}
|
||||
|
||||
// Having the desc stick around can be useful for debugging.
|
||||
#ifndef _DEBUG
|
||||
delete desc;
|
||||
desc = nullptr;
|
||||
#endif
|
||||
return success;
|
||||
}
|
||||
|
||||
void VKRGraphicsPipeline::QueueForDeletion(VulkanContext *vulkan) {
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
if (!pipeline[i])
|
||||
continue;
|
||||
VkPipeline pipeline = this->pipeline[i]->BlockUntilReady();
|
||||
vulkan->Delete().QueueDeletePipeline(pipeline);
|
||||
}
|
||||
vulkan->Delete().QueueCallback([](void *p) {
|
||||
VKRGraphicsPipeline *pipeline = (VKRGraphicsPipeline *)p;
|
||||
delete pipeline;
|
||||
}, this);
|
||||
}
|
||||
|
||||
u32 VKRGraphicsPipeline::GetVariantsBitmask() const {
|
||||
u32 bitmask = 0;
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
if (pipeline[i]) {
|
||||
bitmask |= 1 << i;
|
||||
}
|
||||
}
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
bool VKRComputePipeline::Create(VulkanContext *vulkan) {
|
||||
if (!desc) {
|
||||
// Already failed to create this one.
|
||||
@ -110,17 +146,25 @@ 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), tag_(tag) {
|
||||
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);
|
||||
|
||||
// We create the actual framebuffer objects on demand, because some combinations might not make sense.
|
||||
}
|
||||
|
||||
VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPassType renderPassType) {
|
||||
if (framebuf[(int)renderPassType]) {
|
||||
return framebuf[(int)renderPassType];
|
||||
}
|
||||
|
||||
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
VkImageView views[2]{};
|
||||
|
||||
fbci.renderPass = renderPass;
|
||||
fbci.renderPass = compatibleRenderPass->Get(vulkan_, renderPassType);
|
||||
fbci.attachmentCount = 2;
|
||||
fbci.pAttachments = views;
|
||||
views[0] = color.imageView;
|
||||
@ -129,15 +173,16 @@ VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VkRen
|
||||
fbci.height = height;
|
||||
fbci.layers = 1;
|
||||
|
||||
VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf);
|
||||
VkResult res = vkCreateFramebuffer(vulkan_->GetDevice(), &fbci, nullptr, &framebuf[(int)renderPassType]);
|
||||
_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_.empty() && vulkan_->Extensions().EXT_debug_utils) {
|
||||
vulkan_->SetDebugName(color.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_color_%s", tag_.c_str()).c_str());
|
||||
vulkan_->SetDebugName(depth.image, VK_OBJECT_TYPE_IMAGE, StringFromFormat("fb_depth_%s", tag_.c_str()).c_str());
|
||||
vulkan_->SetDebugName(framebuf[(int)renderPassType], VK_OBJECT_TYPE_FRAMEBUFFER, StringFromFormat("fb_%s", tag_.c_str()).c_str());
|
||||
}
|
||||
|
||||
return framebuf[(int)renderPassType];
|
||||
}
|
||||
|
||||
VKRFramebuffer::~VKRFramebuffer() {
|
||||
@ -155,8 +200,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 +540,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_);
|
||||
@ -576,7 +623,9 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile
|
||||
|
||||
VLOG("PUSH: Fencing %d", curFrame);
|
||||
|
||||
vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX);
|
||||
if (vkWaitForFences(device, 1, &frameData.fence, true, UINT64_MAX) == VK_ERROR_DEVICE_LOST) {
|
||||
_assert_msg_(false, "Device lost in vkWaitForFences");
|
||||
}
|
||||
vkResetFences(device, 1, &frameData.fence);
|
||||
|
||||
// Can't set this until after the fence.
|
||||
@ -663,24 +712,91 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() {
|
||||
return frameData_[curFrame].initCmd;
|
||||
}
|
||||
|
||||
VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, uint32_t variantBitmask, const char *tag) {
|
||||
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline();
|
||||
_dbg_assert_(desc->vertexShader);
|
||||
_dbg_assert_(desc->fragmentShader);
|
||||
pipeline->desc = desc;
|
||||
pipeline->tag = tag;
|
||||
if (curRenderStep_) {
|
||||
// The common case
|
||||
pipelinesToCheck_.push_back(pipeline);
|
||||
} else {
|
||||
if (!variantBitmask) {
|
||||
WARN_LOG(G3D, "WARNING: Will not compile any variants of pipeline, not in renderpass and empty variantBitmask");
|
||||
}
|
||||
// Presumably we're in initialization, loading the shader cache.
|
||||
// Look at variantBitmask to see what variants we should queue up.
|
||||
RPKey key{
|
||||
VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR, VKRRenderPassLoadAction::CLEAR,
|
||||
VKRRenderPassStoreAction::STORE, VKRRenderPassStoreAction::DONT_CARE, VKRRenderPassStoreAction::DONT_CARE,
|
||||
};
|
||||
VKRRenderPass *compatibleRenderPass = queueRunner_.GetRenderPass(key);
|
||||
compileMutex_.lock();
|
||||
for (int i = 0; i < RP_TYPE_COUNT; i++) {
|
||||
if (!(variantBitmask & (1 << i)))
|
||||
continue;
|
||||
RenderPassType rpType = (RenderPassType)i;
|
||||
pipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline, compatibleRenderPass->Get(vulkan_, rpType), rpType));
|
||||
}
|
||||
compileMutex_.unlock();
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
VKRComputePipeline *VulkanRenderManager::CreateComputePipeline(VKRComputePipelineDesc *desc) {
|
||||
VKRComputePipeline *pipeline = new VKRComputePipeline();
|
||||
pipeline->desc = desc;
|
||||
compileMutex_.lock();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline));
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void VulkanRenderManager::EndCurRenderStep() {
|
||||
if (!curRenderStep_)
|
||||
return;
|
||||
|
||||
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_ };
|
||||
compileMutex_.lock();
|
||||
for (VKRGraphicsPipeline *pipeline : pipelinesToCheck_) {
|
||||
if (!pipeline->pipeline[rpType]) {
|
||||
pipeline->pipeline[rpType] = Promise<VkPipeline>::CreateEmpty();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType), rpType));
|
||||
}
|
||||
curRenderArea_.Reset();
|
||||
|
||||
// We no longer have a current render step.
|
||||
curRenderStep_ = nullptr;
|
||||
curPipelineFlags_ = 0;
|
||||
}
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
pipelinesToCheck_.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) {
|
||||
@ -907,9 +1023,8 @@ bool VulkanRenderManager::InitBackbufferFramebuffers(int width, int height) {
|
||||
// We share the same depth buffer but have multiple color buffers, see the loop below.
|
||||
VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view };
|
||||
|
||||
VLOG("InitFramebuffers: %dx%d", width, height);
|
||||
VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
||||
fb_info.renderPass = queueRunner_.GetBackbufferRenderPass();
|
||||
fb_info.renderPass = queueRunner_.GetCompatibleRenderPass()->Get(vulkan_, RP_TYPE_BACKBUFFER);
|
||||
fb_info.attachmentCount = 2;
|
||||
fb_info.pAttachments = attachments;
|
||||
fb_info.width = width;
|
||||
@ -1017,7 +1132,6 @@ static void CleanupRenderCommands(std::vector<VkRenderData> *cmds) {
|
||||
case VKRRenderCommand::REMOVED:
|
||||
continue;
|
||||
|
||||
case VKRRenderCommand::BIND_PIPELINE:
|
||||
case VKRRenderCommand::VIEWPORT:
|
||||
case VKRRenderCommand::SCISSOR:
|
||||
case VKRRenderCommand::BLEND:
|
||||
|
@ -43,17 +43,26 @@ 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;
|
||||
VKRImage color{};
|
||||
VKRImage depth{};
|
||||
VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType);
|
||||
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
VKRImage color{};
|
||||
VKRImage depth{};
|
||||
|
||||
const char *Tag() const {
|
||||
return tag_.c_str();
|
||||
}
|
||||
|
||||
// TODO: Hide.
|
||||
VulkanContext *vulkan_;
|
||||
std::string tag;
|
||||
private:
|
||||
VkFramebuffer framebuf[RP_TYPE_COUNT]{};
|
||||
|
||||
std::string tag_;
|
||||
};
|
||||
|
||||
enum class VKRRunType {
|
||||
@ -124,15 +133,20 @@ struct VKRGraphicsPipelineDesc {
|
||||
VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
||||
|
||||
// Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish.
|
||||
Promise<VkShaderModule> *vertexShader;
|
||||
Promise<VkShaderModule> *fragmentShader;
|
||||
Promise<VkShaderModule> *vertexShader = nullptr;
|
||||
Promise<VkShaderModule> *fragmentShader = nullptr;
|
||||
|
||||
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 };
|
||||
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
|
||||
// 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.
|
||||
@ -141,19 +155,18 @@ struct VKRComputePipelineDesc {
|
||||
VkComputePipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
|
||||
};
|
||||
|
||||
// Wrapped pipeline.
|
||||
// Wrapped pipeline. Doesn't own desc.
|
||||
struct VKRGraphicsPipeline {
|
||||
VKRGraphicsPipeline() {
|
||||
pipeline = Promise<VkPipeline>::CreateEmpty();
|
||||
}
|
||||
~VKRGraphicsPipeline() {
|
||||
delete desc;
|
||||
}
|
||||
bool Create(VulkanContext *vulkan, VkRenderPass compatibleRenderPass, RenderPassType rpType);
|
||||
|
||||
VKRGraphicsPipelineDesc *desc = nullptr;
|
||||
Promise<VkPipeline> *pipeline;
|
||||
// This deletes the whole VKRGraphicsPipeline, you must remove your last pointer to it when doing this.
|
||||
void QueueForDeletion(VulkanContext *vulkan);
|
||||
|
||||
bool Create(VulkanContext *vulkan);
|
||||
u32 GetVariantsBitmask() const;
|
||||
|
||||
VKRGraphicsPipelineDesc *desc = nullptr; // not owned!
|
||||
Promise<VkPipeline> *pipeline[RP_TYPE_COUNT]{};
|
||||
std::string tag;
|
||||
};
|
||||
|
||||
struct VKRComputePipeline {
|
||||
@ -170,13 +183,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,40 +244,23 @@ 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.
|
||||
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc) {
|
||||
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline();
|
||||
pipeline->desc = desc;
|
||||
// We delay creating pipelines until the end of the current render pass, so we can create the right type immediately.
|
||||
// Unless a variantBitmask is passed in, in which case we can just go ahead.
|
||||
// WARNING: desc must stick around during the lifetime of the pipeline! It's not enough to build it on the stack and drop it.
|
||||
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, uint32_t variantBitmask, const char *tag);
|
||||
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc);
|
||||
|
||||
void NudgeCompilerThread() {
|
||||
compileMutex_.lock();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline));
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc) {
|
||||
VKRComputePipeline *pipeline = new VKRComputePipeline();
|
||||
pipeline->desc = desc;
|
||||
compileMutex_.lock();
|
||||
compileQueue_.push_back(CompileQueueEntry(pipeline));
|
||||
compileCond_.notify_one();
|
||||
compileMutex_.unlock();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void 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);
|
||||
VkRenderData data{ VKRRenderCommand::BIND_GRAPHICS_PIPELINE };
|
||||
pipelinesToCheck_.push_back(pipeline);
|
||||
data.graphics_pipeline.pipeline = pipeline;
|
||||
data.graphics_pipeline.pipelineLayout = pipelineLayout;
|
||||
curPipelineFlags_ |= flags;
|
||||
@ -431,20 +430,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 +566,9 @@ private:
|
||||
std::mutex compileMutex_;
|
||||
std::vector<CompileQueueEntry> compileQueue_;
|
||||
|
||||
// pipelines to check and possibly create at the end of the current render pass.
|
||||
std::vector<VKRGraphicsPipeline *> pipelinesToCheck_;
|
||||
|
||||
// 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"
|
||||
|
||||
@ -192,6 +193,7 @@ public:
|
||||
const std::string &GetSource() const { return source_; }
|
||||
~VKShaderModule() {
|
||||
if (module_) {
|
||||
DEBUG_LOG(G3D, "Queueing %s (shmodule %p) for release", tag_.c_str(), module_);
|
||||
vulkan_->Delete().QueueDeleteShaderModule(module_);
|
||||
}
|
||||
}
|
||||
@ -217,7 +219,7 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con
|
||||
std::vector<uint32_t> spirv;
|
||||
std::string errorMessage;
|
||||
if (!GLSLtoSPV(vkstage_, source_.c_str(), GLSLVariant::VULKAN, spirv, &errorMessage)) {
|
||||
WARN_LOG(G3D, "Shader compile to module failed: %s", errorMessage.c_str());
|
||||
WARN_LOG(G3D, "Shader compile to module failed (%s): %s", tag_.c_str(), errorMessage.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -230,10 +232,10 @@ bool VKShaderModule::Compile(VulkanContext *vulkan, ShaderLanguage language, con
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vulkan->CreateShaderModule(spirv, &module_)) {
|
||||
if (vulkan->CreateShaderModule(spirv, &module_, vkstage_ == VK_SHADER_STAGE_VERTEX_BIT ? "thin3d_vs" : "thin3d_fs")) {
|
||||
ok_ = true;
|
||||
} else {
|
||||
WARN_LOG(G3D, "vkCreateShaderModule failed");
|
||||
WARN_LOG(G3D, "vkCreateShaderModule failed (%s)", tag_.c_str());
|
||||
ok_ = false;
|
||||
}
|
||||
return ok_;
|
||||
@ -248,13 +250,18 @@ public:
|
||||
|
||||
class VKPipeline : public Pipeline {
|
||||
public:
|
||||
VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags) : flags(_flags), vulkan_(vulkan) {
|
||||
VKPipeline(VulkanContext *vulkan, size_t size, PipelineFlags _flags, const char *tag) : vulkan_(vulkan), flags(_flags), tag_(tag) {
|
||||
uboSize_ = (int)size;
|
||||
ubo_ = new uint8_t[uboSize_];
|
||||
}
|
||||
~VKPipeline() {
|
||||
vulkan_->Delete().QueueDeletePipeline(backbufferPipeline);
|
||||
vulkan_->Delete().QueueDeletePipeline(framebufferPipeline);
|
||||
DEBUG_LOG(G3D, "Queueing %s (pipeline) for release", tag_.c_str());
|
||||
if (pipeline) {
|
||||
pipeline->QueueForDeletion(vulkan_);
|
||||
}
|
||||
for (auto dep : deps) {
|
||||
dep->Release();
|
||||
}
|
||||
delete[] ubo_;
|
||||
}
|
||||
|
||||
@ -272,10 +279,12 @@ public:
|
||||
return uboSize_;
|
||||
}
|
||||
|
||||
VkPipeline backbufferPipeline = VK_NULL_HANDLE;
|
||||
VkPipeline framebufferPipeline = VK_NULL_HANDLE;
|
||||
|
||||
VKRGraphicsPipeline *pipeline = nullptr;
|
||||
VKRGraphicsPipelineDesc vkrDesc;
|
||||
PipelineFlags flags;
|
||||
|
||||
std::vector<VKShaderModule *> deps;
|
||||
|
||||
int stride[4]{};
|
||||
int dynamicUniformSize = 0;
|
||||
|
||||
@ -285,6 +294,7 @@ private:
|
||||
VulkanContext *vulkan_;
|
||||
uint8_t *ubo_;
|
||||
int uboSize_;
|
||||
std::string tag_;
|
||||
};
|
||||
|
||||
class VKTexture;
|
||||
@ -374,8 +384,8 @@ public:
|
||||
InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;
|
||||
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
||||
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag) override;
|
||||
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;
|
||||
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;
|
||||
|
||||
Texture *CreateTexture(const TextureDesc &desc) override;
|
||||
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
||||
@ -429,7 +439,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 +476,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:
|
||||
@ -893,6 +896,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
VkResult res = vkCreateDescriptorSetLayout(device_, &dsl, nullptr, &descriptorSetLayout_);
|
||||
_assert_(VK_SUCCESS == res);
|
||||
|
||||
vulkan_->SetDebugName(descriptorSetLayout_, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "thin3d_d_layout");
|
||||
|
||||
VkPipelineLayoutCreateInfo pl = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
pl.pPushConstantRanges = nullptr;
|
||||
pl.pushConstantRangeCount = 0;
|
||||
@ -901,6 +906,8 @@ VKContext::VKContext(VulkanContext *vulkan, bool splitSubmit)
|
||||
res = vkCreatePipelineLayout(device_, &pl, nullptr, &pipelineLayout_);
|
||||
_assert_(VK_SUCCESS == res);
|
||||
|
||||
vulkan_->SetDebugName(pipelineLayout_, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "thin3d_p_layout");
|
||||
|
||||
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
||||
res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
|
||||
_assert_(VK_SUCCESS == res);
|
||||
@ -1036,7 +1043,7 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) {
|
||||
return descSet;
|
||||
}
|
||||
|
||||
Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
|
||||
Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
|
||||
VKInputLayout *input = (VKInputLayout *)desc.inputLayout;
|
||||
VKBlendState *blend = (VKBlendState *)desc.blend;
|
||||
VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil;
|
||||
@ -1047,7 +1054,26 @@ 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(vulkan_, desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float), (PipelineFlags)pipelineFlags, tag);
|
||||
|
||||
VKRGraphicsPipelineDesc &gDesc = pipeline->vkrDesc;
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> stages;
|
||||
stages.resize(desc.shaders.size());
|
||||
|
||||
for (auto &iter : desc.shaders) {
|
||||
VKShaderModule *vkshader = (VKShaderModule *)iter;
|
||||
vkshader->AddRef();
|
||||
pipeline->deps.push_back(vkshader);
|
||||
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 +1082,51 @@ 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.cbs.pAttachments = &gDesc.blend0;
|
||||
|
||||
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 (size_t 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, 1 << RP_TYPE_BACKBUFFER, tag ? tag : "thin3d");
|
||||
|
||||
if (desc.uniformDesc) {
|
||||
pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize;
|
||||
@ -1291,12 +1284,12 @@ void VKContext::BindTextures(int start, int count, Texture **textures) {
|
||||
}
|
||||
}
|
||||
|
||||
ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const std::string &tag) {
|
||||
ShaderModule *VKContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t size, const char *tag) {
|
||||
VKShaderModule *shader = new VKShaderModule(stage, tag);
|
||||
if (shader->Compile(vulkan_, language, data, size)) {
|
||||
return shader;
|
||||
} else {
|
||||
ERROR_LOG(G3D, "Failed to compile shader:\n%s", (const char *)LineNumberString((const char *)data).c_str());
|
||||
ERROR_LOG(G3D, "Failed to compile shader %s:\n%s", tag, (const char *)LineNumberString((const char *)data).c_str());
|
||||
shader->Release();
|
||||
return nullptr;
|
||||
}
|
||||
@ -1338,7 +1331,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 +1351,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 +1367,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 +1485,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,
|
||||
@ -619,8 +616,8 @@ public:
|
||||
virtual RasterState *CreateRasterState(const RasterStateDesc &desc) = 0;
|
||||
// virtual ComputePipeline CreateComputePipeline(const ComputePipelineDesc &desc) = 0
|
||||
virtual InputLayout *CreateInputLayout(const InputLayoutDesc &desc) = 0;
|
||||
virtual ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const std::string &tag = "thin3d") = 0;
|
||||
virtual Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc) = 0;
|
||||
virtual ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag = "thin3d") = 0;
|
||||
virtual Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) = 0;
|
||||
|
||||
// Note that these DO NOT AddRef so you must not ->Release presets unless you manually AddRef them.
|
||||
ShaderModule *GetVshaderPreset(VertexShaderPreset preset) { return vsPresets_[preset]; }
|
||||
|
@ -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_;
|
||||
|
@ -64,6 +64,7 @@ Draw2DPipelineInfo GenerateDraw2DCopyColorFs(ShaderWriter &writer) {
|
||||
writer.EndFSMain("outColor", FSFLAG_NONE);
|
||||
|
||||
return Draw2DPipelineInfo{
|
||||
"draw2d_copy_color",
|
||||
RASTER_COLOR,
|
||||
RASTER_COLOR,
|
||||
};
|
||||
@ -77,6 +78,7 @@ Draw2DPipelineInfo GenerateDraw2DCopyDepthFs(ShaderWriter &writer) {
|
||||
writer.EndFSMain("outColor", FSFLAG_WRITEDEPTH);
|
||||
|
||||
return Draw2DPipelineInfo{
|
||||
"draw2d_copy_depth",
|
||||
RASTER_DEPTH,
|
||||
RASTER_DEPTH,
|
||||
};
|
||||
@ -95,6 +97,7 @@ Draw2DPipelineInfo GenerateDraw2D565ToDepthFs(ShaderWriter &writer) {
|
||||
writer.EndFSMain("outColor", FSFLAG_WRITEDEPTH);
|
||||
|
||||
return Draw2DPipelineInfo{
|
||||
"draw2d_565_to_depth",
|
||||
RASTER_COLOR,
|
||||
RASTER_DEPTH,
|
||||
};
|
||||
@ -119,6 +122,7 @@ Draw2DPipelineInfo GenerateDraw2D565ToDepthDeswizzleFs(ShaderWriter &writer) {
|
||||
writer.EndFSMain("outColor", FSFLAG_WRITEDEPTH);
|
||||
|
||||
return Draw2DPipelineInfo{
|
||||
"draw2d_565_to_depth_deswizzle",
|
||||
RASTER_COLOR,
|
||||
RASTER_DEPTH
|
||||
};
|
||||
@ -198,7 +202,7 @@ Draw2DPipeline *Draw2D::Create2DPipeline(std::function<Draw2DPipelineInfo (Shade
|
||||
ShaderWriter writer(fsCode, shaderLanguageDesc, ShaderStage::Fragment);
|
||||
Draw2DPipelineInfo info = generate(writer);
|
||||
|
||||
ShaderModule *fs = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), "draw2d_fs");
|
||||
ShaderModule *fs = draw_->CreateShaderModule(ShaderStage::Fragment, shaderLanguageDesc.shaderLanguage, (const uint8_t *)fsCode, strlen(fsCode), info.tag);
|
||||
|
||||
_assert_(fs);
|
||||
|
||||
@ -237,7 +241,7 @@ Draw2DPipeline *Draw2D::Create2DPipeline(std::function<Draw2DPipelineInfo (Shade
|
||||
info.samplers.is_empty() ? samplers : info.samplers,
|
||||
};
|
||||
|
||||
Draw::Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc);
|
||||
Draw::Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc, info.tag);
|
||||
|
||||
fs->Release();
|
||||
|
||||
|
@ -31,6 +31,7 @@ inline RasterChannel Draw2DSourceChannel(Draw2DShader shader) {
|
||||
}
|
||||
|
||||
struct Draw2DPipelineInfo {
|
||||
const char *tag;
|
||||
RasterChannel readChannel;
|
||||
RasterChannel writeChannel;
|
||||
Slice<SamplerDef> samplers;
|
||||
|
@ -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;
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/GPU/Shader.h"
|
||||
#include "Common/GPU/thin3d.h"
|
||||
|
||||
@ -39,4 +40,10 @@ 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 class FragmentShaderFlags : u32 {
|
||||
FS_FLAG_INPUT_ATTACHMENT = 1,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(FragmentShaderFlags);
|
||||
|
||||
bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLanguageDesc &compat, Draw::Bugs bugs, uint64_t *uniformMask, FragmentShaderFlags *fragmentShaderFlags, std::string *errorString);
|
||||
|
@ -439,7 +439,7 @@ Draw::Pipeline *PresentationCommon::CreatePipeline(std::vector<Draw::ShaderModul
|
||||
RasterState *rasterNoCull = draw_->CreateRasterState({});
|
||||
|
||||
PipelineDesc pipelineDesc{ Primitive::TRIANGLE_LIST, shaders, inputLayout, depth, blendstateOff, rasterNoCull, uniformDesc };
|
||||
Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc);
|
||||
Pipeline *pipeline = draw_->CreateGraphicsPipeline(pipelineDesc, "presentation");
|
||||
|
||||
inputLayout->Release();
|
||||
depth->Release();
|
||||
|
@ -185,6 +185,7 @@ Draw2DPipelineInfo GenerateReinterpretFragmentShader(ShaderWriter &writer, GEBuf
|
||||
writer.EndFSMain("outColor", FSFLAG_NONE);
|
||||
|
||||
return Draw2DPipelineInfo{
|
||||
"reinterpret",
|
||||
RASTER_COLOR,
|
||||
RASTER_COLOR,
|
||||
};
|
||||
|
@ -237,7 +237,7 @@ bool FramebufferManagerCommon::PerformStencilUpload(u32 addr, int size, StencilU
|
||||
{ stencilUploadVs, stencilUploadFs },
|
||||
inputLayout, stencilWrite, blendOff, rasterNoCull, &stencilUBDesc,
|
||||
};
|
||||
stencilUploadPipeline_ = draw_->CreateGraphicsPipeline(stencilWriteDesc);
|
||||
stencilUploadPipeline_ = draw_->CreateGraphicsPipeline(stencilWriteDesc, "stencil_upload");
|
||||
_assert_(stencilUploadPipeline_);
|
||||
|
||||
delete[] fsCode;
|
||||
|
@ -1866,10 +1866,9 @@ static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferForma
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat));
|
||||
return false;
|
||||
}
|
||||
WARN_LOG(G3D, "Invalid CLUT/framebuffer combination: %s vs %s", GeTextureFormatToString(texFormat), GeBufferFormatToString(bufferFormat));
|
||||
return false;
|
||||
} else if (texFormat == GE_TFMT_5650 && bufferFormat == GE_FORMAT_DEPTH16) {
|
||||
// We can also "depal" 565 format, this is used to read depth buffers as 565 on occasion (#15491).
|
||||
return true;
|
||||
|
@ -213,6 +213,7 @@ Draw2DPipeline *TextureShaderCache::GetDepalettizeShader(uint32_t clutMode, GETe
|
||||
Draw2DPipeline *ts = draw2D_->Create2DPipeline([=](ShaderWriter &writer) -> Draw2DPipelineInfo {
|
||||
GenerateDepalFs(writer, config);
|
||||
return Draw2DPipelineInfo{
|
||||
"depal",
|
||||
config.bufferFormat == GE_FORMAT_DEPTH16 ? RASTER_DEPTH : RASTER_COLOR,
|
||||
RASTER_COLOR,
|
||||
samplers
|
||||
|
@ -209,7 +209,7 @@ void GPU_D3D11::BeginHostFrame() {
|
||||
CheckGPUFeatures();
|
||||
framebufferManager_->Resized();
|
||||
drawEngine_.Resized();
|
||||
textureCacheD3D11_->NotifyConfigChanged();
|
||||
textureCache_->NotifyConfigChanged();
|
||||
shaderManagerD3D11_->DirtyLastShader();
|
||||
resized_ = false;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -265,7 +265,7 @@ void GPU_DX9::BeginHostFrame() {
|
||||
framebufferManager_->Resized();
|
||||
drawEngine_.Resized();
|
||||
shaderManagerDX9_->DirtyShader();
|
||||
textureCacheDX9_->NotifyConfigChanged();
|
||||
textureCache_->NotifyConfigChanged();
|
||||
resized_ = false;
|
||||
}
|
||||
}
|
||||
|
@ -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_);
|
||||
|
@ -93,7 +93,7 @@ GPU_GLES::GPU_GLES(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
// Update again after init to be sure of any silly driver problems.
|
||||
UpdateVsyncInterval(true);
|
||||
|
||||
textureCacheGL_->NotifyConfigChanged();
|
||||
textureCache_->NotifyConfigChanged();
|
||||
|
||||
// Load shader cache.
|
||||
std::string discID = g_paramSFO.GetDiscID();
|
||||
@ -325,7 +325,7 @@ void GPU_GLES::BeginHostFrame() {
|
||||
framebufferManager_->Resized();
|
||||
drawEngine_.Resized();
|
||||
shaderManagerGL_->DirtyShader();
|
||||
textureCacheGL_->NotifyConfigChanged();
|
||||
textureCache_->NotifyConfigChanged();
|
||||
resized_ = false;
|
||||
}
|
||||
|
||||
|
@ -710,7 +710,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;
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
u32 cmd = op >> 24;
|
||||
u32 data = op & 0xFFFFFF;
|
||||
|
||||
static constexpr char *primTypes[8] = {
|
||||
static const char * const primTypes[8] = {
|
||||
"POINTS",
|
||||
"LINES",
|
||||
"LINE_STRIP",
|
||||
@ -765,7 +765,7 @@ void GeDisassembleOp(u32 pc, u32 op, u32 prev, char *buffer, int bufsize) {
|
||||
float g = (float)((data>>8) & 0xff)/255.0f;
|
||||
float b = (float)(data>>16)/255.0f;
|
||||
|
||||
static constexpr char *lightColorTypes[] = {
|
||||
static const char * const lightColorTypes[] = {
|
||||
"ambient",
|
||||
"diffuse",
|
||||
"specular",
|
||||
|
@ -141,6 +141,7 @@ void DrawEngineVulkan::InitDeviceObjects() {
|
||||
dsl.pBindings = bindings;
|
||||
VkResult res = vkCreateDescriptorSetLayout(device, &dsl, nullptr, &descriptorSetLayout_);
|
||||
_dbg_assert_(VK_SUCCESS == res);
|
||||
vulkan->SetDebugName(descriptorSetLayout_, VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, "drawengine_d_layout");
|
||||
|
||||
static constexpr int DEFAULT_DESC_POOL_SIZE = 512;
|
||||
std::vector<VkDescriptorPoolSize> dpTypes;
|
||||
@ -179,6 +180,8 @@ void DrawEngineVulkan::InitDeviceObjects() {
|
||||
res = vkCreatePipelineLayout(device, &pl, nullptr, &pipelineLayout_);
|
||||
_dbg_assert_(VK_SUCCESS == res);
|
||||
|
||||
vulkan->SetDebugName(pipelineLayout_, VK_OBJECT_TYPE_PIPELINE_LAYOUT, "drawengine_p_layout");
|
||||
|
||||
VkSamplerCreateInfo samp{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
|
||||
samp.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
samp.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
@ -768,9 +771,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, 0);
|
||||
if (!pipeline || !pipeline->pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
return;
|
||||
@ -895,9 +896,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, 0);
|
||||
if (!pipeline || !pipeline->pipeline) {
|
||||
// Already logged, let's bail out.
|
||||
decodedVerts_ = 0;
|
||||
|
@ -91,7 +91,7 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw)
|
||||
// Update again after init to be sure of any silly driver problems.
|
||||
UpdateVsyncInterval(true);
|
||||
|
||||
textureCacheVulkan_->NotifyConfigChanged();
|
||||
textureCache_->NotifyConfigChanged();
|
||||
|
||||
// Load shader cache.
|
||||
std::string discID = g_paramSFO.GetDiscID();
|
||||
@ -303,7 +303,7 @@ void GPU_Vulkan::BeginHostFrame() {
|
||||
BuildReportingInfo();
|
||||
framebufferManager_->Resized();
|
||||
drawEngine_.Resized();
|
||||
textureCacheVulkan_->NotifyConfigChanged();
|
||||
textureCache_->NotifyConfigChanged();
|
||||
resized_ = false;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,10 @@
|
||||
|
||||
using namespace PPSSPP_VK;
|
||||
|
||||
u32 VulkanPipeline::GetVariantsBitmask() const {
|
||||
return pipeline->GetVariantsBitmask();
|
||||
}
|
||||
|
||||
PipelineManagerVulkan::PipelineManagerVulkan(VulkanContext *vulkan) : pipelines_(256), vulkan_(vulkan) {
|
||||
// The pipeline cache is created on demand (or explicitly through Load).
|
||||
}
|
||||
@ -30,21 +34,12 @@ PipelineManagerVulkan::~PipelineManagerVulkan() {
|
||||
}
|
||||
|
||||
void PipelineManagerVulkan::Clear() {
|
||||
// This should kill off all the shaders at once.
|
||||
// This could also be an opportunity to store the whole cache to disk. Will need to also
|
||||
// store the keys.
|
||||
|
||||
pipelines_.Iterate([&](const VulkanPipelineKey &key, VulkanPipeline *value) {
|
||||
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 {
|
||||
if (!value->pipeline) {
|
||||
// Something went wrong.
|
||||
ERROR_LOG(G3D, "Null pipeline found in PipelineManagerVulkan::Clear - didn't wait for asyncs?");
|
||||
} else {
|
||||
value->pipeline->QueueForDeletion(vulkan_);
|
||||
}
|
||||
delete value;
|
||||
});
|
||||
@ -175,9 +170,10 @@ static std::string CutFromMain(std::string str) {
|
||||
}
|
||||
|
||||
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();
|
||||
VkPipelineLayout layout, const VulkanPipelineRasterStateKey &key,
|
||||
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) {
|
||||
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
|
||||
VKRGraphicsPipelineDesc *desc = &vulkanPipeline->desc;
|
||||
desc->pipelineCache = pipelineCache;
|
||||
|
||||
PROFILE_THIS_SCOPE("pipelinebuild");
|
||||
@ -298,31 +294,10 @@ 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;
|
||||
desc->pipelineLayout = layout;
|
||||
|
||||
pipe.pColorBlendState = &desc->cbs;
|
||||
pipe.pDepthStencilState = &desc->dss;
|
||||
pipe.pRasterizationState = &desc->rs;
|
||||
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, variantBitmask, "game");
|
||||
|
||||
// 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;
|
||||
|
||||
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc);
|
||||
|
||||
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
|
||||
vulkanPipeline->pipeline = pipeline;
|
||||
vulkanPipeline->flags = 0;
|
||||
if (useBlendConstant)
|
||||
@ -335,7 +310,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, u32 variantBitmask) {
|
||||
if (!pipelineCache_) {
|
||||
VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO };
|
||||
VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_);
|
||||
@ -343,10 +318,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,8 +330,8 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *
|
||||
return iter;
|
||||
|
||||
VulkanPipeline *pipeline = CreateVulkanPipeline(
|
||||
renderManager, pipelineCache_, layout, renderPass,
|
||||
rasterKey, decFmt, vs, fs, useHwTransform);
|
||||
renderManager, pipelineCache_, layout,
|
||||
rasterKey, decFmt, vs, fs, useHwTransform, variantBitmask);
|
||||
pipelines_.Insert(key, pipeline);
|
||||
|
||||
// Don't return placeholder null pipelines.
|
||||
@ -564,9 +537,8 @@ struct StoredVulkanPipelineKey {
|
||||
VShaderID vShaderID;
|
||||
FShaderID fShaderID;
|
||||
uint32_t vtxFmtId;
|
||||
bool useHWTransform;
|
||||
bool backbufferPass;
|
||||
VulkanQueueRunner::RPKey renderPassKey;
|
||||
uint32_t variants;
|
||||
bool useHWTransform; // TODO: Still needed?
|
||||
|
||||
// For std::set. Better zero-initialize the struct properly for this to work.
|
||||
bool operator < (const StoredVulkanPipelineKey &other) const {
|
||||
@ -623,18 +595,11 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha
|
||||
key.useHWTransform = pkey.useHWTransform;
|
||||
key.fShaderID = fshader->GetID();
|
||||
key.vShaderID = vshader->GetID();
|
||||
key.variants = value->GetVariantsBitmask();
|
||||
if (key.useHWTransform) {
|
||||
// NOTE: This is not a vtype, but a decoded vertex format.
|
||||
key.vtxFmtId = pkey.vtxFmtId;
|
||||
}
|
||||
// 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,22 +705,16 @@ 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, key.variants);
|
||||
if (!pipeline) {
|
||||
pipelineCreateFailCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
rm->NudgeCompilerThread();
|
||||
|
||||
NOTICE_LOG(G3D, "Recreated Vulkan pipeline cache (%d pipelines, %d failed).", (int)size, pipelineCreateFailCount);
|
||||
return true;
|
||||
}
|
||||
|
@ -28,13 +28,14 @@
|
||||
#include "GPU/Vulkan/VulkanUtil.h"
|
||||
#include "GPU/Vulkan/StateMappingVulkan.h"
|
||||
#include "GPU/Vulkan/VulkanQueueRunner.h"
|
||||
#include "GPU/Vulkan/VulkanRenderManager.h"
|
||||
|
||||
struct VKRGraphicsPipeline;
|
||||
class VulkanRenderManager;
|
||||
|
||||
struct VulkanPipelineKey {
|
||||
VulkanPipelineRasterStateKey raster; // prim is included here
|
||||
VkRenderPass renderPass;
|
||||
VKRRenderPass *renderPass;
|
||||
Promise<VkShaderModule> *vShader;
|
||||
Promise<VkShaderModule> *fShader;
|
||||
uint32_t vtxFmtId;
|
||||
@ -53,11 +54,14 @@ struct VulkanPipelineKey {
|
||||
// Simply wraps a Vulkan pipeline, providing some metadata.
|
||||
struct VulkanPipeline {
|
||||
VKRGraphicsPipeline *pipeline;
|
||||
VKRGraphicsPipelineDesc desc;
|
||||
int flags; // PipelineFlags enum above.
|
||||
|
||||
bool UsesBlendConstant() const { return (flags & PIPELINE_FLAG_USES_BLEND_CONSTANT) != 0; }
|
||||
bool UsesLines() const { return (flags & PIPELINE_FLAG_USES_LINES) != 0; }
|
||||
bool UsesDepthStencil() const { return (flags & PIPELINE_FLAG_USES_DEPTH_STENCIL) != 0; }
|
||||
|
||||
u32 GetVariantsBitmask() const;
|
||||
};
|
||||
|
||||
class VulkanContext;
|
||||
@ -71,7 +75,8 @@ 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);
|
||||
// variantMask is only used when loading pipelines from cache.
|
||||
VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantMask);
|
||||
int GetNumPipelines() const { return (int)pipelines_.size(); }
|
||||
|
||||
void Clear();
|
||||
|
@ -71,7 +71,7 @@ Promise<VkShaderModule> *CompileShaderModuleAsync(VulkanContext *vulkan, VkShade
|
||||
|
||||
VkShaderModule shaderModule = VK_NULL_HANDLE;
|
||||
if (success) {
|
||||
success = vulkan->CreateShaderModule(spirv, &shaderModule);
|
||||
success = vulkan->CreateShaderModule(spirv, &shaderModule, stage == VK_SHADER_STAGE_VERTEX_BIT ? "game_vertex" : "game_fragment");
|
||||
#ifdef SHADERLOG
|
||||
OutputDebugStringA("OK");
|
||||
#endif
|
||||
@ -81,7 +81,7 @@ Promise<VkShaderModule> *CompileShaderModuleAsync(VulkanContext *vulkan, VkShade
|
||||
};
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Don't parallelize in debug mode, pathological behavior due to mutex locks in allocator.
|
||||
// Don't parallelize in debug mode, pathological behavior due to mutex locks in allocator which is HEAVILY used by glslang.
|
||||
return Promise<VkShaderModule>::AlreadyDone(compile());
|
||||
#else
|
||||
return Promise<VkShaderModule>::Spawn(&g_threadManager, compile, TaskType::CPU_COMPUTE);
|
||||
@ -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 25
|
||||
struct VulkanCacheHeader {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
@ -417,10 +418,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;
|
||||
|
@ -44,7 +44,7 @@ VkShaderModule CompileShaderModule(VulkanContext *vulkan, VkShaderStageFlagBits
|
||||
return VK_NULL_HANDLE;
|
||||
} else {
|
||||
VkShaderModule module;
|
||||
if (vulkan->CreateShaderModule(spirv, &module)) {
|
||||
if (vulkan->CreateShaderModule(spirv, &module, stage == VK_SHADER_STAGE_VERTEX_BIT ? "system_vs" : "system_fs")) {
|
||||
return module;
|
||||
} else {
|
||||
return VK_NULL_HANDLE;
|
||||
|
@ -390,39 +390,39 @@ void GPUDriverTestScreen::DiscardTest() {
|
||||
{ draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), discardFragShader_ },
|
||||
inputLayout, depthStencilWrite, blendOffNoColor, rasterNoCull, &vsColBufDesc,
|
||||
};
|
||||
discardWriteDepthStencil_ = draw->CreateGraphicsPipeline(discardDesc);
|
||||
discardWriteDepthStencil_ = draw->CreateGraphicsPipeline(discardDesc, "test");
|
||||
discardDesc.depthStencil = depthWrite;
|
||||
discardWriteDepth_ = draw->CreateGraphicsPipeline(discardDesc);
|
||||
discardWriteDepth_ = draw->CreateGraphicsPipeline(discardDesc, "test");
|
||||
discardDesc.depthStencil = stencilWrite;
|
||||
discardWriteStencil_ = draw->CreateGraphicsPipeline(discardDesc);
|
||||
discardWriteStencil_ = draw->CreateGraphicsPipeline(discardDesc, "test");
|
||||
|
||||
PipelineDesc testDesc{
|
||||
Primitive::TRIANGLE_LIST,
|
||||
{ draw->GetVshaderPreset(VS_TEXTURE_COLOR_2D), draw->GetFshaderPreset(FS_TEXTURE_COLOR_2D) },
|
||||
inputLayout, stencilEqual, blendOff, rasterNoCull, &vsColBufDesc,
|
||||
};
|
||||
drawTestStencilEqual_ = draw->CreateGraphicsPipeline(testDesc);
|
||||
drawTestStencilEqual_ = draw->CreateGraphicsPipeline(testDesc, "test");
|
||||
|
||||
testDesc.depthStencil = stencilEqualDepthAlways;
|
||||
drawTestStencilEqualDepthAlways_ = draw->CreateGraphicsPipeline(testDesc);
|
||||
drawTestStencilEqualDepthAlways_ = draw->CreateGraphicsPipeline(testDesc, "test");
|
||||
|
||||
testDesc.depthStencil = stencilNotEqual;
|
||||
drawTestStencilNotEqual_ = draw->CreateGraphicsPipeline(testDesc);
|
||||
drawTestStencilNotEqual_ = draw->CreateGraphicsPipeline(testDesc, "test");
|
||||
|
||||
testDesc.depthStencil = stenciNotEqualDepthAlways;
|
||||
drawTestStencilNotEqualDepthAlways_ = draw->CreateGraphicsPipeline(testDesc);
|
||||
drawTestStencilNotEqualDepthAlways_ = draw->CreateGraphicsPipeline(testDesc, "test");
|
||||
|
||||
testDesc.depthStencil = stencilAlwaysDepthTestGreater;
|
||||
drawTestStencilAlwaysDepthGreater_ = draw->CreateGraphicsPipeline(testDesc);
|
||||
drawTestStencilAlwaysDepthGreater_ = draw->CreateGraphicsPipeline(testDesc, "test");
|
||||
|
||||
testDesc.depthStencil = stencilAlwaysDepthTestLessEqual;
|
||||
drawTestStencilAlwaysDepthLessEqual_ = draw->CreateGraphicsPipeline(testDesc);
|
||||
drawTestStencilAlwaysDepthLessEqual_ = draw->CreateGraphicsPipeline(testDesc, "test");
|
||||
|
||||
testDesc.depthStencil = depthTestGreater;
|
||||
drawTestDepthGreater_ = draw->CreateGraphicsPipeline(testDesc);
|
||||
drawTestDepthGreater_ = draw->CreateGraphicsPipeline(testDesc, "test");
|
||||
|
||||
testDesc.depthStencil = depthTestLessEqual;
|
||||
drawTestDepthLessEqual_ = draw->CreateGraphicsPipeline(testDesc);
|
||||
drawTestDepthLessEqual_ = draw->CreateGraphicsPipeline(testDesc, "test");
|
||||
|
||||
inputLayout->Release();
|
||||
blendOff->Release();
|
||||
@ -557,14 +557,14 @@ void GPUDriverTestScreen::ShaderTest() {
|
||||
{ adrenoLogicDiscardVertShader_, adrenoLogicDiscardFragShader_ },
|
||||
inputLayout, depthStencilOff, blendOff, rasterNoCull, &vsColBufDesc,
|
||||
};
|
||||
adrenoLogicDiscardPipeline_ = draw->CreateGraphicsPipeline(adrenoLogicDiscardDesc);
|
||||
adrenoLogicDiscardPipeline_ = draw->CreateGraphicsPipeline(adrenoLogicDiscardDesc, "test");
|
||||
|
||||
PipelineDesc flatDesc{
|
||||
Primitive::TRIANGLE_LIST,
|
||||
{ flatVertShader_, flatFragShader_ },
|
||||
inputLayout, depthStencilOff, blendOff, rasterNoCull, &vsColBufDesc,
|
||||
};
|
||||
flatShadingPipeline_ = draw->CreateGraphicsPipeline(flatDesc);
|
||||
flatShadingPipeline_ = draw->CreateGraphicsPipeline(flatDesc, "test");
|
||||
|
||||
inputLayout->Release();
|
||||
blendOff->Release();
|
||||
|
@ -1890,6 +1890,7 @@ void DeveloperToolsScreen::CreateViews() {
|
||||
|
||||
void DeveloperToolsScreen::onFinish(DialogResult result) {
|
||||
g_Config.Save("DeveloperToolsScreen::onFinish");
|
||||
NativeMessageReceived("gpu_resized", "");
|
||||
}
|
||||
|
||||
void GameSettingsScreen::CallbackRestoreDefaults(bool yes) {
|
||||
|
@ -950,13 +950,13 @@ bool CreateGlobalPipelines() {
|
||||
inputLayout, depth, blendNormal, rasterNoCull, &vsTexColBufDesc,
|
||||
};
|
||||
|
||||
colorPipeline = g_draw->CreateGraphicsPipeline(colorDesc);
|
||||
colorPipeline = g_draw->CreateGraphicsPipeline(colorDesc, "global_color");
|
||||
if (!colorPipeline) {
|
||||
// Something really critical is wrong, don't care much about correct releasing of the states.
|
||||
return false;
|
||||
}
|
||||
|
||||
texColorPipeline = g_draw->CreateGraphicsPipeline(texColorDesc);
|
||||
texColorPipeline = g_draw->CreateGraphicsPipeline(texColorDesc, "global_texcolor");
|
||||
if (!texColorPipeline) {
|
||||
// Something really critical is wrong, don't care much about correct releasing of the states.
|
||||
return false;
|
||||
|
@ -801,7 +801,8 @@ SOURCES_C += $(ZSTDDIR)/common/debug.c \
|
||||
$(ZSTDDIR)/dictBuilder/fastcover.c \
|
||||
$(ZSTDDIR)/dictBuilder/zdict.c
|
||||
|
||||
ifneq (,$(findstring msvc,$(platform)))
|
||||
# zstd only uses asm for Linux/macOS and GNUC.
|
||||
ifneq ($(PLATFORM_EXT), win32)
|
||||
ifeq ($(TARGET_ARCH),x86_64)
|
||||
SOURCES_C += $(ZSTDDIR)/decompress/huf_decompress_amd64.S
|
||||
endif
|
||||
|
@ -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