core: rename mbgl directories to mln (#4511)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
592
src/mln/vulkan/renderable_resource.cpp
Normal file
592
src/mln/vulkan/renderable_resource.cpp
Normal file
@@ -0,0 +1,592 @@
|
||||
#include <mln/vulkan/renderable_resource.hpp>
|
||||
#include <mln/vulkan/context.hpp>
|
||||
#include <mln/util/logging.hpp>
|
||||
#include <mln/util/constants.hpp>
|
||||
|
||||
namespace mln {
|
||||
namespace vulkan {
|
||||
|
||||
SurfaceRenderableResource::~SurfaceRenderableResource() {
|
||||
if (!backend.getDevice()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
backend.getDevice()->waitIdle(backend.getDispatcher());
|
||||
} catch (const vk::DeviceLostError& error) {
|
||||
Log::Error(mln::Event::Render, "Vulkan device lost during surface shutdown");
|
||||
}
|
||||
|
||||
// specific order
|
||||
swapchainFramebuffers.clear();
|
||||
renderPass.reset();
|
||||
swapchainImageViews.clear();
|
||||
swapchainImages.clear();
|
||||
swapchain.reset();
|
||||
surface.reset();
|
||||
|
||||
depthAllocation.reset();
|
||||
colorAllocations.clear();
|
||||
|
||||
readTexture.reset();
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::initColor(uint32_t w, uint32_t h) {
|
||||
const uint32_t imageCount = backend.getMaxFrames();
|
||||
|
||||
colorAllocations.reserve(imageCount);
|
||||
swapchainImages.reserve(imageCount);
|
||||
|
||||
setColorFormat(vk::Format::eR8G8B8A8Unorm);
|
||||
extent = vk::Extent2D(w, h);
|
||||
|
||||
const auto imageUsage = vk::ImageUsageFlags() | vk::ImageUsageFlagBits::eColorAttachment |
|
||||
vk::ImageUsageFlagBits::eTransferSrc;
|
||||
|
||||
const auto imageCreateInfo = vk::ImageCreateInfo()
|
||||
.setImageType(vk::ImageType::e2D)
|
||||
.setFormat(colorFormat)
|
||||
.setExtent({w, h, 1})
|
||||
.setMipLevels(1)
|
||||
.setArrayLayers(1)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setTiling(vk::ImageTiling::eOptimal)
|
||||
.setUsage(imageUsage)
|
||||
.setSharingMode(vk::SharingMode::eExclusive)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined);
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo = {};
|
||||
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
allocCreateInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
for (uint32_t i = 0; i < imageCount; ++i) {
|
||||
colorAllocations.push_back(std::make_unique<ImageAllocation>(backend.getAllocator()));
|
||||
auto& colorAllocation = colorAllocations.back();
|
||||
|
||||
if (!colorAllocation->create(allocCreateInfo, imageCreateInfo)) {
|
||||
mln::Log::Error(mln::Event::Render, "Vulkan color texture allocation failed");
|
||||
}
|
||||
|
||||
swapchainImages.push_back(colorAllocation->image);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::initSwapchain(uint32_t w, uint32_t h) {
|
||||
const auto& physicalDevice = backend.getPhysicalDevice();
|
||||
const auto& device = backend.getDevice();
|
||||
const auto& dispatcher = backend.getDispatcher();
|
||||
|
||||
const std::vector<vk::SurfaceFormatKHR>& formats = physicalDevice.getSurfaceFormatsKHR(surface.get(), dispatcher);
|
||||
const auto& formatIt = std::find_if(formats.begin(), formats.end(), [](const vk::SurfaceFormatKHR& format) {
|
||||
return (format.format == vk::Format::eB8G8R8A8Unorm || format.format == vk::Format::eR8G8B8A8Unorm) &&
|
||||
format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear;
|
||||
});
|
||||
|
||||
if (formatIt == formats.end()) throw std::runtime_error("No suitable swapchain format found");
|
||||
|
||||
// only vk::PresentModeKHR::eFifo (vsync on) is guaranteed
|
||||
if (presentMode != vk::PresentModeKHR::eFifo) {
|
||||
const auto& presentModes = physicalDevice.getSurfacePresentModesKHR(surface.get(), dispatcher);
|
||||
|
||||
if (std::find(presentModes.begin(), presentModes.end(), presentMode) == presentModes.end()) {
|
||||
mln::Log::Error(
|
||||
mln::Event::Render,
|
||||
"Requested PresentModeKHR not available (" + std::to_string(static_cast<int>(presentMode)) + ")");
|
||||
|
||||
presentMode = vk::PresentModeKHR::eFifo;
|
||||
}
|
||||
}
|
||||
|
||||
vk::ImageUsageFlags imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||
|
||||
capabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface.get(), dispatcher);
|
||||
|
||||
// when reading from the swapchain disable pre-rotation and enable source transfer usage
|
||||
if (surfaceRead) {
|
||||
capabilities.currentTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
|
||||
imageUsage |= vk::ImageUsageFlagBits::eTransferSrc;
|
||||
}
|
||||
|
||||
// pick surface size
|
||||
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
|
||||
extent = capabilities.currentExtent;
|
||||
} else {
|
||||
// update values based on surface limits
|
||||
extent.width = std::min(std::max(w, capabilities.minImageExtent.width), capabilities.maxImageExtent.width);
|
||||
extent.height = std::min(std::max(h, capabilities.minImageExtent.height), capabilities.maxImageExtent.height);
|
||||
}
|
||||
|
||||
if (hasSurfaceTransformSupport()) {
|
||||
if (capabilities.currentTransform & vk::SurfaceTransformFlagBitsKHR::eRotate90 ||
|
||||
capabilities.currentTransform & vk::SurfaceTransformFlagBitsKHR::eRotate270) {
|
||||
std::swap(extent.width, extent.height);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t swapchainImageCount = capabilities.minImageCount + 1;
|
||||
// check surface limits (0 is unlimited)
|
||||
if (capabilities.maxImageCount > 0) {
|
||||
swapchainImageCount = std::min(swapchainImageCount, capabilities.maxImageCount);
|
||||
}
|
||||
|
||||
auto swapchainCreateInfo = vk::SwapchainCreateInfoKHR()
|
||||
.setSurface(surface.get())
|
||||
.setMinImageCount(swapchainImageCount)
|
||||
.setImageFormat(formatIt->format)
|
||||
.setImageColorSpace(formatIt->colorSpace)
|
||||
.setPresentMode(presentMode)
|
||||
.setImageExtent(extent)
|
||||
.setImageArrayLayers(1)
|
||||
.setImageUsage(imageUsage);
|
||||
|
||||
int32_t graphicsQueueIndex = backend.getGraphicsQueueIndex();
|
||||
int32_t presentQueueIndex = backend.getPresentQueueIndex();
|
||||
|
||||
if (graphicsQueueIndex != presentQueueIndex) {
|
||||
// TODO if this scenario is widespread and performance is a problem (shouldn't be the case on most hardware)
|
||||
// rework to vk::SharingMode::eExclusive + queue ownership
|
||||
swapchainCreateInfo.setImageSharingMode(vk::SharingMode::eConcurrent);
|
||||
const std::array<uint32_t, 2> queueIndices = {static_cast<uint32_t>(graphicsQueueIndex),
|
||||
static_cast<uint32_t>(presentQueueIndex)};
|
||||
swapchainCreateInfo.setQueueFamilyIndices(queueIndices);
|
||||
} else {
|
||||
swapchainCreateInfo.setImageSharingMode(vk::SharingMode::eExclusive);
|
||||
}
|
||||
|
||||
swapchainCreateInfo.setPreTransform(hasSurfaceTransformSupport() ? capabilities.currentTransform
|
||||
: vk::SurfaceTransformFlagBitsKHR::eIdentity);
|
||||
swapchainCreateInfo.setClipped(VK_TRUE);
|
||||
|
||||
if (capabilities.supportedCompositeAlpha & vk::CompositeAlphaFlagBitsKHR::eInherit) {
|
||||
swapchainCreateInfo.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eInherit);
|
||||
} else {
|
||||
swapchainCreateInfo.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque);
|
||||
}
|
||||
|
||||
// update this when recreating
|
||||
swapchainCreateInfo.setOldSwapchain(swapchain.get());
|
||||
|
||||
swapchain = device->createSwapchainKHRUnique(swapchainCreateInfo, nullptr, dispatcher);
|
||||
swapchainImages = device->getSwapchainImagesKHR(swapchain.get(), dispatcher);
|
||||
|
||||
setColorFormat(swapchainCreateInfo.imageFormat);
|
||||
extent = swapchainCreateInfo.imageExtent;
|
||||
|
||||
acquireSemaphores.reserve(backend.getMaxFrames());
|
||||
for (uint32_t index = 0; index < backend.getMaxFrames(); ++index) {
|
||||
acquireSemaphores.emplace_back(device->createSemaphoreUnique({}, nullptr, dispatcher));
|
||||
|
||||
const auto indexStr = std::to_string(index);
|
||||
backend.setDebugName(acquireSemaphores.back().get(), "AcquireSemaphore_" + indexStr);
|
||||
}
|
||||
|
||||
presentSemaphores.reserve(swapchainImages.size());
|
||||
for (uint32_t index = 0; index < swapchainImages.size(); ++index) {
|
||||
presentSemaphores.emplace_back(device->createSemaphoreUnique({}, nullptr, dispatcher));
|
||||
|
||||
const auto indexStr = std::to_string(index);
|
||||
backend.setDebugName(presentSemaphores.back().get(), "PresentSemaphore_" + indexStr);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::initDepthStencil() {
|
||||
const auto& physicalDevice = backend.getPhysicalDevice();
|
||||
const auto& device = backend.getDevice();
|
||||
const auto& dispatcher = backend.getDispatcher();
|
||||
|
||||
// check for depth format support
|
||||
const std::vector<vk::Format> formats = {
|
||||
vk::Format::eD24UnormS8Uint,
|
||||
vk::Format::eD32SfloatS8Uint,
|
||||
vk::Format::eD16UnormS8Uint,
|
||||
};
|
||||
|
||||
const auto& formatIt = std::find_if(formats.begin(), formats.end(), [&](const auto& format) {
|
||||
const auto& formatProps = physicalDevice.getFormatProperties(format, dispatcher);
|
||||
return formatProps.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment;
|
||||
});
|
||||
|
||||
if (formatIt == formats.end()) {
|
||||
mln::Log::Error(mln::Event::Render, "Depth/Stencil format not available");
|
||||
return;
|
||||
}
|
||||
|
||||
setDepthFormat(*formatIt);
|
||||
|
||||
const auto imageUsage = vk::ImageUsageFlags() | vk::ImageUsageFlagBits::eDepthStencilAttachment |
|
||||
vk::ImageUsageFlagBits::eTransientAttachment;
|
||||
|
||||
const auto imageCreateInfo = vk::ImageCreateInfo()
|
||||
.setImageType(vk::ImageType::e2D)
|
||||
.setFormat(depthFormat)
|
||||
.setExtent({extent.width, extent.height, 1})
|
||||
.setMipLevels(1)
|
||||
.setArrayLayers(1)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setTiling(vk::ImageTiling::eOptimal)
|
||||
.setUsage(imageUsage)
|
||||
.setSharingMode(vk::SharingMode::eExclusive)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined);
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo = {};
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED;
|
||||
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
|
||||
|
||||
uint32_t lazyMemoryIndex = 0;
|
||||
uint32_t memoryTypeBits = std::numeric_limits<uint32_t>::max();
|
||||
if (vmaFindMemoryTypeIndex(backend.getAllocator(), memoryTypeBits, &allocCreateInfo, &lazyMemoryIndex) !=
|
||||
VK_SUCCESS) {
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
|
||||
}
|
||||
|
||||
depthAllocation = std::make_unique<ImageAllocation>(backend.getAllocator());
|
||||
if (!depthAllocation->create(allocCreateInfo, imageCreateInfo)) {
|
||||
mln::Log::Error(mln::Event::Render, "Vulkan depth texture allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto imageViewCreateInfo =
|
||||
vk::ImageViewCreateInfo()
|
||||
.setImage(depthAllocation->image)
|
||||
.setViewType(vk::ImageViewType::e2D)
|
||||
.setFormat(depthFormat)
|
||||
.setComponents(vk::ComponentMapping()) // defaults to vk::ComponentSwizzle::eIdentity
|
||||
.setSubresourceRange(vk::ImageSubresourceRange(
|
||||
vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil, 0, 1, 0, 1));
|
||||
|
||||
depthAllocation->imageView = device->createImageViewUnique(imageViewCreateInfo, nullptr, dispatcher);
|
||||
|
||||
backend.setDebugName(depthAllocation->image, "SwapchainDepthImage");
|
||||
backend.setDebugName(depthAllocation->imageView.get(), "SwapchainDepthImageView");
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::initRenderPass() {
|
||||
// The current render pass should be invalidated if:
|
||||
// - color/depth format changes (use setColorFormat/setDepthFormat)
|
||||
// - renderable resource type changes
|
||||
// - this is done currently only by inheritance and it can't change during runtime
|
||||
|
||||
if (renderPass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& device = backend.getDevice();
|
||||
const auto& dispatcher = backend.getDispatcher();
|
||||
const auto colorLayout = surface ? vk::ImageLayout::ePresentSrcKHR : vk::ImageLayout::eTransferSrcOptimal;
|
||||
|
||||
const std::array<vk::AttachmentDescription, 2> attachments = {
|
||||
vk::AttachmentDescription()
|
||||
.setFormat(colorFormat)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||
.setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
|
||||
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined)
|
||||
.setFinalLayout(colorLayout),
|
||||
|
||||
vk::AttachmentDescription()
|
||||
.setFormat(depthFormat)
|
||||
.setSamples(vk::SampleCountFlagBits::e1)
|
||||
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setStencilLoadOp(vk::AttachmentLoadOp::eClear)
|
||||
.setStencilStoreOp(vk::AttachmentStoreOp::eDontCare)
|
||||
.setInitialLayout(vk::ImageLayout::eUndefined)
|
||||
.setFinalLayout(vk::ImageLayout::eDepthStencilAttachmentOptimal)};
|
||||
|
||||
const vk::AttachmentReference colorAttachmentRef(0, vk::ImageLayout::eColorAttachmentOptimal);
|
||||
const vk::AttachmentReference depthAttachmentRef(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||
|
||||
const auto subpass = vk::SubpassDescription()
|
||||
.setPipelineBindPoint(vk::PipelineBindPoint::eGraphics)
|
||||
.setColorAttachmentCount(1)
|
||||
.setColorAttachments(colorAttachmentRef)
|
||||
.setPDepthStencilAttachment(&depthAttachmentRef);
|
||||
|
||||
const std::array<vk::SubpassDependency, 2> dependencies = {
|
||||
vk::SubpassDependency()
|
||||
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
|
||||
.setDstSubpass(0)
|
||||
.setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
||||
.setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
||||
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
|
||||
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
|
||||
.setDependencyFlags(vk::DependencyFlagBits::eByRegion),
|
||||
|
||||
vk::SubpassDependency()
|
||||
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
|
||||
.setDstSubpass(0)
|
||||
.setSrcStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
||||
vk::PipelineStageFlagBits::eLateFragmentTests)
|
||||
.setDstStageMask(vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
||||
vk::PipelineStageFlagBits::eLateFragmentTests)
|
||||
.setSrcAccessMask(vk::AccessFlagBits::eDepthStencilAttachmentWrite)
|
||||
.setDstAccessMask(vk::AccessFlagBits::eDepthStencilAttachmentWrite)
|
||||
.setDependencyFlags(vk::DependencyFlagBits::eByRegion),
|
||||
};
|
||||
|
||||
const auto renderPassCreateInfo =
|
||||
vk::RenderPassCreateInfo().setAttachments(attachments).setSubpasses(subpass).setDependencies(dependencies);
|
||||
|
||||
renderPass = device->createRenderPassUnique(renderPassCreateInfo, nullptr, dispatcher);
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::setColorFormat(vk::Format format) {
|
||||
if (colorFormat == format) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderPass.reset();
|
||||
colorFormat = format;
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::setDepthFormat(vk::Format format) {
|
||||
if (depthFormat == format) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderPass.reset();
|
||||
depthFormat = format;
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::swap() {
|
||||
auto& context = static_cast<Context&>(backend.getContext());
|
||||
|
||||
if (surfaceRead) {
|
||||
copySurfaceToReadTexture();
|
||||
}
|
||||
|
||||
context.submitFrame();
|
||||
|
||||
if (surfaceRead) {
|
||||
context.waitFrame();
|
||||
surfaceRead = false;
|
||||
}
|
||||
}
|
||||
|
||||
const vk::Image SurfaceRenderableResource::getAcquiredImage() const {
|
||||
if (surface) {
|
||||
return swapchainImages[acquiredImageIndex];
|
||||
}
|
||||
|
||||
return colorAllocations[acquiredImageIndex]->image;
|
||||
}
|
||||
|
||||
const vk::Semaphore& SurfaceRenderableResource::getAcquireSemaphore() const {
|
||||
const auto& context = static_cast<const Context&>(backend.getContext());
|
||||
return acquireSemaphores[context.getCurrentFrameResourceIndex()].get();
|
||||
}
|
||||
|
||||
const vk::Semaphore& SurfaceRenderableResource::getPresentSemaphore() const {
|
||||
return presentSemaphores[acquiredImageIndex].get();
|
||||
}
|
||||
|
||||
bool SurfaceRenderableResource::hasSurfaceTransformSupport() const {
|
||||
return surface && capabilities.supportedTransforms != vk::SurfaceTransformFlagBitsKHR::eIdentity;
|
||||
}
|
||||
|
||||
bool SurfaceRenderableResource::didSurfaceTransformUpdate() const {
|
||||
const auto& physicalDevice = backend.getPhysicalDevice();
|
||||
const auto& updatedCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface.get(), backend.getDispatcher());
|
||||
|
||||
return capabilities.currentTransform != updatedCapabilities.currentTransform;
|
||||
}
|
||||
|
||||
float SurfaceRenderableResource::getRotation() const {
|
||||
switch (capabilities.currentTransform) {
|
||||
default:
|
||||
case vk::SurfaceTransformFlagBitsKHR::eIdentity:
|
||||
return 0.0f * M_PI / 180.0f;
|
||||
|
||||
case vk::SurfaceTransformFlagBitsKHR::eRotate90:
|
||||
case vk::SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate90:
|
||||
return 90.0f * M_PI / 180.0f;
|
||||
|
||||
case vk::SurfaceTransformFlagBitsKHR::eRotate180:
|
||||
case vk::SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate180:
|
||||
return 180.0f * M_PI / 180.0f;
|
||||
|
||||
case vk::SurfaceTransformFlagBitsKHR::eRotate270:
|
||||
case vk::SurfaceTransformFlagBitsKHR::eHorizontalMirrorRotate270:
|
||||
return 270.0f * M_PI / 180.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::init(uint32_t w, uint32_t h) {
|
||||
if (surface) {
|
||||
initSwapchain(w, h);
|
||||
} else {
|
||||
initColor(w, h);
|
||||
}
|
||||
|
||||
const auto& device = backend.getDevice();
|
||||
const auto& dispatcher = backend.getDispatcher();
|
||||
|
||||
// create swapchain image views
|
||||
swapchainImageViews.reserve(swapchainImages.size());
|
||||
|
||||
auto imageViewCreateInfo = vk::ImageViewCreateInfo()
|
||||
.setViewType(vk::ImageViewType::e2D)
|
||||
.setFormat(colorFormat)
|
||||
.setComponents(vk::ComponentMapping()) // defaults to vk::ComponentSwizzle::eIdentity
|
||||
.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
|
||||
for (const auto& image : swapchainImages) {
|
||||
imageViewCreateInfo.setImage(image);
|
||||
swapchainImageViews.push_back(device->createImageViewUnique(imageViewCreateInfo, nullptr, dispatcher));
|
||||
|
||||
const size_t index = swapchainImageViews.size() - 1;
|
||||
backend.setDebugName(image, "SwapchainImage_" + std::to_string(index));
|
||||
backend.setDebugName(image, "SwapchainImageView_" + std::to_string(index));
|
||||
}
|
||||
|
||||
// depth resources
|
||||
initDepthStencil();
|
||||
|
||||
// create render pass
|
||||
initRenderPass();
|
||||
|
||||
// create swapchain framebuffers
|
||||
swapchainFramebuffers.reserve(swapchainImageViews.size());
|
||||
|
||||
auto framebufferCreateInfo = vk::FramebufferCreateInfo()
|
||||
.setRenderPass(renderPass.get())
|
||||
.setAttachmentCount(2)
|
||||
.setWidth(extent.width)
|
||||
.setHeight(extent.height)
|
||||
.setLayers(1);
|
||||
|
||||
for (const auto& imageView : swapchainImageViews) {
|
||||
const std::array<vk::ImageView, 2> imageViews = {imageView.get(), depthAllocation->imageView.get()};
|
||||
|
||||
framebufferCreateInfo.setAttachments(imageViews);
|
||||
swapchainFramebuffers.push_back(device->createFramebufferUnique(framebufferCreateInfo, nullptr, dispatcher));
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::recreateSwapchain() {
|
||||
if (!surface) return;
|
||||
|
||||
backend.getDevice()->waitIdle(backend.getDispatcher());
|
||||
|
||||
swapchainFramebuffers.clear();
|
||||
swapchainImageViews.clear();
|
||||
swapchainImages.clear();
|
||||
acquireSemaphores.clear();
|
||||
presentSemaphores.clear();
|
||||
|
||||
readTexture.reset();
|
||||
|
||||
init(extent.width, extent.height);
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::queueSurfaceRead() {
|
||||
if (surfaceRead) {
|
||||
return;
|
||||
}
|
||||
|
||||
surfaceRead = true;
|
||||
if (surface) {
|
||||
backend.getContext<Context>().requestSurfaceUpdate(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceRenderableResource::copySurfaceToReadTexture() {
|
||||
auto& contextImpl = backend.getContext<Context>();
|
||||
const auto& dispatcher = backend.getDispatcher();
|
||||
const auto& physicalDevice = backend.getPhysicalDevice();
|
||||
|
||||
if (!readTexture) {
|
||||
readTexture = std::make_unique<Texture2D>(contextImpl);
|
||||
readTexture->setFormat(gfx::TexturePixelType::RGBA, gfx::TextureChannelDataType::UnsignedByte);
|
||||
readTexture->setUsage(Texture2DUsage::Read);
|
||||
}
|
||||
|
||||
readTexture->setSize({extent.width, extent.height});
|
||||
|
||||
const auto swapchainImage = getAcquiredImage();
|
||||
auto& commandBuffer = contextImpl.getCommandBuffer();
|
||||
|
||||
const auto oldLayout = surface ? vk::ImageLayout::ePresentSrcKHR : vk::ImageLayout::eTransferSrcOptimal;
|
||||
const auto srcStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||
const vk::AccessFlags srcAccess = vk::AccessFlagBits::eColorAttachmentWrite;
|
||||
const vk::AccessFlags dstAccess = vk::AccessFlagBits::eTransferRead;
|
||||
|
||||
const auto readBarrier = vk::ImageMemoryBarrier()
|
||||
.setImage(swapchainImage)
|
||||
.setOldLayout(oldLayout)
|
||||
.setNewLayout(vk::ImageLayout::eTransferSrcOptimal)
|
||||
.setSrcAccessMask(srcAccess)
|
||||
.setDstAccessMask(dstAccess)
|
||||
.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
|
||||
commandBuffer->pipelineBarrier(
|
||||
srcStage, vk::PipelineStageFlagBits::eTransfer, {}, nullptr, nullptr, readBarrier, dispatcher);
|
||||
|
||||
bool useBlit =
|
||||
surface &&
|
||||
(physicalDevice.getFormatProperties(colorFormat, dispatcher).optimalTilingFeatures &
|
||||
vk::FormatFeatureFlagBits::eBlitSrc &&
|
||||
physicalDevice.getFormatProperties(readTexture->getVulkanFormat(), dispatcher).linearTilingFeatures &
|
||||
vk::FormatFeatureFlagBits::eBlitDst);
|
||||
|
||||
if (useBlit) {
|
||||
readTexture->blitImage(swapchainImage, readTexture->getSize(), 0, 0, commandBuffer);
|
||||
} else {
|
||||
readTexture->copyImage(swapchainImage, readTexture->getSize(), 0, 0, commandBuffer);
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
const auto presentBarrier = vk::ImageMemoryBarrier()
|
||||
.setImage(swapchainImage)
|
||||
.setOldLayout(vk::ImageLayout::eTransferSrcOptimal)
|
||||
.setNewLayout(vk::ImageLayout::ePresentSrcKHR)
|
||||
.setSrcAccessMask(vk::AccessFlagBits::eMemoryRead)
|
||||
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
|
||||
.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
|
||||
.setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
|
||||
commandBuffer->pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
||||
vk::PipelineStageFlagBits::eTransfer,
|
||||
{},
|
||||
nullptr,
|
||||
nullptr,
|
||||
presentBarrier,
|
||||
dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<PremultipliedImage> SurfaceRenderableResource::readImage() {
|
||||
if (!readTexture) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto& dispatcher = backend.getDispatcher();
|
||||
const auto& physicalDevice = backend.getPhysicalDevice();
|
||||
|
||||
bool useBlit =
|
||||
surface &&
|
||||
(physicalDevice.getFormatProperties(colorFormat, dispatcher).optimalTilingFeatures &
|
||||
vk::FormatFeatureFlagBits::eBlitSrc &&
|
||||
physicalDevice.getFormatProperties(readTexture->getVulkanFormat(), dispatcher).linearTilingFeatures &
|
||||
vk::FormatFeatureFlagBits::eBlitDst);
|
||||
|
||||
const auto image = readTexture->readImage();
|
||||
|
||||
// swizzle pixels to RGBA
|
||||
if (!useBlit && colorFormat == vk::Format::eB8G8R8A8Unorm) {
|
||||
for (size_t i = 0, size = image->size.area(); i < size; ++i) {
|
||||
std::swap(image->data[i * image->channels + 0], image->data[i * image->channels + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace mln
|
||||
Reference in New Issue
Block a user