Multisampling groundwork

This commit is contained in:
Henrik Rydgård 2022-11-27 11:39:44 +01:00
parent 6daecb4e2b
commit 06af304c8d
13 changed files with 157 additions and 58 deletions

View file

@ -2,14 +2,41 @@
#include "Common/GPU/Vulkan/VulkanFramebuffer.h"
#include "Common/GPU/Vulkan/VulkanQueueRunner.h"
VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, bool createDepthStencilBuffer, const char *tag)
VkSampleCountFlagBits SampleCountToFlagBits(int count) {
// TODO: Check hardware support here, or elsewhere?
// Some hardware only supports 4x.
switch (count) {
case 1: return VK_SAMPLE_COUNT_1_BIT;
case 2: return VK_SAMPLE_COUNT_2_BIT;
case 4: return VK_SAMPLE_COUNT_4_BIT;
case 8: return VK_SAMPLE_COUNT_8_BIT;
case 16: return VK_SAMPLE_COUNT_16_BIT; // rare
default:
_assert_(false);
return VK_SAMPLE_COUNT_1_BIT;
}
}
VKRFramebuffer::VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _numSamples, bool createDepthStencilBuffer, const char *tag)
: vulkan_(vk), tag_(tag), width(_width), height(_height), numLayers(_numLayers) {
_dbg_assert_(tag);
CreateImage(vulkan_, initCmd, color, width, height, numLayers, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);
CreateImage(vulkan_, initCmd, color, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);
if (createDepthStencilBuffer) {
CreateImage(vulkan_, initCmd, depth, width, height, numLayers, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);
CreateImage(vulkan_, initCmd, depth, width, height, numLayers, VK_SAMPLE_COUNT_1_BIT, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);
}
if (_numSamples > 1) {
sampleCount = SampleCountToFlagBits(_numSamples);
// TODO: Create a different tag for these?
CreateImage(vulkan_, initCmd, msaaColor, width, height, numLayers, sampleCount, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true, tag);
if (createDepthStencilBuffer) {
CreateImage(vulkan_, initCmd, depth, width, height, numLayers, sampleCount, vulkan_->GetDeviceInfo().preferredDepthStencilFormat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, false, tag);
}
} else {
sampleCount = VK_SAMPLE_COUNT_1_BIT;
}
UpdateTag(tag);
@ -55,7 +82,7 @@ VkFramebuffer VKRFramebuffer::Get(VKRRenderPass *compatibleRenderPass, RenderPas
}
views[1] = depth.rtView;
}
fbci.renderPass = compatibleRenderPass->Get(vulkan_, rpType);
fbci.renderPass = compatibleRenderPass->Get(vulkan_, rpType, sampleCount);
fbci.attachmentCount = hasDepth ? 2 : 1;
fbci.pAttachments = views;
fbci.width = width;
@ -108,7 +135,7 @@ VKRFramebuffer::~VKRFramebuffer() {
// NOTE: If numLayers > 1, it will create an array texture, rather than a normal 2D texture.
// This requires a different sampling path!
void VKRFramebuffer::CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, int numLayers, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag) {
void VKRFramebuffer::CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, int numLayers, VkSampleCountFlagBits sampleCount, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag) {
// We don't support more exotic layer setups for now. Mono or stereo.
_dbg_assert_(numLayers == 1 || numLayers == 2);
@ -120,7 +147,7 @@ void VKRFramebuffer::CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKR
ici.extent.depth = 1;
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ici.imageType = VK_IMAGE_TYPE_2D;
ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.samples = sampleCount;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.format = format;
// Strictly speaking we don't yet need VK_IMAGE_USAGE_SAMPLED_BIT for depth buffers since we do not yet sample depth buffers.
@ -226,45 +253,76 @@ static VkAttachmentStoreOp ConvertStoreAction(VKRRenderPassStoreAction action) {
// 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
VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType) {
VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPassType rpType, VkSampleCountFlagBits sampleCount) {
bool selfDependency = RenderPassTypeHasInput(rpType);
bool isBackbuffer = rpType == RenderPassType::BACKBUFFER;
bool hasDepth = RenderPassTypeHasDepth(rpType);
bool multiview = RenderPassTypeHasMultiView(rpType);
bool multisample = rpType & RenderPassType::MULTISAMPLE;
if (multiview) {
// TODO: Assert that the device has multiview support enabled.
}
VkAttachmentDescription attachments[2] = {};
attachments[0].format = isBackbuffer ? 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 = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].flags = 0;
int colorAttachmentIndex = 0;
int depthAttachmentIndex = 1;
int attachmentCount = 0;
VkAttachmentDescription attachments[4]{};
attachments[attachmentCount].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
attachments[attachmentCount].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[attachmentCount].loadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.colorLoadAction);
attachments[attachmentCount].storeOp = ConvertStoreAction(key.colorStoreAction);
attachments[attachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[attachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[attachmentCount].initialLayout = isBackbuffer ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[attachmentCount].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachmentCount++;
if (hasDepth) {
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);
attachments[1].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);
attachments[1].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].flags = 0;
attachments[attachmentCount].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
attachments[attachmentCount].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[attachmentCount].loadOp = multisample ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : ConvertLoadAction(key.depthLoadAction);
attachments[attachmentCount].storeOp = ConvertStoreAction(key.depthStoreAction);
attachments[attachmentCount].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);
attachments[attachmentCount].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[attachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachmentCount++;
}
if (multisample) {
colorAttachmentIndex = attachmentCount;
attachments[attachmentCount].format = isBackbuffer ? vulkan->GetSwapchainFormat() : VK_FORMAT_R8G8B8A8_UNORM;
attachments[attachmentCount].samples = sampleCount;
attachments[attachmentCount].loadOp = ConvertLoadAction(key.colorLoadAction);
attachments[attachmentCount].storeOp = ConvertStoreAction(key.colorStoreAction);
attachments[attachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[attachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[attachmentCount].finalLayout = isBackbuffer ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachmentCount++;
if (hasDepth) {
depthAttachmentIndex = attachmentCount;
attachments[attachmentCount].format = vulkan->GetDeviceInfo().preferredDepthStencilFormat;
attachments[attachmentCount].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[attachmentCount].loadOp = ConvertLoadAction(key.depthLoadAction);
attachments[attachmentCount].storeOp = ConvertStoreAction(key.depthStoreAction);
attachments[attachmentCount].stencilLoadOp = ConvertLoadAction(key.stencilLoadAction);
attachments[attachmentCount].stencilStoreOp = ConvertStoreAction(key.stencilStoreAction);
attachments[attachmentCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[attachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachmentCount++;
}
}
VkAttachmentReference color_reference{};
color_reference.attachment = 0;
color_reference.attachment = colorAttachmentIndex;
color_reference.layout = selfDependency ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depth_reference{};
depth_reference.attachment = 1;
depth_reference.attachment = depthAttachmentIndex;
depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
@ -279,7 +337,14 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas
}
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
subpass.pResolveAttachments = nullptr;
VkAttachmentReference resolve_references[2];
if (multisample) {
resolve_references[0].attachment = 0; // the non-msaa color buffer.
subpass.pResolveAttachments = resolve_references;
} else {
subpass.pResolveAttachments = nullptr;
}
if (hasDepth) {
subpass.pDepthStencilAttachment = &depth_reference;
}
@ -291,7 +356,7 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas
size_t numDeps = 0;
VkRenderPassCreateInfo rp{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rp.attachmentCount = hasDepth ? 2 : 1;
rp.attachmentCount = attachmentCount;
rp.pAttachments = attachments;
rp.subpassCount = 1;
rp.pSubpasses = &subpass;
@ -342,12 +407,16 @@ VkRenderPass CreateRenderPass(VulkanContext *vulkan, const RPKey &key, RenderPas
return pass;
}
VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType) {
VkRenderPass VKRRenderPass::Get(VulkanContext *vulkan, RenderPassType rpType, VkSampleCountFlagBits sampleCount) {
// 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.
// WARNING: We don't include sampleCount in the key, there's only the distinction multisampled or not
// which comes from the rpType.
// So you CAN NOT mix and match different non-one sample counts.
if (!pass[(int)rpType]) {
pass[(int)rpType] = CreateRenderPass(vulkan, key_, (RenderPassType)rpType);
pass[(int)rpType] = CreateRenderPass(vulkan, key_, (RenderPassType)rpType, sampleCount);
}
return pass[(int)rpType];
}

View file

@ -15,12 +15,13 @@ enum class RenderPassType {
HAS_DEPTH = 1,
COLOR_INPUT = 2, // input attachment
MULTIVIEW = 4,
MULTISAMPLE = 8,
// This is the odd one out, and gets special handling in MergeRPTypes.
// If this flag is set, none of the other flags can be set.
// For the backbuffer we can always use CLEAR/DONT_CARE, so bandwidth cost for a depth channel is negligible
// so we don't bother with a non-depth version.
BACKBUFFER = 8,
BACKBUFFER = 16,
TYPE_COUNT = BACKBUFFER + 1,
};
@ -54,7 +55,7 @@ struct VKRImage {
class VKRFramebuffer {
public:
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, bool createDepthStencilBuffer, const char *tag);
VKRFramebuffer(VulkanContext *vk, VkCommandBuffer initCmd, VKRRenderPass *compatibleRenderPass, int _width, int _height, int _numLayers, int _numSamples, bool createDepthStencilBuffer, const char *tag);
~VKRFramebuffer();
VkFramebuffer Get(VKRRenderPass *compatibleRenderPass, RenderPassType rpType);
@ -62,10 +63,15 @@ public:
int width = 0;
int height = 0;
int numLayers = 0;
VkSampleCountFlagBits sampleCount;
VKRImage color{}; // color.image is always there.
VKRImage depth{}; // depth.image is allowed to be VK_NULL_HANDLE.
// These are only initialized and used if numSamples > 1.
VKRImage msaaColor{};
VKRImage msaaDepth{};
const char *Tag() const {
return tag_.c_str();
}
@ -76,13 +82,13 @@ public:
return depth.image != VK_NULL_HANDLE;
}
// TODO: Hide.
VulkanContext *vulkan_;
VulkanContext *Vulkan() const { return vulkan_; }
private:
static void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, int numLayers, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag);
static void CreateImage(VulkanContext *vulkan, VkCommandBuffer cmd, VKRImage &img, int width, int height, int numLayers, VkSampleCountFlagBits sampleCount, VkFormat format, VkImageLayout initialLayout, bool color, const char *tag);
VkFramebuffer framebuf[(size_t)RenderPassType::TYPE_COUNT]{};
VulkanContext *vulkan_;
std::string tag_;
};
@ -98,6 +104,8 @@ inline bool RenderPassTypeHasMultiView(RenderPassType type) {
return (type & RenderPassType::MULTIVIEW) != 0;
}
VkSampleCountFlagBits SampleCountToFlagBits(int count);
// Must be the same order as Draw::RPAction
enum class VKRRenderPassLoadAction : uint8_t {
KEEP, // default. avoid when possible.
@ -124,7 +132,7 @@ class VKRRenderPass {
public:
VKRRenderPass(const RPKey &key) : key_(key) {}
VkRenderPass Get(VulkanContext *vulkan, RenderPassType rpType);
VkRenderPass Get(VulkanContext *vulkan, RenderPassType rpType, VkSampleCountFlagBits sampleCount);
void Destroy(VulkanContext *vulkan) {
for (size_t i = 0; i < (size_t)RenderPassType::TYPE_COUNT; i++) {
if (pass[i]) {

View file

@ -198,7 +198,7 @@ bool VulkanQueueRunner::InitBackbufferFramebuffers(int width, int height) {
VkImageView attachments[2] = { VK_NULL_HANDLE, depth_.view };
VkFramebufferCreateInfo fb_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
fb_info.renderPass = GetCompatibleRenderPass()->Get(vulkan_, RenderPassType::BACKBUFFER);
fb_info.renderPass = GetCompatibleRenderPass()->Get(vulkan_, RenderPassType::BACKBUFFER, VK_SAMPLE_COUNT_1_BIT);
fb_info.attachmentCount = 2;
fb_info.pAttachments = attachments;
fb_info.width = width;
@ -1323,7 +1323,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
// 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[(size_t)rpType] = Promise<VkPipeline>::CreateEmpty();
graphicsPipeline->Create(vulkan_, renderPass->Get(vulkan_, rpType), rpType);
graphicsPipeline->Create(vulkan_, renderPass->Get(vulkan_, rpType, step.render.framebuffer ? step.render.framebuffer->sampleCount : VK_SAMPLE_COUNT_1_BIT), rpType);
}
VkPipeline pipeline = graphicsPipeline->pipeline[(size_t)rpType]->BlockUntilReady();
@ -1523,6 +1523,8 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
bool hasDepth = RenderPassTypeHasDepth(step.render.renderPassType);
VkSampleCountFlagBits sampleCount;
if (step.render.framebuffer) {
_dbg_assert_(step.render.finalColorLayout != VK_IMAGE_LAYOUT_UNDEFINED);
_dbg_assert_(step.render.finalDepthStencilLayout != VK_IMAGE_LAYOUT_UNDEFINED);
@ -1535,6 +1537,7 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
VKRFramebuffer *fb = step.render.framebuffer;
framebuf = fb->Get(renderPass, step.render.renderPassType);
sampleCount = fb->sampleCount;
_dbg_assert_(framebuf != VK_NULL_HANDLE);
w = fb->width;
h = fb->height;
@ -1590,10 +1593,11 @@ VKRRenderPass *VulkanQueueRunner::PerformBindFramebufferAsRenderTarget(const VKR
numClearVals = hasDepth ? 2 : 1; // We might do depth-less backbuffer in the future, though doubtful of the value.
clearVal[1].depthStencil.depth = 0.0f;
clearVal[1].depthStencil.stencil = 0;
sampleCount = VK_SAMPLE_COUNT_1_BIT;
}
VkRenderPassBeginInfo rp_begin = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
rp_begin.renderPass = renderPass->Get(vulkan_, step.render.renderPassType);
rp_begin.renderPass = renderPass->Get(vulkan_, step.render.renderPassType, sampleCount);
rp_begin.framebuffer = framebuf;
VkRect2D rc = step.render.renderArea;

View file

@ -481,7 +481,7 @@ VkCommandBuffer VulkanRenderManager::GetInitCmd() {
return frameData_[curFrame].GetInitCmd(vulkan_);
}
VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, const char *tag) {
VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, const char *tag) {
VKRGraphicsPipeline *pipeline = new VKRGraphicsPipeline();
_dbg_assert_(desc->vertexShader);
_dbg_assert_(desc->fragmentShader);
@ -523,7 +523,7 @@ VKRGraphicsPipeline *VulkanRenderManager::CreateGraphicsPipeline(VKRGraphicsPipe
}
pipeline->pipeline[i] = Promise<VkPipeline>::CreateEmpty();
compileQueue_.push_back(CompileQueueEntry(pipeline, compatibleRenderPass->Get(vulkan_, rpType), rpType));
compileQueue_.push_back(CompileQueueEntry(pipeline, compatibleRenderPass->Get(vulkan_, rpType, sampleCount), rpType));
needsCompile = true;
}
if (needsCompile)
@ -575,17 +575,23 @@ void VulkanRenderManager::EndCurRenderStep() {
if (curRenderStep_->render.framebuffer->numLayers > 1) {
rpType = (RenderPassType)(rpType | RenderPassType::MULTIVIEW);
}
if (curRenderStep_->render.framebuffer->sampleCount != VK_SAMPLE_COUNT_1_BIT) {
rpType = (RenderPassType)(rpType | RenderPassType::MULTISAMPLE);
}
}
VKRRenderPass *renderPass = queueRunner_.GetRenderPass(key);
curRenderStep_->render.renderPassType = rpType;
VkSampleCountFlagBits sampleCount = curRenderStep_->render.framebuffer ? curRenderStep_->render.framebuffer->sampleCount : VK_SAMPLE_COUNT_1_BIT;
compileMutex_.lock();
bool needsCompile = false;
for (VKRGraphicsPipeline *pipeline : pipelinesToCheck_) {
if (!pipeline->pipeline[(size_t)rpType]) {
pipeline->pipeline[(size_t)rpType] = Promise<VkPipeline>::CreateEmpty();
compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType), rpType));
compileQueue_.push_back(CompileQueueEntry(pipeline, renderPass->Get(vulkan_, rpType, sampleCount), rpType));
needsCompile = true;
}
}

View file

@ -217,7 +217,7 @@ public:
// 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, PipelineFlags pipelineFlags, uint32_t variantBitmask, const char *tag);
VKRGraphicsPipeline *CreateGraphicsPipeline(VKRGraphicsPipelineDesc *desc, PipelineFlags pipelineFlags, uint32_t variantBitmask, VkSampleCountFlagBits sampleCount, const char *tag);
VKRComputePipeline *CreateComputePipeline(VKRComputePipelineDesc *desc);
void NudgeCompilerThread() {

View file

@ -1210,7 +1210,7 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char
VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
raster->ToVulkan(&gDesc.rs);
pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, pipelineFlags, 1 << (size_t)RenderPassType::BACKBUFFER, tag ? tag : "thin3d");
pipeline->pipeline = renderManager_.CreateGraphicsPipeline(&gDesc, pipelineFlags, 1 << (size_t)RenderPassType::BACKBUFFER, VK_SAMPLE_COUNT_1_BIT, tag ? tag : "thin3d");
if (desc.uniformDesc) {
pipeline->dynamicUniformSize = (int)desc.uniformDesc->uniformBufferSize;
@ -1569,7 +1569,7 @@ public:
}
~VKFramebuffer() {
_assert_msg_(buf_, "Null buf_ in VKFramebuffer - double delete?");
buf_->vulkan_->Delete().QueueCallback([](void *fb) {
buf_->Vulkan()->Delete().QueueCallback([](void *fb) {
VKRFramebuffer *vfb = static_cast<VKRFramebuffer *>(fb);
delete vfb;
}, buf_);
@ -1584,8 +1584,13 @@ private:
};
Framebuffer *VKContext::CreateFramebuffer(const FramebufferDesc &desc) {
_assert_(desc.numSamples > 0);
_assert_(desc.numLayers > 0);
_assert_(desc.width > 0);
_assert_(desc.height > 0);
VkCommandBuffer cmd = renderManager_.GetInitCmd();
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.numLayers, desc.z_stencil, desc.tag);
VKRFramebuffer *vkrfb = new VKRFramebuffer(vulkan_, cmd, renderManager_.GetQueueRunner()->GetCompatibleRenderPass(), desc.width, desc.height, desc.numLayers, desc.numSamples, desc.z_stencil, desc.tag);
return new VKFramebuffer(vkrfb);
}

View file

@ -300,6 +300,7 @@ struct FramebufferDesc {
int height;
int depth;
int numLayers;
int numSamples;
bool z_stencil;
const char *tag; // For graphics debuggers
};

View file

@ -901,6 +901,7 @@ static ConfigSetting graphicsSettings[] = {
// Most low-performance (and many high performance) mobile GPUs do not support aniso anyway so defaulting to 4 is fine.
ConfigSetting("AnisotropyLevel", &g_Config.iAnisotropyLevel, 4, true, true),
ConfigSetting("MultiSampleLevel", &g_Config.iMultiSampleLevel, 1, true, true),
ReportedConfigSetting("VertexDecCache", &g_Config.bVertexCache, false, true, true),
ReportedConfigSetting("TextureBackoffCache", &g_Config.bTextureBackoffCache, false, true, true),

View file

@ -210,6 +210,7 @@ public:
int iForceFullScreen = -1; // -1 = nope, 0 = force off, 1 = force on (not saved.)
int iInternalResolution; // 0 = Auto (native), 1 = 1x (480x272), 2 = 2x, 3 = 3x, 4 = 4x and so on.
int iAnisotropyLevel; // 0 - 5, powers of 2: 0 = 1x = no aniso
int iMultiSampleLevel;
int bHighQualityDepth;
bool bReplaceTextures;
bool bSaveNewTextures;

View file

@ -1652,7 +1652,7 @@ void FramebufferManagerCommon::ResizeFramebufFBO(VirtualFramebuffer *vfb, int w,
shaderManager_->DirtyLastShader();
char tag[128];
size_t len = FormatFramebufferName(vfb, tag, sizeof(tag));
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), true, tag });
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 1, true, tag });
if (Memory::IsVRAMAddress(vfb->fb_address) && vfb->fb_stride != 0) {
NotifyMemInfo(MemBlockFlags::ALLOC, vfb->fb_address, ColorBufferByteSize(vfb), tag, len);
}
@ -2019,7 +2019,7 @@ VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAd
char name[64];
snprintf(name, sizeof(name), "%08x_color_RAM", vfb->fb_address);
textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_CREATED);
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), true, name });
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 1, true, name });
vfbs_.push_back(vfb);
u32 byteSize = ColorBufferByteSize(vfb);
@ -2072,7 +2072,7 @@ VirtualFramebuffer *FramebufferManagerCommon::FindDownloadTempBuffer(VirtualFram
snprintf(name, sizeof(name), "download_temp");
// TODO: We don't have a way to create a depth-only framebuffer yet.
// Also, at least on Vulkan we always create both depth and color, need to rework how we handle renderpasses.
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, channel == RASTER_DEPTH ? true : false, name });
nvfb->fbo = draw_->CreateFramebuffer({ nvfb->bufferWidth, nvfb->bufferHeight, 1, 1, 1, channel == RASTER_DEPTH ? true : false, name });
if (!nvfb->fbo) {
ERROR_LOG(FRAMEBUF, "Error creating FBO! %d x %d", nvfb->renderWidth, nvfb->renderHeight);
delete nvfb;
@ -2466,7 +2466,7 @@ Draw::Framebuffer *FramebufferManagerCommon::GetTempFBO(TempFBO reason, u16 w, u
char name[128];
snprintf(name, sizeof(name), "tempfbo_%s_%dx%d", TempFBOReasonToString(reason), w / renderScaleFactor_, h / renderScaleFactor_);
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, GetFramebufferLayers(), z_stencil, name });
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, GetFramebufferLayers(), 1, z_stencil, name });
if (!fbo) {
return nullptr;
}
@ -3156,7 +3156,7 @@ VirtualFramebuffer *FramebufferManagerCommon::ResolveFramebufferColorToFormat(Vi
char tag[128];
FormatFramebufferName(vfb, tag, sizeof(tag));
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), true, tag });
vfb->fbo = draw_->CreateFramebuffer({ vfb->renderWidth, vfb->renderHeight, 1, GetFramebufferLayers(), 1, true, tag });
vfbs_.push_back(vfb);
}

View file

@ -270,7 +270,7 @@ bool PresentationCommon::UpdatePostShader() {
previousIndex_ = 0;
for (int i = 0; i < FRAMES; ++i) {
previousFramebuffers_[i] = draw_->CreateFramebuffer({ w, h, 1, 1, false, "inter_presentation" });
previousFramebuffers_[i] = draw_->CreateFramebuffer({ w, h, 1, 1, 1, false, "inter_presentation" });
if (!previousFramebuffers_[i]) {
DestroyPostShader();
return false;
@ -386,7 +386,7 @@ bool PresentationCommon::AllocateFramebuffer(int w, int h) {
}
// No depth/stencil for post processing
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, false, "presentation" });
Draw::Framebuffer *fbo = draw_->CreateFramebuffer({ w, h, 1, 1, 1, false, "presentation" });
if (!fbo) {
return false;
}

View file

@ -1299,6 +1299,7 @@ void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) {
desc.depth = 1;
desc.z_stencil = false;
desc.numLayers = 1;
desc.numSamples = 1;
desc.tag = "dynamic_clut";
dynamicClutFbo_ = draw_->CreateFramebuffer(desc);
desc.tag = "dynamic_clut_temp";

View file

@ -8,6 +8,7 @@
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Common/GPU/Vulkan/VulkanContext.h"
#include "Core/Config.h"
#include "GPU/Vulkan/VulkanUtil.h"
#include "GPU/Vulkan/PipelineManagerVulkan.h"
#include "GPU/Vulkan/ShaderManagerVulkan.h"
@ -171,7 +172,7 @@ static std::string CutFromMain(std::string str) {
}
static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache,
VkPipelineLayout layout, PipelineFlags pipelineFlags, const VulkanPipelineRasterStateKey &key,
VkPipelineLayout layout, PipelineFlags pipelineFlags, VkSampleCountFlagBits sampleCount, const VulkanPipelineRasterStateKey &key,
const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantBitmask) {
VulkanPipeline *vulkanPipeline = new VulkanPipeline();
VKRGraphicsPipelineDesc *desc = &vulkanPipeline->desc;
@ -307,7 +308,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
tag = FragmentShaderDesc(fs->GetID()) + " VS " + VertexShaderDesc(vs->GetID());
#endif
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, pipelineFlags, variantBitmask, tag.c_str());
VKRGraphicsPipeline *pipeline = renderManager->CreateGraphicsPipeline(desc, pipelineFlags, variantBitmask, sampleCount, tag.c_str());
vulkanPipeline->pipeline = pipeline;
if (useBlendConstant) {
@ -351,8 +352,10 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *
pipelineFlags |= PipelineFlags::USES_MULTIVIEW;
}
VkSampleCountFlagBits sampleCount = SampleCountToFlagBits(g_Config.iMultiSampleLevel);
VulkanPipeline *pipeline = CreateVulkanPipeline(
renderManager, pipelineCache_, layout, pipelineFlags,
renderManager, pipelineCache_, layout, pipelineFlags, sampleCount,
rasterKey, decFmt, vs, fs, gs, useHwTransform, variantBitmask);
pipelines_.Insert(key, pipeline);