mirror of
https://github.com/love2d/love.git
synced 2026-08-15 15:51:12 +02:00
vulkan: implement MSAA textures.
This commit is contained in:
@@ -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())
|
||||
{
|
||||
@@ -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())
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
@@ -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{};
|
||||
@@ -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 };
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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++)
|
||||
|
||||
@@ -50,6 +50,7 @@ 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;
|
||||
|
||||
@@ -57,6 +58,7 @@ struct ColorAttachment
|
||||
{
|
||||
return format == attachment.format &&
|
||||
layout == attachment.layout &&
|
||||
msaaLayout == attachment.msaaLayout &&
|
||||
loadOp == attachment.loadOp &&
|
||||
msaaSamples == attachment.msaaSamples;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
@@ -337,7 +340,7 @@ private:
|
||||
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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Buffer.h"
|
||||
|
||||
#include <limits>
|
||||
#include <array>
|
||||
|
||||
namespace love
|
||||
{
|
||||
@@ -132,7 +133,7 @@ bool Texture::loadVolatile()
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
imageInfo.usage = usageFlags;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.samples = msaaSamples;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkImageFormatListCreateInfo viewFormatsInfo{};
|
||||
viewFormatsInfo.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO;
|
||||
@@ -153,26 +154,54 @@ bool Texture::loadVolatile()
|
||||
|
||||
imageAllocationCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
|
||||
|
||||
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &textureImage, &textureImageAllocation, nullptr) != VK_SUCCESS)
|
||||
throw love::Exception("failed to create image");
|
||||
if ((msaaSamples & VK_SAMPLE_COUNT_1_BIT) != 0 || readable)
|
||||
{
|
||||
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &imageData.image, &imageData.allocation, nullptr) != VK_SUCCESS)
|
||||
throw love::Exception("failed to create image");
|
||||
}
|
||||
|
||||
if ((msaaSamples & VK_SAMPLE_COUNT_1_BIT) == 0)
|
||||
{
|
||||
imageInfo.samples = msaaSamples;
|
||||
if (vmaCreateImage(allocator, &imageInfo, &imageAllocationCreateInfo, &msaaImageData.image, &msaaImageData.allocation, nullptr) != VK_SUCCESS)
|
||||
throw love::Exception("failed to create MSAA image");
|
||||
}
|
||||
|
||||
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
|
||||
|
||||
if (computeWrite)
|
||||
imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
imageData.layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
else if (readable)
|
||||
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
imageData.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
else if (renderTarget && isPixelFormatDepthStencil(format))
|
||||
imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
imageData.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
else if (renderTarget)
|
||||
imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
imageData.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
else // TODO: is there a better layout for this situation?
|
||||
imageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
imageData.layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, imageLayout,
|
||||
0, VK_REMAINING_MIP_LEVELS,
|
||||
0, VK_REMAINING_ARRAY_LAYERS);
|
||||
if (imageData.image != VK_NULL_HANDLE)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, imageData.layout,
|
||||
0, VK_REMAINING_MIP_LEVELS,
|
||||
0, VK_REMAINING_ARRAY_LAYERS);
|
||||
}
|
||||
|
||||
if (msaaImageData.image != VK_NULL_HANDLE)
|
||||
{
|
||||
if (renderTarget && isPixelFormatDepthStencil(format))
|
||||
msaaImageData.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
else if (renderTarget)
|
||||
msaaImageData.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
else // TODO: is there a better layout for this situation?
|
||||
msaaImageData.layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, msaaImageData.image, format, renderTarget,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED, msaaImageData.layout,
|
||||
0, VK_REMAINING_MIP_LEVELS,
|
||||
0, VK_REMAINING_ARRAY_LAYERS);
|
||||
}
|
||||
|
||||
bool hasdata = slices.get(0, 0) != nullptr;
|
||||
|
||||
@@ -195,14 +224,20 @@ bool Texture::loadVolatile()
|
||||
}
|
||||
}
|
||||
else
|
||||
clear();
|
||||
{
|
||||
if (imageData.image != VK_NULL_HANDLE)
|
||||
clear(imageData);
|
||||
if (msaaImageData.image != VK_NULL_HANDLE)
|
||||
clear(msaaImageData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Texture *roottex = (Texture *) rootView.texture;
|
||||
textureImage = roottex->textureImage;
|
||||
textureImageAllocation = VK_NULL_HANDLE;
|
||||
imageLayout = roottex->imageLayout;
|
||||
imageData = roottex->imageData;
|
||||
msaaImageData = roottex->msaaImageData;
|
||||
imageData.allocation = VK_NULL_HANDLE;
|
||||
msaaImageData.allocation = VK_NULL_HANDLE;
|
||||
msaaSamples = roottex->msaaSamples;
|
||||
}
|
||||
|
||||
@@ -214,34 +249,8 @@ bool Texture::loadVolatile()
|
||||
|
||||
if (renderTarget)
|
||||
{
|
||||
renderTargetImageViews.resize(getMipmapCount());
|
||||
for (int mip = 0; mip < getMipmapCount(); mip++)
|
||||
{
|
||||
renderTargetImageViews.at(mip).resize(layerCount);
|
||||
|
||||
for (int slice = 0; slice < layerCount; slice++)
|
||||
{
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = textureImage;
|
||||
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
|
||||
if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_CUBE)
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = vulkanFormat.internalFormat;
|
||||
viewInfo.subresourceRange.aspectMask = imageAspect;
|
||||
viewInfo.subresourceRange.baseMipLevel = mip + rootView.startMipmap;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = slice + rootView.startLayer;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
viewInfo.components.r = vulkanFormat.swizzleR;
|
||||
viewInfo.components.g = vulkanFormat.swizzleG;
|
||||
viewInfo.components.b = vulkanFormat.swizzleB;
|
||||
viewInfo.components.a = vulkanFormat.swizzleA;
|
||||
|
||||
if (vkCreateImageView(device, &viewInfo, nullptr, &renderTargetImageViews.at(mip).at(slice)) != VK_SUCCESS)
|
||||
throw love::Exception("could not create render target image view");
|
||||
}
|
||||
}
|
||||
createRenderTargetImageViews(imageData);
|
||||
createRenderTargetImageViews(msaaImageData);
|
||||
}
|
||||
|
||||
if (!debugName.empty())
|
||||
@@ -253,15 +262,25 @@ bool Texture::loadVolatile()
|
||||
if (root)
|
||||
{
|
||||
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;
|
||||
nameInfo.objectHandle = (uint64_t)textureImage;
|
||||
nameInfo.objectHandle = (uint64_t)imageData.image;
|
||||
}
|
||||
else
|
||||
{
|
||||
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE_VIEW;
|
||||
nameInfo.objectHandle = (uint64_t)textureImageView;
|
||||
nameInfo.objectHandle = (uint64_t)imageData.imageView;
|
||||
}
|
||||
nameInfo.pObjectName = debugName.c_str();
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
|
||||
if (msaaImageData.image != VK_NULL_HANDLE && root)
|
||||
{
|
||||
nameInfo.objectType = VK_OBJECT_TYPE_IMAGE;
|
||||
nameInfo.objectHandle = (uint64_t)msaaImageData.image;
|
||||
|
||||
std::string msaaName = debugName + " (MSAA buffer)";
|
||||
nameInfo.pObjectName = msaaName.c_str();
|
||||
vkSetDebugUtilsObjectNameEXT(device, &nameInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,26 +291,29 @@ bool Texture::loadVolatile()
|
||||
|
||||
void Texture::unloadVolatile()
|
||||
{
|
||||
if (textureImage == VK_NULL_HANDLE)
|
||||
return;
|
||||
std::array<VulkanImageData *, 2> datas = {&imageData, &msaaImageData};
|
||||
|
||||
vgfx->queueCleanUp([
|
||||
device = device,
|
||||
textureImageView = textureImageView,
|
||||
allocator = allocator,
|
||||
textureImage = textureImage,
|
||||
textureImageAllocation = textureImageAllocation,
|
||||
textureImageViews = std::move(renderTargetImageViews)] () {
|
||||
vkDestroyImageView(device, textureImageView, nullptr);
|
||||
if (textureImageAllocation)
|
||||
vmaDestroyImage(allocator, textureImage, textureImageAllocation);
|
||||
for (const auto &views : textureImageViews)
|
||||
for (const auto &view : views)
|
||||
vkDestroyImageView(device, view, nullptr);
|
||||
});
|
||||
for (const VulkanImageData *data : datas)
|
||||
{
|
||||
if (data->image == VK_NULL_HANDLE)
|
||||
continue;
|
||||
|
||||
textureImage = VK_NULL_HANDLE;
|
||||
textureImageAllocation = VK_NULL_HANDLE;
|
||||
vgfx->queueCleanUp([
|
||||
device = device,
|
||||
allocator = allocator,
|
||||
imageData = std::move(*data)] () {
|
||||
if (imageData.imageView != VK_NULL_HANDLE)
|
||||
vkDestroyImageView(device, imageData.imageView, nullptr);
|
||||
if (imageData.allocation != VK_NULL_HANDLE)
|
||||
vmaDestroyImage(allocator, imageData.image, imageData.allocation);
|
||||
for (const auto &views : imageData.renderTargetImageViews)
|
||||
for (const auto &view : views)
|
||||
vkDestroyImageView(device, view, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
imageData = {};
|
||||
msaaImageData = {};
|
||||
|
||||
updateGraphicsMemorySize(false);
|
||||
}
|
||||
@@ -303,7 +325,7 @@ Texture::~Texture()
|
||||
|
||||
ptrdiff_t Texture::getRenderTargetHandle() const
|
||||
{
|
||||
return (ptrdiff_t)textureImageView;
|
||||
return (ptrdiff_t)imageData.image;
|
||||
}
|
||||
|
||||
ptrdiff_t Texture::getSamplerHandle() const
|
||||
@@ -313,7 +335,16 @@ ptrdiff_t Texture::getSamplerHandle() const
|
||||
|
||||
VkImageView Texture::getRenderTargetView(int mip, int layer)
|
||||
{
|
||||
return renderTargetImageViews.at(mip).at(layer);
|
||||
if (imageData.renderTargetImageViews.empty())
|
||||
return imageData.imageView;
|
||||
return imageData.renderTargetImageViews.at(mip).at(layer);
|
||||
}
|
||||
|
||||
VkImageView Texture::getMSAARenderTargetView(int mip, int layer)
|
||||
{
|
||||
if (msaaImageData.renderTargetImageViews.empty())
|
||||
return msaaImageData.imageView;
|
||||
return msaaImageData.renderTargetImageViews.at(mip).at(layer);
|
||||
}
|
||||
|
||||
VkSampleCountFlagBits Texture::getMsaaSamples() const
|
||||
@@ -328,7 +359,7 @@ int Texture::getMSAA() const
|
||||
|
||||
ptrdiff_t Texture::getHandle() const
|
||||
{
|
||||
return (ptrdiff_t)textureImage;
|
||||
return (ptrdiff_t)imageData.imageView;
|
||||
}
|
||||
|
||||
void Texture::setSamplerState(const SamplerState &s)
|
||||
@@ -339,7 +370,12 @@ void Texture::setSamplerState(const SamplerState &s)
|
||||
|
||||
VkImageLayout Texture::getImageLayout() const
|
||||
{
|
||||
return imageLayout;
|
||||
return imageData.layout;
|
||||
}
|
||||
|
||||
VkImageLayout Texture::getMSAAImageLayout() const
|
||||
{
|
||||
return msaaImageData.layout;
|
||||
}
|
||||
|
||||
void Texture::createTextureImageView()
|
||||
@@ -347,7 +383,7 @@ void Texture::createTextureImageView()
|
||||
auto vulkanFormat = Vulkan::getTextureFormat(format);
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = textureImage;
|
||||
viewInfo.image = imageData.image;
|
||||
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
|
||||
viewInfo.format = vulkanFormat.internalFormat;
|
||||
viewInfo.subresourceRange.aspectMask = imageAspect;
|
||||
@@ -364,11 +400,56 @@ void Texture::createTextureImageView()
|
||||
viewInfo.components.b = vulkanFormat.swizzleB;
|
||||
viewInfo.components.a = vulkanFormat.swizzleA;
|
||||
|
||||
if (vkCreateImageView(device, &viewInfo, nullptr, &textureImageView) != VK_SUCCESS)
|
||||
throw love::Exception("could not create texture image view");
|
||||
imageData.imageView = VK_NULL_HANDLE;
|
||||
if (imageData.image != VK_NULL_HANDLE && readable)
|
||||
{
|
||||
if (vkCreateImageView(device, &viewInfo, nullptr, &imageData.imageView) != VK_SUCCESS)
|
||||
throw love::Exception("could not create texture image view");
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::clear()
|
||||
void Texture::createRenderTargetImageViews(VulkanImageData &data)
|
||||
{
|
||||
if (data.image == VK_NULL_HANDLE || (getMipmapCount() == 1 && layerCount == 1 && data.imageView != VK_NULL_HANDLE))
|
||||
{
|
||||
// Reuse the main image view in this situation, if it exists.
|
||||
data.renderTargetImageViews.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
auto vulkanFormat = Vulkan::getTextureFormat(format);
|
||||
|
||||
data.renderTargetImageViews.resize(getMipmapCount());
|
||||
for (int mip = 0; mip < getMipmapCount(); mip++)
|
||||
{
|
||||
data.renderTargetImageViews.at(mip).resize(layerCount);
|
||||
|
||||
for (int slice = 0; slice < layerCount; slice++)
|
||||
{
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = data.image;
|
||||
viewInfo.viewType = Vulkan::getImageViewType(getTextureType());
|
||||
if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_CUBE)
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = vulkanFormat.internalFormat;
|
||||
viewInfo.subresourceRange.aspectMask = imageAspect;
|
||||
viewInfo.subresourceRange.baseMipLevel = mip + rootView.startMipmap;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = slice + rootView.startLayer;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
viewInfo.components.r = vulkanFormat.swizzleR;
|
||||
viewInfo.components.g = vulkanFormat.swizzleG;
|
||||
viewInfo.components.b = vulkanFormat.swizzleB;
|
||||
viewInfo.components.a = vulkanFormat.swizzleA;
|
||||
|
||||
if (vkCreateImageView(device, &viewInfo, nullptr, &data.renderTargetImageViews.at(mip).at(slice)) != VK_SUCCESS)
|
||||
throw love::Exception("could not create render target image view");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::clear(const VulkanImageData &data)
|
||||
{
|
||||
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
|
||||
|
||||
@@ -379,12 +460,12 @@ void Texture::clear()
|
||||
range.baseArrayLayer = 0;
|
||||
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
|
||||
|
||||
VkImageLayout clearLayout = imageLayout == VK_IMAGE_LAYOUT_GENERAL ? imageLayout : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
VkImageLayout clearLayout = data.layout == VK_IMAGE_LAYOUT_GENERAL ? data.layout : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
|
||||
if (clearLayout != imageLayout)
|
||||
if (clearLayout != data.layout)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
|
||||
imageLayout, clearLayout,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, data.image, format, renderTarget,
|
||||
data.layout, clearLayout,
|
||||
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
|
||||
}
|
||||
|
||||
@@ -393,18 +474,18 @@ void Texture::clear()
|
||||
VkClearDepthStencilValue depthStencilColor{};
|
||||
depthStencilColor.depth = 0.0f;
|
||||
depthStencilColor.stencil = 0;
|
||||
vkCmdClearDepthStencilImage(commandBuffer, textureImage, clearLayout, &depthStencilColor, 1, &range);
|
||||
vkCmdClearDepthStencilImage(commandBuffer, data.image, clearLayout, &depthStencilColor, 1, &range);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto clearColor = getClearColor(this, ColorD(0, 0, 0, 0));
|
||||
vkCmdClearColorImage(commandBuffer, textureImage, clearLayout, &clearColor, 1, &range);
|
||||
vkCmdClearColorImage(commandBuffer, data.image, clearLayout, &clearColor, 1, &range);
|
||||
}
|
||||
|
||||
if (clearLayout != imageLayout)
|
||||
if (clearLayout != data.layout)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
|
||||
clearLayout, imageLayout,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, data.image, format, renderTarget,
|
||||
clearLayout, data.layout,
|
||||
0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS);
|
||||
}
|
||||
}
|
||||
@@ -450,15 +531,15 @@ void Texture::generateMipmapsInternal()
|
||||
{
|
||||
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
|
||||
imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget,
|
||||
imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
rootView.startMipmap, static_cast<uint32_t>(getMipmapCount()),
|
||||
rootView.startLayer, static_cast<uint32_t>(layerCount));
|
||||
|
||||
VkImageMemoryBarrier barrier{};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.image = textureImage;
|
||||
barrier.image = imageData.image;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
@@ -477,7 +558,7 @@ void Texture::generateMipmapsInternal()
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
0, nullptr,
|
||||
0, nullptr,
|
||||
@@ -498,29 +579,29 @@ void Texture::generateMipmapsInternal()
|
||||
blit.dstSubresource.baseArrayLayer = rootView.startLayer;
|
||||
blit.dstSubresource.layerCount = static_cast<uint32_t>(layerCount);
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
{
|
||||
vkCmdBlitImage(commandBuffer,
|
||||
textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
imageData.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
imageData.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, &blit,
|
||||
VK_FILTER_LINEAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
vkCmdBlitImage(commandBuffer,
|
||||
textureImage, VK_IMAGE_LAYOUT_GENERAL,
|
||||
textureImage, VK_IMAGE_LAYOUT_GENERAL,
|
||||
imageData.image, VK_IMAGE_LAYOUT_GENERAL,
|
||||
imageData.image, VK_IMAGE_LAYOUT_GENERAL,
|
||||
1, &blit,
|
||||
VK_FILTER_LINEAR);
|
||||
}
|
||||
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
barrier.newLayout = imageLayout;
|
||||
barrier.newLayout = imageData.layout;
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
|
||||
0, nullptr,
|
||||
0, nullptr,
|
||||
@@ -529,11 +610,11 @@ void Texture::generateMipmapsInternal()
|
||||
|
||||
barrier.subresourceRange.baseMipLevel = rootView.startMipmap + mipLevels - 1;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
barrier.newLayout = imageLayout;
|
||||
barrier.newLayout = imageData.layout;
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
vkCmdPipelineBarrier(commandBuffer,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
|
||||
0, nullptr,
|
||||
@@ -587,31 +668,31 @@ void Texture::uploadByteData(const void *data, size_t size, int level, int slice
|
||||
|
||||
auto commandBuffer = vgfx->getCommandBufferForDataTransfer();
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
|
||||
imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget,
|
||||
imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
level, 1, baseLayer, 1);
|
||||
|
||||
vkCmdCopyBufferToImage(
|
||||
commandBuffer,
|
||||
stagingBuffer,
|
||||
textureImage,
|
||||
imageData.image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1,
|
||||
®ion
|
||||
);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout,
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageData.layout,
|
||||
level, 1, baseLayer, 1);
|
||||
}
|
||||
else
|
||||
vkCmdCopyBufferToImage(
|
||||
commandBuffer,
|
||||
stagingBuffer,
|
||||
textureImage,
|
||||
imageLayout,
|
||||
imageData.image,
|
||||
imageData.layout,
|
||||
1,
|
||||
®ion
|
||||
);
|
||||
@@ -641,16 +722,16 @@ void Texture::copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int
|
||||
region.imageExtent.width = static_cast<uint32_t>(rect.w);
|
||||
region.imageExtent.height = static_cast<uint32_t>(rect.h);
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1);
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1);
|
||||
|
||||
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), imageData.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1);
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, imageData.layout, layers.mipLevel, 1, layers.baseArrayLayer, 1);
|
||||
}
|
||||
else
|
||||
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), textureImage, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion);
|
||||
vkCmdCopyBufferToImage(commandBuffer, (VkBuffer)source->getHandle(), imageData.image, VK_IMAGE_LAYOUT_GENERAL, 1, ®ion);
|
||||
}
|
||||
|
||||
void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size)
|
||||
@@ -674,16 +755,16 @@ void Texture::copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const
|
||||
region.imageExtent.height = static_cast<uint32_t>(rect.h);
|
||||
region.imageExtent.depth = 1;
|
||||
|
||||
if (imageLayout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
if (imageData.layout != VK_IMAGE_LAYOUT_GENERAL)
|
||||
{
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1);
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, imageData.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layers.mipLevel, 1, layers.baseArrayLayer, 1);
|
||||
|
||||
vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, (VkBuffer) dest->getHandle(), 1, ®ion);
|
||||
vkCmdCopyImageToBuffer(commandBuffer, imageData.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, (VkBuffer) dest->getHandle(), 1, ®ion);
|
||||
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, textureImage, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageLayout, layers.mipLevel, 1, layers.baseArrayLayer, 1);
|
||||
Vulkan::cmdTransitionImageLayout(commandBuffer, imageData.image, format, renderTarget, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, imageData.layout, layers.mipLevel, 1, layers.baseArrayLayer, 1);
|
||||
}
|
||||
else
|
||||
vkCmdCopyImageToBuffer(commandBuffer, textureImage, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, ®ion);
|
||||
vkCmdCopyImageToBuffer(commandBuffer, imageData.image, VK_IMAGE_LAYOUT_GENERAL, (VkBuffer)dest->getHandle(), 1, ®ion);
|
||||
|
||||
// TODO: This could be combined with the cmdTransitionImageLayout barrier.
|
||||
((Buffer *)dest)->postGPUWriteBarrier(commandBuffer);
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
void setSamplerState(const SamplerState &s) override;
|
||||
|
||||
VkImageLayout getImageLayout() const;
|
||||
VkImageLayout getMSAAImageLayout() const;
|
||||
|
||||
void copyFromBuffer(graphics::Buffer *source, size_t sourceoffset, int sourcewidth, size_t size, int slice, int mipmap, const Rect &rect) override;
|
||||
void copyToBuffer(graphics::Buffer *dest, int slice, int mipmap, const Rect &rect, size_t destoffset, int destwidth, size_t size) override;
|
||||
@@ -59,6 +60,7 @@ public:
|
||||
ptrdiff_t getSamplerHandle() const override;
|
||||
|
||||
VkImageView getRenderTargetView(int mip, int layer);
|
||||
VkImageView getMSAARenderTargetView(int mip, int layer);
|
||||
VkSampleCountFlagBits getMsaaSamples() const;
|
||||
|
||||
void uploadByteData(const void *data, size_t size, int level, int slice, const Rect &r) override;
|
||||
@@ -71,18 +73,26 @@ public:
|
||||
static VkClearColorValue getClearColor(love::graphics::Texture *texture, const ColorD &color);
|
||||
|
||||
private:
|
||||
|
||||
struct VulkanImageData
|
||||
{
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VmaAllocation allocation = VK_NULL_HANDLE;
|
||||
VkImageView imageView = VK_NULL_HANDLE;
|
||||
std::vector<std::vector<VkImageView>> renderTargetImageViews;
|
||||
};
|
||||
|
||||
void createTextureImageView();
|
||||
void clear();
|
||||
void createRenderTargetImageViews(VulkanImageData &data);
|
||||
void clear(const VulkanImageData &data);
|
||||
|
||||
Graphics *vgfx = nullptr;
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
VkImageAspectFlags imageAspect;
|
||||
VmaAllocator allocator = VK_NULL_HANDLE;
|
||||
VkImage textureImage = VK_NULL_HANDLE;
|
||||
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VmaAllocation textureImageAllocation = VK_NULL_HANDLE;
|
||||
VkImageView textureImageView = VK_NULL_HANDLE;
|
||||
std::vector<std::vector<VkImageView>> renderTargetImageViews;
|
||||
VulkanImageData imageData;
|
||||
VulkanImageData msaaImageData;
|
||||
VkSampler textureSampler = VK_NULL_HANDLE;
|
||||
Slices slices;
|
||||
int layerCount = 0;
|
||||
|
||||
@@ -822,22 +822,22 @@ VkIndexType Vulkan::getVulkanIndexBufferType(IndexDataType type)
|
||||
}
|
||||
}
|
||||
|
||||
void Vulkan::setImageLayoutTransitionOptions(bool previous, bool renderTarget, bool depthStencil, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags)
|
||||
void Vulkan::addImageLayoutTransitionOptions(bool previous, bool renderTarget, bool depthStencil, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags)
|
||||
{
|
||||
switch (layout)
|
||||
{
|
||||
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||
accessMask = 0;
|
||||
accessMask |= 0;
|
||||
if (previous)
|
||||
stageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
stageFlags |= VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
else
|
||||
stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
stageFlags |= VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_GENERAL:
|
||||
// We use the general image layout for images that are both compute write and readable.
|
||||
// todo: can we optimize this?
|
||||
accessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
|
||||
stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
accessMask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT;
|
||||
stageFlags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
if (renderTarget)
|
||||
{
|
||||
if (depthStencil)
|
||||
@@ -853,28 +853,28 @@ void Vulkan::setImageLayoutTransitionOptions(bool previous, bool renderTarget, b
|
||||
}
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
accessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
stageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
accessMask |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
stageFlags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||
accessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
|
||||
stageFlags = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
accessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
|
||||
stageFlags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
accessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
stageFlags = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
||||
accessMask |= VK_ACCESS_SHADER_READ_BIT;
|
||||
stageFlags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||
accessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
accessMask |= VK_ACCESS_TRANSFER_READ_BIT;
|
||||
stageFlags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
accessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
stageFlags = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
accessMask |= VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
stageFlags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
|
||||
accessMask = 0;
|
||||
stageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
accessMask |= 0;
|
||||
stageFlags |= VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
break;
|
||||
default:
|
||||
throw love::Exception("unimplemented image layout");
|
||||
@@ -897,11 +897,11 @@ void Vulkan::cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImage ima
|
||||
|
||||
const PixelFormatInfo &info = getPixelFormatInfo(format);
|
||||
|
||||
VkPipelineStageFlags sourceStage;
|
||||
VkPipelineStageFlags destinationStage;
|
||||
VkPipelineStageFlags sourceStage = 0;
|
||||
VkPipelineStageFlags destinationStage = 0;
|
||||
|
||||
setImageLayoutTransitionOptions(true, renderTarget, info.depth || info.stencil, oldLayout, barrier.srcAccessMask, sourceStage);
|
||||
setImageLayoutTransitionOptions(false, renderTarget, info.depth || info.stencil, newLayout, barrier.dstAccessMask, destinationStage);
|
||||
addImageLayoutTransitionOptions(true, renderTarget, info.depth || info.stencil, oldLayout, barrier.srcAccessMask, sourceStage);
|
||||
addImageLayoutTransitionOptions(false, renderTarget, info.depth || info.stencil, newLayout, barrier.dstAccessMask, destinationStage);
|
||||
|
||||
if (info.color)
|
||||
barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
@@ -80,8 +80,8 @@ public:
|
||||
static VkStencilOp getStencilOp(StencilAction);
|
||||
static VkIndexType getVulkanIndexBufferType(IndexDataType type);
|
||||
|
||||
static void setImageLayoutTransitionOptions(
|
||||
bool previous, bool renderTarget, bool depthStencil, VkImageLayout layout, VkAccessFlags &accessMask, VkPipelineStageFlags &stageFlags);
|
||||
static void addImageLayoutTransitionOptions(
|
||||
bool previous, bool renderTarget, bool depthStencil, VkImageLayout layout, VkAccessFlags& accessMask, VkPipelineStageFlags& stageFlags);
|
||||
|
||||
static void cmdTransitionImageLayout(
|
||||
VkCommandBuffer, VkImage, PixelFormat format, bool renderTarget, VkImageLayout oldLayout, VkImageLayout newLayout,
|
||||
|
||||
Reference in New Issue
Block a user