Merge branch 'hrydgard:master' into compat_openxr_gta

This commit is contained in:
Luboš Vonásek 2022-09-08 18:39:11 +02:00 committed by GitHub
commit 5be9455bf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 618 additions and 509 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {

View File

@ -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; }

View File

@ -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;
}

View File

@ -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().

View File

@ -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) {

View File

@ -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.

View File

@ -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,11 +712,79 @@ 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_;
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));
}
}
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();
@ -680,7 +797,6 @@ void VulkanRenderManager::EndCurRenderStep() {
// 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:

View File

@ -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;

View File

@ -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);
}

View File

@ -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]; }

View File

@ -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_;

View File

@ -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();

View File

@ -31,6 +31,7 @@ inline RasterChannel Draw2DSourceChannel(Draw2DShader shader) {
}
struct Draw2DPipelineInfo {
const char *tag;
RasterChannel readChannel;
RasterChannel writeChannel;
Slice<SamplerDef> samplers;

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -185,6 +185,7 @@ Draw2DPipelineInfo GenerateReinterpretFragmentShader(ShaderWriter &writer, GEBuf
writer.EndFSMain("outColor", FSFLAG_NONE);
return Draw2DPipelineInfo{
"reinterpret",
RASTER_COLOR,
RASTER_COLOR,
};

View File

@ -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;

View File

@ -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;
}
} 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;

View File

@ -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

View File

@ -209,7 +209,7 @@ void GPU_D3D11::BeginHostFrame() {
CheckGPUFeatures();
framebufferManager_->Resized();
drawEngine_.Resized();
textureCacheD3D11_->NotifyConfigChanged();
textureCache_->NotifyConfigChanged();
shaderManagerD3D11_->DirtyLastShader();
resized_ = false;
}

View File

@ -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 {

View File

@ -265,7 +265,7 @@ void GPU_DX9::BeginHostFrame() {
framebufferManager_->Resized();
drawEngine_.Resized();
shaderManagerDX9_->DirtyShader();
textureCacheDX9_->NotifyConfigChanged();
textureCache_->NotifyConfigChanged();
resized_ = false;
}
}

View File

@ -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_);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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",

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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) {

View File

@ -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;

View File

@ -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

View File

@ -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;