Skip to content

Commit

Permalink
vulkan: implement MSAA textures.
Browse files Browse the repository at this point in the history
  • Loading branch information
slime73 committed Dec 1, 2024
1 parent 2e58f1d commit fe4fc84
Show file tree
Hide file tree
Showing 7 changed files with 345 additions and 210 deletions.
167 changes: 104 additions & 63 deletions src/modules/graphics/vulkan/Graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ void Graphics::submitGpuCommands(SubmitMode submitMode, void *screenshotCallback

if (submitMode == SUBMIT_PRESENT)
{
VkImage backbufferImage = fakeBackbuffer != nullptr ? (VkImage)fakeBackbuffer->getHandle() : swapChainImages.at(imageIndex);
VkImage backbufferImage = fakeBackbuffer != nullptr ? (VkImage)fakeBackbuffer->getRenderTargetHandle() : swapChainImages.at(imageIndex);

if (pendingScreenshotCallbacks.empty())
{
Expand Down Expand Up @@ -1193,13 +1193,8 @@ static bool computeDispatchBarrierFlags(Shader *shader, VkAccessFlags &dstAccess

// All writable images use the GENERAL layout.
// TODO: this is pretty messy.
VkAccessFlags texAccessFlags = 0;
VkPipelineStageFlags texStageFlags = 0;
bool depthStencil = isPixelFormatDepthStencil(tex->getPixelFormat());
Vulkan::setImageLayoutTransitionOptions(false, tex->isRenderTarget(), depthStencil, VK_IMAGE_LAYOUT_GENERAL, texAccessFlags, texStageFlags);

dstAccessFlags |= texAccessFlags;
dstStageFlags |= texStageFlags;
Vulkan::addImageLayoutTransitionOptions(false, tex->isRenderTarget(), depthStencil, VK_IMAGE_LAYOUT_GENERAL, dstAccessFlags, dstStageFlags);
}

for (const auto &info : shader->getActiveStorageBufferInfo())
Expand Down Expand Up @@ -1285,7 +1280,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int pixelw, in
if (isWindow)
setDefaultRenderPass();
else
setRenderPass(rts, pixelw, pixelh, hasSRGBtexture);
setRenderPass(rts, pixelw, pixelh);
}

// END IMPLEMENTATION OVERRIDDEN FUNCTIONS
Expand Down Expand Up @@ -2120,8 +2115,9 @@ VkFramebuffer Graphics::createFramebuffer(FramebufferConfiguration &configuratio
if (configuration.staticData.depthView)
attachments.push_back(configuration.staticData.depthView);

if (configuration.staticData.resolveView)
attachments.push_back(configuration.staticData.resolveView);
// Resolve attachments after everything else to match createRenderPass.
for (const auto &colorResolveView : configuration.colorResolveViews)
attachments.push_back(colorResolveView);

VkFramebufferCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
Expand Down Expand Up @@ -2198,6 +2194,7 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration)

std::vector<VkAttachmentDescription> attachments;
std::vector<VkAttachmentReference> colorAttachmentRefs;
std::vector<VkAttachmentReference> colorResolveAttachmentRefs;

uint32_t attachment = 0;
for (const auto &colorAttachment : configuration.colorAttachments)
Expand All @@ -2214,24 +2211,33 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration)
colorDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorDescription.initialLayout = colorAttachment.layout;
colorDescription.finalLayout = colorAttachment.layout;
if (colorAttachment.msaaSamples > 1)
{
colorDescription.initialLayout = colorAttachment.msaaLayout;
colorDescription.finalLayout = colorAttachment.msaaLayout;
}
else
{
colorDescription.initialLayout = colorAttachment.layout;
colorDescription.finalLayout = colorAttachment.layout;
}
attachments.push_back(colorDescription);

VkAccessFlags texBeginAccessFlags = 0;
VkPipelineStageFlags texBeginStageFlags = 0;
Vulkan::setImageLayoutTransitionOptions(true, true, false, colorAttachment.layout, texBeginAccessFlags, texBeginStageFlags);
beginDependency.srcAccessMask |= texBeginAccessFlags;
beginDependency.srcStageMask |= texBeginStageFlags;
// I had a TODO here, but I don't remember why...
if (colorAttachment.layout != VK_IMAGE_LAYOUT_UNDEFINED)
{
Vulkan::addImageLayoutTransitionOptions(true, true, false, colorAttachment.layout, beginDependency.srcAccessMask, beginDependency.srcStageMask);
Vulkan::addImageLayoutTransitionOptions(false, true, false, colorAttachment.layout, endDependency.dstAccessMask, endDependency.dstStageMask);
}

VkAccessFlags texEndAccessFlags = 0;
VkPipelineStageFlags texEndStageFlags = 0;
Vulkan::setImageLayoutTransitionOptions(false, true, false, colorAttachment.layout, texEndAccessFlags, texEndStageFlags);
endDependency.dstAccessMask |= texEndAccessFlags;
endDependency.dstStageMask |= texEndStageFlags;
if (colorAttachment.msaaLayout != VK_IMAGE_LAYOUT_UNDEFINED)
{
Vulkan::addImageLayoutTransitionOptions(true, true, false, colorAttachment.msaaLayout, beginDependency.srcAccessMask, beginDependency.srcStageMask);
Vulkan::addImageLayoutTransitionOptions(false, true, false, colorAttachment.msaaLayout, endDependency.dstAccessMask, endDependency.dstStageMask);
}
}

subPass.colorAttachmentCount = static_cast<uint32_t>(colorAttachmentRefs.size());
subPass.colorAttachmentCount = static_cast<uint32_t>(configuration.colorAttachments.size());
subPass.pColorAttachments = colorAttachmentRefs.data();

VkAttachmentReference depthStencilAttachmentRef{};
Expand All @@ -2252,36 +2258,43 @@ VkRenderPass Graphics::createRenderPass(RenderPassConfiguration &configuration)
depthStencilAttachment.finalLayout = configuration.staticData.depthStencilAttachment.layout;
attachments.push_back(depthStencilAttachment);

VkAccessFlags texBeginAccessFlags = 0;
VkPipelineStageFlags texBeginStageFlags = 0;
Vulkan::setImageLayoutTransitionOptions(true, true, true, configuration.staticData.depthStencilAttachment.layout, texBeginAccessFlags, texBeginStageFlags);
beginDependency.srcAccessMask |= texBeginAccessFlags;
beginDependency.srcStageMask |= texBeginStageFlags;

VkAccessFlags texEndAccessFlags = 0;
VkPipelineStageFlags texEndStageFlags = 0;
Vulkan::setImageLayoutTransitionOptions(false, true, true, configuration.staticData.depthStencilAttachment.layout, texEndAccessFlags, texEndStageFlags);
endDependency.dstAccessMask |= texEndAccessFlags;
endDependency.dstStageMask |= texEndStageFlags;
Vulkan::addImageLayoutTransitionOptions(true, true, true, configuration.staticData.depthStencilAttachment.layout, beginDependency.srcAccessMask, beginDependency.srcStageMask);
Vulkan::addImageLayoutTransitionOptions(false, true, true, configuration.staticData.depthStencilAttachment.layout, endDependency.dstAccessMask, endDependency.dstStageMask);
}

VkAttachmentReference colorAttachmentResolveRef{};
// Add resolve attachments after everything else to make pClearValues simpler to implement.
if (configuration.staticData.resolve)
{
colorAttachmentResolveRef.attachment = attachment++;
colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
subPass.pResolveAttachments = &colorAttachmentResolveRef;
for (const auto &colorAttachment : configuration.colorAttachments)
{
VkAttachmentReference reference{};
reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

VkAttachmentDescription colorAttachmentResolve{};
colorAttachmentResolve.format = configuration.colorAttachments.at(0).format;
colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments.push_back(colorAttachmentResolve);
if (colorAttachment.layout == VK_IMAGE_LAYOUT_UNDEFINED)
{
reference.attachment = VK_ATTACHMENT_UNUSED;
colorResolveAttachmentRefs.push_back(reference);
}
else
{
reference.attachment = attachment++;
colorResolveAttachmentRefs.push_back(reference);

VkAttachmentDescription resolveDescription{};
resolveDescription.format = colorAttachment.format;
resolveDescription.samples = VK_SAMPLE_COUNT_1_BIT;
resolveDescription.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
resolveDescription.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
resolveDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
resolveDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
resolveDescription.initialLayout = colorAttachment.layout;
resolveDescription.finalLayout = colorAttachment.layout;

attachments.push_back(resolveDescription);
}
}

subPass.pResolveAttachments = colorResolveAttachmentRefs.data();
}

std::array<VkSubpassDependency, 2> dependencies = { beginDependency, endDependency };
Expand Down Expand Up @@ -2496,8 +2509,6 @@ void Graphics::setDefaultRenderPass()

RenderPassConfiguration renderPassConfiguration{};

renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples });

VkFormat dsformat = backbufferHasDepth || backbufferHasStencil ? depthStencilFormat : VK_FORMAT_UNDEFINED;
renderPassConfiguration.staticData.depthStencilAttachment = { dsformat, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples };
if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
Expand All @@ -2512,19 +2523,22 @@ void Graphics::setDefaultRenderPass()

if (msaaSamples & VK_SAMPLE_COUNT_1_BIT)
{
renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples });

if (!swapChainImageViews.empty())
framebufferConfiguration.colorViews.push_back(swapChainImageViews.at(imageIndex));
else
framebufferConfiguration.colorViews.push_back(fakeBackbuffer->getRenderTargetView(0, 0));
framebufferConfiguration.staticData.resolveView = VK_NULL_HANDLE;
}
else
{
renderPassConfiguration.colorAttachments.push_back({ swapChainImageFormat, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ATTACHMENT_LOAD_OP_LOAD, msaaSamples });

framebufferConfiguration.colorViews.push_back(colorImageView);
if (!swapChainImageViews.empty())
framebufferConfiguration.staticData.resolveView = swapChainImageViews.at(imageIndex);
framebufferConfiguration.colorResolveViews.push_back(swapChainImageViews.at(imageIndex));
else
framebufferConfiguration.staticData.resolveView = fakeBackbuffer->getRenderTargetView(0, 0);
framebufferConfiguration.colorResolveViews.push_back(fakeBackbuffer->getRenderTargetView(0, 0));
}

renderPassState.renderPassConfiguration = std::move(renderPassConfiguration);
Expand Down Expand Up @@ -2554,35 +2568,62 @@ void Graphics::setDefaultRenderPass()
}
}

void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture)
void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh)
{
// fixme: hasSRGBtexture
RenderPassConfiguration renderPassConfiguration{};
VkSampleCountFlagBits msaa = VK_SAMPLE_COUNT_1_BIT;

for (const auto &color : rts.colors)
{
auto tex = (Texture *)color.texture;
renderPassConfiguration.colorAttachments.push_back({
Vulkan::getTextureFormat(color.texture->getPixelFormat()).internalFormat,
((Texture*)color.texture)->getImageLayout(),
Vulkan::getTextureFormat(tex->getPixelFormat()).internalFormat,
tex->getImageLayout(),
tex->getMSAAImageLayout(),
VK_ATTACHMENT_LOAD_OP_LOAD,
dynamic_cast<Texture*>(color.texture)->getMsaaSamples() });
tex->getMsaaSamples() });

if (tex->getMSAAImageLayout() != VK_IMAGE_LAYOUT_UNDEFINED && tex->getImageLayout() != VK_IMAGE_LAYOUT_UNDEFINED)
renderPassConfiguration.staticData.resolve = true;

msaa = tex->getMsaaSamples();
}

if (rts.depthStencil.texture != nullptr)
{
auto tex = (Texture *)rts.depthStencil.texture;
renderPassConfiguration.staticData.depthStencilAttachment = {
Vulkan::getTextureFormat(rts.depthStencil.texture->getPixelFormat()).internalFormat,
((Texture*)rts.depthStencil.texture)->getImageLayout(),
tex->getImageLayout(),
VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_LOAD_OP_LOAD,
dynamic_cast<Texture*>(rts.depthStencil.texture)->getMsaaSamples() };
tex->getMsaaSamples() };

msaa = tex->getMsaaSamples();
}

FramebufferConfiguration configuration{};

for (const auto &color : rts.colors)
{
auto tex = (Texture*)color.texture;
configuration.colorViews.push_back(tex->getRenderTargetView(color.mipmap, color.slice));
if (tex->getMSAA() > 1)
{
configuration.colorViews.push_back(tex->getMSAARenderTargetView(color.mipmap, color.slice));
configuration.colorResolveViews.push_back(tex->getRenderTargetView(color.mipmap, color.slice));
}
else
{
configuration.colorViews.push_back(tex->getRenderTargetView(color.mipmap, color.slice));
}
}
if (rts.depthStencil.texture != nullptr)
{
auto tex = (Texture*)rts.depthStencil.texture;
configuration.staticData.depthView = tex->getRenderTargetView(rts.depthStencil.mipmap, rts.depthStencil.slice);
if (tex->getMSAA() > 1)
configuration.staticData.depthView = tex->getMSAARenderTargetView(rts.depthStencil.mipmap, rts.depthStencil.slice);
else
configuration.staticData.depthView = tex->getRenderTargetView(rts.depthStencil.mipmap, rts.depthStencil.slice);
}

configuration.staticData.width = static_cast<uint32_t>(pixelw);
Expand All @@ -2606,7 +2647,7 @@ void Graphics::setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, b
renderPassState.pipeline = VK_NULL_HANDLE;
renderPassState.width = static_cast<float>(pixelw);
renderPassState.height = static_cast<float>(pixelh);
renderPassState.msaa = VK_SAMPLE_COUNT_1_BIT;
renderPassState.msaa = msaa;
renderPassState.numColorAttachments = static_cast<uint32_t>(rts.colors.size());
renderPassState.packedColorAttachmentFormats = 0;
for (size_t i = 0; i < rts.colors.size(); i++)
Expand Down
9 changes: 6 additions & 3 deletions src/modules/graphics/vulkan/Graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ struct ColorAttachment
{
VkFormat format = VK_FORMAT_UNDEFINED;
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImageLayout msaaLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkAttachmentLoadOp loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT;

bool operator==(const ColorAttachment &attachment) const
{
return format == attachment.format &&
layout == attachment.layout &&
msaaLayout == attachment.msaaLayout &&
loadOp == attachment.loadOp &&
msaaSamples == attachment.msaaSamples;
}
Expand Down Expand Up @@ -112,11 +114,11 @@ struct RenderPassConfigurationHasher
struct FramebufferConfiguration
{
std::vector<VkImageView> colorViews;
std::vector<VkImageView> colorResolveViews;

struct StaticFramebufferConfiguration
{
VkImageView depthView = VK_NULL_HANDLE;
VkImageView resolveView = VK_NULL_HANDLE;

uint32_t width = 0;
uint32_t height = 0;
Expand All @@ -126,7 +128,7 @@ struct FramebufferConfiguration

bool operator==(const FramebufferConfiguration &conf) const
{
return colorViews == conf.colorViews &&
return colorViews == conf.colorViews && colorResolveViews == conf.colorResolveViews &&
(memcmp(&staticData, &conf.staticData, sizeof(StaticFramebufferConfiguration)) == 0);
}
};
Expand All @@ -137,6 +139,7 @@ struct FramebufferConfigurationHasher
{
size_t hashes[] = {
XXH32(configuration.colorViews.data(), configuration.colorViews.size() * sizeof(VkImageView), 0),
XXH32(configuration.colorResolveViews.data(), configuration.colorResolveViews.size() * sizeof(VkImageView), 0),
XXH32(&configuration.staticData, sizeof(configuration.staticData), 0),
};

Expand Down Expand Up @@ -337,7 +340,7 @@ class Graphics final : public love::graphics::Graphics
VertexAttributesID attributesID,
const BufferBindings &buffers, graphics::Texture *texture,
PrimitiveType, CullMode);
void setRenderPass(const RenderTargets &rts, int pixelw, int pixelh, bool hasSRGBtexture);
void setRenderPass(const RenderTargets &rts, int pixelw, int pixelh);
void setDefaultRenderPass();
void startRenderPass();
void endRenderPass();
Expand Down
2 changes: 1 addition & 1 deletion src/modules/graphics/vulkan/Shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,7 @@ void Shader::setTextureDescriptor(const UniformInfo *info, love::graphics::Textu

// Samplers may change after this call, so they're set just before the
// descriptor set is used instead of here.
VkImageView view = vkTexture != nullptr ? (VkImageView)vkTexture->getRenderTargetHandle() : VK_NULL_HANDLE;
VkImageView view = vkTexture != nullptr ? (VkImageView)vkTexture->getHandle() : VK_NULL_HANDLE;
if (view != imageInfo.imageView)
{
imageInfo.imageLayout = vkTexture != nullptr ? vkTexture->getImageLayout() : VK_IMAGE_LAYOUT_UNDEFINED;
Expand Down
Loading

0 comments on commit fe4fc84

Please sign in to comment.