-
Tobias Frisch authored
Signed-off-by:
Tobias Frisch <tfrisch@uni-koblenz.de>
Tobias Frisch authoredSigned-off-by:
Tobias Frisch <tfrisch@uni-koblenz.de>
Core.cpp 43.10 KiB
/**
* @authors Artur Wasmut
* @file src/vkcv/Core.cpp
* @brief Handling of global states regarding dependencies
*/
#include <GLFW/glfw3.h>
#include "vkcv/Core.hpp"
#include "PassManager.hpp"
#include "GraphicsPipelineManager.hpp"
#include "ComputePipelineManager.hpp"
#include "vkcv/BufferManager.hpp"
#include "SamplerManager.hpp"
#include "ImageManager.hpp"
#include "DescriptorManager.hpp"
#include "WindowManager.hpp"
#include "CommandStreamManager.hpp"
#include <cmath>
#include "vkcv/Logger.hpp"
#include "vkcv/BlitDownsampler.hpp"
namespace vkcv
{
Core Core::create(const char *applicationName,
uint32_t applicationVersion,
const std::vector<vk::QueueFlagBits>& queueFlags,
const Features& features,
const std::vector<const char *>& instanceExtensions)
{
Context context = Context::create(
applicationName, applicationVersion,
queueFlags,
features,
instanceExtensions
);
const auto& queueManager = context.getQueueManager();
const std::unordered_set<int> queueFamilySet = generateQueueFamilyIndexSet(queueManager);
const auto commandResources = createCommandResources(context.getDevice(), queueFamilySet);
const auto defaultSyncResources = createSyncResources(context.getDevice());
return Core(std::move(context) , commandResources, defaultSyncResources);
}
const Context &Core::getContext() const
{
return m_Context;
}
Core::Core(Context &&context, const CommandResources& commandResources, const SyncResources& syncResources) noexcept :
m_Context(std::move(context)),
m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)},
m_PipelineManager{std::make_unique<GraphicsPipelineManager>(m_Context.m_Device, m_Context.m_PhysicalDevice)},
m_ComputePipelineManager{std::make_unique<ComputePipelineManager>(m_Context.m_Device)},
m_DescriptorManager(std::make_unique<DescriptorManager>(m_Context.m_Device)),
m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager())},
m_SamplerManager(std::unique_ptr<SamplerManager>(new SamplerManager(m_Context.m_Device))),
m_ImageManager{std::unique_ptr<ImageManager>(new ImageManager(*m_BufferManager))},
m_CommandStreamManager{std::unique_ptr<CommandStreamManager>(new CommandStreamManager)},
m_WindowManager(std::make_unique<WindowManager>()),
m_SwapchainManager(std::make_unique<SwapchainManager>()),
m_CommandResources(commandResources),
m_SyncResources(syncResources),
m_downsampler(nullptr)
{
m_BufferManager->m_core = this;
m_BufferManager->init();
m_CommandStreamManager->init(this);
m_SwapchainManager->m_context = &m_Context;
m_ImageManager->m_core = this;
m_downsampler = std::unique_ptr<Downsampler>(new BlitDownsampler(*this, *m_ImageManager));
}
Core::~Core() noexcept {
m_Context.getDevice().waitIdle();
destroyCommandResources(m_Context.getDevice(), m_CommandResources);
destroySyncResources(m_Context.getDevice(), m_SyncResources);
}
GraphicsPipelineHandle Core::createGraphicsPipeline(const GraphicsPipelineConfig &config)
{
return m_PipelineManager->createPipeline(config, *m_PassManager, *m_DescriptorManager);
}
ComputePipelineHandle Core::createComputePipeline(const ComputePipelineConfig &config)
{
std::vector<vk::DescriptorSetLayout> layouts;
layouts.resize(config.m_DescriptorSetLayouts.size());
for (size_t i = 0; i < layouts.size(); i++) {
layouts[i] = getDescriptorSetLayout(config.m_DescriptorSetLayouts[i]).vulkanHandle;
}
return m_ComputePipelineManager->createComputePipeline(config.m_ShaderProgram, layouts);
}
PassHandle Core::createPass(const PassConfig &config)
{
return m_PassManager->createPass(config);
}
Result Core::acquireSwapchainImage(const SwapchainHandle &swapchainHandle) {
uint32_t imageIndex;
vk::Result result;
try {
result = m_Context.getDevice().acquireNextImageKHR(
m_SwapchainManager->getSwapchain(swapchainHandle).getSwapchain(),
std::numeric_limits<uint64_t>::max(),
m_SyncResources.swapchainImageAcquired,
nullptr,
&imageIndex, {}
);
} catch (const vk::OutOfDateKHRError& e) {
result = vk::Result::eErrorOutOfDateKHR;
} catch (const vk::DeviceLostError& e) {
result = vk::Result::eErrorDeviceLost;
}
if ((result != vk::Result::eSuccess) &&
(result != vk::Result::eSuboptimalKHR)) {
vkcv_log(LogLevel::ERROR, "%s", vk::to_string(result).c_str());
return Result::ERROR;
} else
if (result == vk::Result::eSuboptimalKHR) {
vkcv_log(LogLevel::WARNING, "Acquired image is suboptimal");
m_SwapchainManager->getSwapchain(swapchainHandle).signalSwapchainRecreation();
}
m_currentSwapchainImageIndex = imageIndex;
return Result::SUCCESS;
}
bool Core::beginFrame(uint32_t& width, uint32_t& height, const WindowHandle &windowHandle) {
const SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
if (m_SwapchainManager->getSwapchain(swapchainHandle).shouldUpdateSwapchain()) {
m_Context.getDevice().waitIdle();
m_SwapchainManager->getSwapchain(swapchainHandle).updateSwapchain(m_Context, m_WindowManager->getWindow(windowHandle));
if (!m_SwapchainManager->getSwapchain(swapchainHandle).getSwapchain()) {
return false;
}
setSwapchainImages(swapchainHandle);
}
const auto& extent = m_SwapchainManager->getSwapchain(swapchainHandle).getExtent();
width = extent.width;
height = extent.height;
if ((width < MIN_SURFACE_SIZE) || (height < MIN_SURFACE_SIZE)) {
return false;
}
if (acquireSwapchainImage( swapchainHandle ) != Result::SUCCESS) {
vkcv_log(LogLevel::ERROR, "Acquire failed");
m_currentSwapchainImageIndex = std::numeric_limits<uint32_t>::max();
}
m_Context.getDevice().waitIdle(); // TODO: this is a sin against graphics programming, but its getting late - Alex
m_ImageManager->setCurrentSwapchainImageIndex(m_currentSwapchainImageIndex);
return (m_currentSwapchainImageIndex != std::numeric_limits<uint32_t>::max());
}
std::array<uint32_t, 2> getWidthHeightFromRenderTargets(
const std::vector<ImageHandle>& renderTargets,
const Swapchain& swapchain,
const ImageManager& imageManager) {
std::array<uint32_t, 2> widthHeight;
if (renderTargets.size() > 0) {
const vkcv::ImageHandle firstImage = renderTargets[0];
if (firstImage.isSwapchainImage()) {
const auto& swapchainExtent = swapchain.getExtent();
widthHeight[0] = swapchainExtent.width;
widthHeight[1] = swapchainExtent.height;
}
else {
widthHeight[0] = imageManager.getImageWidth(firstImage);
widthHeight[1] = imageManager.getImageHeight(firstImage);
}
}
else {
widthHeight[0] = 1;
widthHeight[1] = 1;
}
// TODO: validate that width/height match for all attachments
return widthHeight;
}
vk::Framebuffer createFramebuffer(
const std::vector<ImageHandle>& renderTargets,
const ImageManager& imageManager,
const Swapchain& swapchain,
vk::RenderPass renderpass,
vk::Device device) {
std::vector<vk::ImageView> attachmentsViews;
for (const ImageHandle& handle : renderTargets) {
attachmentsViews.push_back(imageManager.getVulkanImageView(handle));
}
const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, swapchain, imageManager);
const vk::FramebufferCreateInfo createInfo(
{},
renderpass,
static_cast<uint32_t>(attachmentsViews.size()),
attachmentsViews.data(),
widthHeight[0],
widthHeight[1],
1);
return device.createFramebuffer(createInfo);
}
void transitionRendertargetsToAttachmentLayout(
const std::vector<ImageHandle>& renderTargets,
ImageManager& imageManager,
const vk::CommandBuffer cmdBuffer) {
for (const ImageHandle& handle : renderTargets) {
const bool isDepthImage = isDepthFormat(imageManager.getImageFormat(handle));
const vk::ImageLayout targetLayout =
isDepthImage ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal;
imageManager.recordImageLayoutTransition(handle, 0, 0, targetLayout, cmdBuffer);
}
}
std::vector<vk::ClearValue> createAttachmentClearValues(const std::vector<AttachmentDescription>& attachments) {
std::vector<vk::ClearValue> clearValues;
for (const auto& attachment : attachments) {
if (attachment.load_operation == AttachmentOperation::CLEAR) {
float clear = 0.0f;
if (isDepthFormat(attachment.format)) {
clear = 1.0f;
}
clearValues.emplace_back(std::array<float, 4>{
clear,
clear,
clear,
1.f
});
}
}
return clearValues;
}
void recordDynamicViewport(vk::CommandBuffer cmdBuffer, uint32_t width, uint32_t height) {
vk::Viewport dynamicViewport(
0.0f, 0.0f,
static_cast<float>(width), static_cast<float>(height),
0.0f, 1.0f
);
vk::Rect2D dynamicScissor({ 0, 0 }, { width, height });
cmdBuffer.setViewport(0, 1, &dynamicViewport);
cmdBuffer.setScissor(0, 1, &dynamicScissor);
}
vk::IndexType getIndexType(IndexBitCount indexByteCount){
switch (indexByteCount) {
case IndexBitCount::Bit8:
return vk::IndexType::eUint8EXT;
case IndexBitCount::Bit16:
return vk::IndexType::eUint16;
case IndexBitCount::Bit32:
return vk::IndexType::eUint32;
default:
vkcv_log(LogLevel::ERROR, "unknown Enum");
return vk::IndexType::eNoneKHR;
}
}
void recordDrawcall(
const Core &core,
const DrawcallInfo &drawcall,
vk::CommandBuffer cmdBuffer,
vk::PipelineLayout pipelineLayout,
const PushConstants &pushConstants,
const size_t drawcallIndex) {
for (uint32_t i = 0; i < drawcall.mesh.vertexBufferBindings.size(); i++) {
const auto& vertexBinding = drawcall.mesh.vertexBufferBindings[i];
cmdBuffer.bindVertexBuffers(i, vertexBinding.buffer, vertexBinding.offset);
}
for (const auto& descriptorUsage : drawcall.descriptorSets) {
cmdBuffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics,
pipelineLayout,
descriptorUsage.setLocation,
core.getDescriptorSet(descriptorUsage.descriptorSet).vulkanHandle,
nullptr);
}
if (pushConstants.getSizePerDrawcall() > 0) {
cmdBuffer.pushConstants(
pipelineLayout,
vk::ShaderStageFlagBits::eAll,
0,
pushConstants.getSizePerDrawcall(),
pushConstants.getDrawcallData(drawcallIndex));
}
if (drawcall.mesh.indexBuffer) {
cmdBuffer.bindIndexBuffer(drawcall.mesh.indexBuffer, 0, getIndexType(drawcall.mesh.indexBitCount));
cmdBuffer.drawIndexed(drawcall.mesh.indexCount, drawcall.instanceCount, 0, 0, {});
}
else {
cmdBuffer.draw(drawcall.mesh.indexCount, drawcall.instanceCount, 0, 0, {});
}
}
void Core::recordDrawcallsToCmdStream(
const CommandStreamHandle& cmdStreamHandle,
const PassHandle& renderpassHandle,
const GraphicsPipelineHandle &pipelineHandle,
const PushConstants &pushConstantData,
const std::vector<DrawcallInfo> &drawcalls,
const std::vector<ImageHandle> &renderTargets,
const WindowHandle &windowHandle) {
SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
return;
}
const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_SwapchainManager->getSwapchain(swapchainHandle), *m_ImageManager);
const auto width = widthHeight[0];
const auto height = widthHeight[1];
const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle);
const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle);
const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle);
const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle);
const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle);
transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer);
const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_SwapchainManager->getSwapchain(swapchainHandle), renderpass, m_Context.m_Device);
if (!framebuffer) {
vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
return;
}
SubmitInfo submitInfo;
submitInfo.queueType = QueueType::Graphics;
submitInfo.signalSemaphores = { m_SyncResources.renderFinished };
auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments);
const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data());
cmdBuffer.beginRenderPass(beginInfo, {}, {});
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
const GraphicsPipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle);
if (pipeConfig.m_UseDynamicViewport) {
recordDynamicViewport(cmdBuffer, width, height);
}
for (size_t i = 0; i < drawcalls.size(); i++) {
recordDrawcall(*this, drawcalls[i], cmdBuffer, pipelineLayout, pushConstantData, i);
}
cmdBuffer.endRenderPass();
};
auto finishFunction = [framebuffer, this]()
{
m_Context.m_Device.destroy(framebuffer);
};
recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
}
void Core::recordIndexedIndirectDrawcallsToCmdStream(
const CommandStreamHandle cmdStreamHandle,
const PassHandle renderpassHandle,
const GraphicsPipelineHandle &pipelineHandle,
const PushConstants &pushConstantData,
const vkcv::DescriptorSetHandle &compiledDescriptorSet,
const vkcv::Mesh &compiledMesh,
const std::vector<ImageHandle> &renderTargets,
const vkcv::Buffer<vk::DrawIndexedIndirectCommand> &indirectBuffer,
const uint32_t drawCount,
const WindowHandle &windowHandle) {
if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
return;
}
SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_SwapchainManager->getSwapchain(swapchainHandle),
*m_ImageManager);
const auto width = widthHeight[0];
const auto height = widthHeight[1];
const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle);
const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle);
const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle);
const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle);
const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle);
transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer);
const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_SwapchainManager->getSwapchain(swapchainHandle), renderpass,
m_Context.m_Device);
if (!framebuffer) {
vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
return;
}
SubmitInfo submitInfo;
submitInfo.queueType = QueueType::Graphics;
submitInfo.signalSemaphores = {m_SyncResources.renderFinished};
auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) {
const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments);
const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(),
clearValues.data());
cmdBuffer.beginRenderPass(beginInfo, {}, {});
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
const GraphicsPipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle);
if (pipeConfig.m_UseDynamicViewport) {
recordDynamicViewport(cmdBuffer, width, height);
}
if (pushConstantData.getSizePerDrawcall() > 0)
{
cmdBuffer.pushConstants(
pipelineLayout,
vk::ShaderStageFlagBits::eAll,
0,
pushConstantData.getSizePerDrawcall(),
pushConstantData.getDrawcallData(0));
}
vkcv::DescriptorSet descSet = m_DescriptorManager->getDescriptorSet(compiledDescriptorSet);
cmdBuffer.bindDescriptorSets(
vk::PipelineBindPoint::eGraphics,
pipelineLayout,
0,
descSet.vulkanHandle,
nullptr);
vk::DeviceSize deviceSize = 0;
cmdBuffer.bindVertexBuffers(0, 1, &compiledMesh.vertexBufferBindings[0].buffer,&deviceSize);
cmdBuffer.bindIndexBuffer(compiledMesh.indexBuffer, 0, getIndexType(compiledMesh.indexBitCount));
cmdBuffer.drawIndexedIndirect(
indirectBuffer.getVulkanHandle(),
0,
drawCount,
sizeof(vk::DrawIndexedIndirectCommand));
cmdBuffer.endRenderPass();
};
auto finishFunction = [framebuffer, this]() {
m_Context.m_Device.destroy(framebuffer);
};
recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
}
void Core::recordMeshShaderDrawcalls(
const CommandStreamHandle& cmdStreamHandle,
const PassHandle& renderpassHandle,
const GraphicsPipelineHandle &pipelineHandle,
const PushConstants& pushConstantData,
const std::vector<MeshShaderDrawcall>& drawcalls,
const std::vector<ImageHandle>& renderTargets,
const WindowHandle& windowHandle) {
SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
return;
}
const std::array<uint32_t, 2> widthHeight = getWidthHeightFromRenderTargets(renderTargets, m_SwapchainManager->getSwapchain(swapchainHandle), *m_ImageManager);
const auto width = widthHeight[0];
const auto height = widthHeight[1];
const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle);
const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle);
const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle);
const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle);
const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height));
vk::CommandBuffer cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle);
transitionRendertargetsToAttachmentLayout(renderTargets, *m_ImageManager, cmdBuffer);
const vk::Framebuffer framebuffer = createFramebuffer(renderTargets, *m_ImageManager, m_SwapchainManager->getSwapchain(swapchainHandle), renderpass, m_Context.m_Device);
if (!framebuffer) {
vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer");
return;
}
SubmitInfo submitInfo;
submitInfo.queueType = QueueType::Graphics;
submitInfo.signalSemaphores = { m_SyncResources.renderFinished };
auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
const std::vector<vk::ClearValue> clearValues = createAttachmentClearValues(passConfig.attachments);
const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data());
cmdBuffer.beginRenderPass(beginInfo, {}, {});
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {});
const GraphicsPipelineConfig& pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle);
if (pipeConfig.m_UseDynamicViewport) {
recordDynamicViewport(cmdBuffer, width, height);
}
for (size_t i = 0; i < drawcalls.size(); i++) {
const uint32_t pushConstantOffset = i * pushConstantData.getSizePerDrawcall();
recordMeshShaderDrawcall(
*this,
cmdBuffer,
pipelineLayout,
pushConstantData,
pushConstantOffset,
drawcalls[i],
0
);
}
cmdBuffer.endRenderPass();
};
auto finishFunction = [framebuffer, this]() {
m_Context.m_Device.destroy(framebuffer);
};
recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction);
}
void Core::recordRayGenerationToCmdStream(
CommandStreamHandle cmdStreamHandle,
vk::Pipeline rtxPipeline,
vk::PipelineLayout rtxPipelineLayout,
vk::StridedDeviceAddressRegionKHR rgenRegion,
vk::StridedDeviceAddressRegionKHR rmissRegion,
vk::StridedDeviceAddressRegionKHR rchitRegion,
vk::StridedDeviceAddressRegionKHR rcallRegion,
const std::vector<DescriptorSetUsage>& descriptorSetUsages,
const PushConstants& pushConstants,
const WindowHandle windowHandle) {
auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, rtxPipeline);
for (const auto& usage : descriptorSetUsages) {
cmdBuffer.bindDescriptorSets(
vk::PipelineBindPoint::eRayTracingKHR,
rtxPipelineLayout,
usage.setLocation,
{ getDescriptorSet(usage.descriptorSet).vulkanHandle },
usage.dynamicOffsets
);
}
if (pushConstants.getSizePerDrawcall() > 0) {
cmdBuffer.pushConstants(
rtxPipelineLayout,
(vk::ShaderStageFlagBits::eClosestHitKHR | vk::ShaderStageFlagBits::eMissKHR | vk::ShaderStageFlagBits::eRaygenKHR), // TODO: add Support for eAnyHitKHR, eCallableKHR, eIntersectionKHR
0,
pushConstants.getSizePerDrawcall(),
pushConstants.getData());
}
auto m_rtxDispatcher = vk::DispatchLoaderDynamic((PFN_vkGetInstanceProcAddr)m_Context.getInstance().getProcAddr("vkGetInstanceProcAddr"));
m_rtxDispatcher.init(m_Context.getInstance());
cmdBuffer.traceRaysKHR(&rgenRegion,&rmissRegion,&rchitRegion,&rcallRegion,
getWindow(windowHandle).getWidth(), getWindow(windowHandle).getHeight(),1, m_rtxDispatcher);
};
recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr);
}
void Core::recordComputeDispatchToCmdStream(
CommandStreamHandle cmdStreamHandle,
ComputePipelineHandle computePipeline,
const uint32_t dispatchCount[3],
const std::vector<DescriptorSetUsage>& descriptorSetUsages,
const PushConstants& pushConstants) {
auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
const auto pipelineLayout = m_ComputePipelineManager->getVkPipelineLayout(computePipeline);
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_ComputePipelineManager->getVkPipeline(computePipeline));
for (const auto& usage : descriptorSetUsages) {
cmdBuffer.bindDescriptorSets(
vk::PipelineBindPoint::eCompute,
pipelineLayout,
usage.setLocation,
{ getDescriptorSet(usage.descriptorSet).vulkanHandle },
usage.dynamicOffsets
);
}
if (pushConstants.getSizePerDrawcall() > 0) {
cmdBuffer.pushConstants(
pipelineLayout,
vk::ShaderStageFlagBits::eCompute,
0,
pushConstants.getSizePerDrawcall(),
pushConstants.getData());
}
cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]);
};
recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr);
}
void Core::recordBeginDebugLabel(const CommandStreamHandle &cmdStream,
const std::string& label,
const std::array<float, 4>& color) {
#ifdef VULKAN_DEBUG_LABELS
static PFN_vkCmdBeginDebugUtilsLabelEXT beginDebugLabel = reinterpret_cast<PFN_vkCmdBeginDebugUtilsLabelEXT>(
m_Context.getDevice().getProcAddr("vkCmdBeginDebugUtilsLabelEXT")
);
if (!beginDebugLabel) {
return;
}
auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
const vk::DebugUtilsLabelEXT debug (
label.c_str(),
color
);
beginDebugLabel(static_cast<VkCommandBuffer>(cmdBuffer), &(static_cast<const VkDebugUtilsLabelEXT&>(debug)));
};
recordCommandsToStream(cmdStream, submitFunction, nullptr);
#endif
}
void Core::recordEndDebugLabel(const CommandStreamHandle &cmdStream) {
#ifdef VULKAN_DEBUG_LABELS
static PFN_vkCmdEndDebugUtilsLabelEXT endDebugLabel = reinterpret_cast<PFN_vkCmdEndDebugUtilsLabelEXT>(
m_Context.getDevice().getProcAddr("vkCmdEndDebugUtilsLabelEXT")
);
if (!endDebugLabel) {
return;
}
auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
endDebugLabel(static_cast<VkCommandBuffer>(cmdBuffer));
};
recordCommandsToStream(cmdStream, submitFunction, nullptr);
#endif
}
void Core::recordComputeIndirectDispatchToCmdStream(
const CommandStreamHandle cmdStream,
const ComputePipelineHandle computePipeline,
const vkcv::BufferHandle buffer,
const size_t bufferArgOffset,
const std::vector<DescriptorSetUsage>& descriptorSetUsages,
const PushConstants& pushConstants) {
auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) {
const auto pipelineLayout = m_ComputePipelineManager->getVkPipelineLayout(computePipeline);
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_ComputePipelineManager->getVkPipeline(computePipeline));
for (const auto& usage : descriptorSetUsages) {
cmdBuffer.bindDescriptorSets(
vk::PipelineBindPoint::eCompute,
pipelineLayout,
usage.setLocation,
{ getDescriptorSet(usage.descriptorSet).vulkanHandle },
usage.dynamicOffsets
);
}
if (pushConstants.getSizePerDrawcall() > 0) {
cmdBuffer.pushConstants(
pipelineLayout,
vk::ShaderStageFlagBits::eCompute,
0,
pushConstants.getSizePerDrawcall(),
pushConstants.getData());
}
cmdBuffer.dispatchIndirect(m_BufferManager->getBuffer(buffer), bufferArgOffset);
};
recordCommandsToStream(cmdStream, submitFunction, nullptr);
}
void Core::endFrame(const WindowHandle& windowHandle) {
SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) {
return;
}
std::array<vk::Semaphore, 2> waitSemaphores{
m_SyncResources.renderFinished,
m_SyncResources.swapchainImageAcquired
};
const vk::SwapchainKHR& swapchain = m_SwapchainManager->getSwapchain(swapchainHandle).getSwapchain();
const vk::PresentInfoKHR presentInfo(
waitSemaphores,
swapchain,
m_currentSwapchainImageIndex
);
vk::Result result;
try {
result = m_Context.getDevice().getQueue(m_SwapchainManager->getSwapchain(swapchainHandle).getPresentQueueIndex(),0).presentKHR(presentInfo);
} catch (const vk::OutOfDateKHRError& e) {
result = vk::Result::eErrorOutOfDateKHR;
} catch (const vk::DeviceLostError& e) {
result = vk::Result::eErrorDeviceLost;
}
if ((result != vk::Result::eSuccess) &&
(result != vk::Result::eSuboptimalKHR)) {
vkcv_log(LogLevel::ERROR, "Swapchain presentation failed (%s)", vk::to_string(result).c_str());
} else
if (result == vk::Result::eSuboptimalKHR) {
vkcv_log(LogLevel::WARNING, "Swapchain presentation is suboptimal");
m_SwapchainManager->signalRecreation(swapchainHandle);
}
}
void Core::recordAndSubmitCommandsImmediate(
const SubmitInfo &submitInfo,
const RecordCommandFunction &record,
const FinishCommandFunction &finish)
{
const vk::Device& device = m_Context.getDevice();
const vkcv::Queue queue = getQueueForSubmit(submitInfo.queueType, m_Context.getQueueManager());
const vk::CommandPool cmdPool = chooseCmdPool(queue, m_CommandResources);
const vk::CommandBuffer cmdBuffer = allocateCommandBuffer(device, cmdPool);
beginCommandBuffer(cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
record(cmdBuffer);
cmdBuffer.end();
vk::Fence waitFence = createFence(device);
submitCommandBufferToQueue(queue.handle, cmdBuffer, waitFence, submitInfo.waitSemaphores, submitInfo.signalSemaphores);
waitForFence(device, waitFence);
device.destroyFence(waitFence);
device.freeCommandBuffers(cmdPool, cmdBuffer);
if (finish) {
finish();
}
}
CommandStreamHandle Core::createCommandStream(QueueType queueType) {
const vkcv::Queue queue = getQueueForSubmit(queueType, m_Context.getQueueManager());
const vk::CommandPool cmdPool = chooseCmdPool(queue, m_CommandResources);
return m_CommandStreamManager->createCommandStream(queue.handle, cmdPool);
}
void Core::recordCommandsToStream(
const CommandStreamHandle cmdStreamHandle,
const RecordCommandFunction &record,
const FinishCommandFunction &finish) {
if (record) {
m_CommandStreamManager->recordCommandsToStream(cmdStreamHandle, record);
}
if (finish) {
m_CommandStreamManager->addFinishCallbackToStream(cmdStreamHandle, finish);
}
}
void Core::submitCommandStream(const CommandStreamHandle& handle,
bool signalRendering) {
std::vector<vk::Semaphore> waitSemaphores;
// FIXME: add proper user controllable sync
std::vector<vk::Semaphore> signalSemaphores;
if (signalRendering) {
signalSemaphores.push_back(m_SyncResources.renderFinished);
}
m_CommandStreamManager->submitCommandStreamSynchronous(handle, waitSemaphores, signalSemaphores);
}
SamplerHandle Core::createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter,
SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode,
float mipLodBias, SamplerBorderColor borderColor) {
return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode, mipLodBias, borderColor);
}
Image Core::createImage(
vk::Format format,
uint32_t width,
uint32_t height,
uint32_t depth,
bool createMipChain,
bool supportStorage,
bool supportColorAttachment,
Multisampling multisampling)
{
uint32_t mipCount = 1;
if (createMipChain) {
mipCount = 1 + (uint32_t)std::floor(std::log2(std::max(width, std::max(height, depth))));
}
return Image::create(
m_ImageManager.get(),
format,
width,
height,
depth,
mipCount,
supportStorage,
supportColorAttachment,
multisampling);
}
Downsampler &Core::getDownsampler() {
return *m_downsampler;
}
WindowHandle Core::createWindow(
const char *applicationName,
uint32_t windowWidth,
uint32_t windowHeight,
bool resizeable) {
WindowHandle windowHandle = m_WindowManager->createWindow(*m_SwapchainManager ,applicationName, windowWidth, windowHeight, resizeable);
SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchainHandle();
setSwapchainImages( swapchainHandle );
return windowHandle;
}
Window& Core::getWindow(const WindowHandle& handle) {
return m_WindowManager->getWindow(handle);
}
uint32_t Core::getImageWidth(const ImageHandle& image)
{
return m_ImageManager->getImageWidth(image);
}
uint32_t Core::getImageHeight(const ImageHandle& image)
{
return m_ImageManager->getImageHeight(image);
}
uint32_t Core::getImageDepth(const ImageHandle& image)
{
return m_ImageManager->getImageDepth(image);
}
vk::Format Core::getImageFormat(const ImageHandle& image) {
return m_ImageManager->getImageFormat(image);
}
bool Core::isImageSupportingStorage(const ImageHandle &image) {
return m_ImageManager->isImageSupportingStorage(image);
}
uint32_t Core::getImageMipLevels(const ImageHandle &image) {
return m_ImageManager->getImageMipCount(image);
}
uint32_t Core::getImageArrayLayers(const ImageHandle &image) {
return m_ImageManager->getImageArrayLayers(image);
}
Swapchain& Core::getSwapchainOfCurrentWindow() {
return m_SwapchainManager->getSwapchain(Window::getFocusedWindow().getSwapchainHandle());
}
Swapchain& Core::getSwapchain(const SwapchainHandle& handle) {
return m_SwapchainManager->getSwapchain(handle);
}
Swapchain& Core::getSwapchain(const WindowHandle& handle) {
SwapchainHandle swapchainHandle = m_WindowManager->getWindow(handle).getSwapchainHandle();
return getSwapchain(swapchainHandle);
}
DescriptorSetLayoutHandle Core::createDescriptorSetLayout(const DescriptorBindings &bindings)
{
return m_DescriptorManager->createDescriptorSetLayout(bindings);
}
DescriptorSetLayout Core::getDescriptorSetLayout(const DescriptorSetLayoutHandle handle) const
{
return m_DescriptorManager->getDescriptorSetLayout(handle);
}
DescriptorSetHandle Core::createDescriptorSet(const DescriptorSetLayoutHandle &layout)
{
return m_DescriptorManager->createDescriptorSet(layout);
}
void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) {
m_DescriptorManager->writeDescriptorSet(
handle,
writes,
*m_ImageManager,
*m_BufferManager,
*m_SamplerManager);
}
DescriptorSet Core::getDescriptorSet(const DescriptorSetHandle handle) const {
return m_DescriptorManager->getDescriptorSet(handle);
}
void Core::prepareSwapchainImageForPresent(const CommandStreamHandle& cmdStream) {
auto swapchainHandle = ImageHandle::createSwapchainImageHandle();
recordCommandsToStream(cmdStream, [swapchainHandle, this](const vk::CommandBuffer cmdBuffer) {
m_ImageManager->recordImageLayoutTransition(
swapchainHandle,
0,
0,
vk::ImageLayout::ePresentSrcKHR,
cmdBuffer
);
}, nullptr);
}
void Core::prepareImageForSampling(const CommandStreamHandle& cmdStream,
const ImageHandle& image,
uint32_t mipLevelCount,
uint32_t mipLevelOffset) {
recordCommandsToStream(cmdStream, [image, mipLevelCount, mipLevelOffset, this](const vk::CommandBuffer cmdBuffer) {
m_ImageManager->recordImageLayoutTransition(
image,
mipLevelCount,
mipLevelOffset,
vk::ImageLayout::eShaderReadOnlyOptimal,
cmdBuffer
);
}, nullptr);
}
void Core::prepareImageForStorage(const CommandStreamHandle& cmdStream,
const ImageHandle& image,
uint32_t mipLevelCount,
uint32_t mipLevelOffset) {
recordCommandsToStream(cmdStream, [image, mipLevelCount, mipLevelOffset, this](const vk::CommandBuffer cmdBuffer) {
m_ImageManager->recordImageLayoutTransition(
image,
mipLevelCount,
mipLevelOffset,
vk::ImageLayout::eGeneral,
cmdBuffer
);
}, nullptr);
}
void Core::prepareImageForAttachmentManually(const vk::CommandBuffer& cmdBuffer, const ImageHandle& image) {
transitionRendertargetsToAttachmentLayout({ image }, *m_ImageManager, cmdBuffer);
}
void Core::updateImageLayoutManual(const vkcv::ImageHandle& image, const vk::ImageLayout layout) {
m_ImageManager->updateImageLayoutManual(image, layout);
}
void Core::recordImageMemoryBarrier(const CommandStreamHandle& cmdStream, const ImageHandle& image) {
recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) {
m_ImageManager->recordImageMemoryBarrier(image, cmdBuffer);
}, nullptr);
}
void Core::recordBufferMemoryBarrier(const CommandStreamHandle& cmdStream, const BufferHandle& buffer) {
recordCommandsToStream(cmdStream, [buffer, this](const vk::CommandBuffer cmdBuffer) {
m_BufferManager->recordBufferMemoryBarrier(buffer, cmdBuffer);
}, nullptr);
}
void Core::resolveMSAAImage(const CommandStreamHandle& cmdStream, const ImageHandle& src, const ImageHandle& dst) {
recordCommandsToStream(cmdStream, [src, dst, this](const vk::CommandBuffer cmdBuffer) {
m_ImageManager->recordMSAAResolve(cmdBuffer, src, dst);
}, nullptr);
}
vk::ImageView Core::getSwapchainImageView() const {
return m_ImageManager->getVulkanImageView(vkcv::ImageHandle::createSwapchainImageHandle());
}
void Core::recordMemoryBarrier(const CommandStreamHandle& cmdStream) {
recordCommandsToStream(cmdStream, [](const vk::CommandBuffer cmdBuffer) {
vk::MemoryBarrier barrier (
vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eMemoryRead,
vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eMemoryRead
);
cmdBuffer.pipelineBarrier(
vk::PipelineStageFlagBits::eAllCommands,
vk::PipelineStageFlagBits::eAllCommands,
vk::DependencyFlags(),
1, &barrier,
0, nullptr,
0, nullptr
);
}, nullptr);
}
void Core::recordBlitImage(const CommandStreamHandle& cmdStream, const ImageHandle& src, const ImageHandle& dst,
SamplerFilterType filterType) {
recordCommandsToStream(cmdStream, [&](const vk::CommandBuffer cmdBuffer) {
m_ImageManager->recordImageLayoutTransition(
src, 0, 0, vk::ImageLayout::eTransferSrcOptimal, cmdBuffer
);
m_ImageManager->recordImageLayoutTransition(
dst, 0, 0, vk::ImageLayout::eTransferDstOptimal, cmdBuffer
);
const std::array<vk::Offset3D, 2> srcOffsets = {
vk::Offset3D(0, 0, 0),
vk::Offset3D(
m_ImageManager->getImageWidth(src),
m_ImageManager->getImageHeight(src),
1
)
};
const std::array<vk::Offset3D, 2> dstOffsets = {
vk::Offset3D(0, 0, 0),
vk::Offset3D(
m_ImageManager->getImageWidth(dst),
m_ImageManager->getImageHeight(dst),
1
)
};
const bool srcDepth = isDepthFormat(m_ImageManager->getImageFormat(src));
const bool dstDepth = isDepthFormat(m_ImageManager->getImageFormat(dst));
const vk::ImageBlit blit = vk::ImageBlit(
vk::ImageSubresourceLayers(
srcDepth?
vk::ImageAspectFlagBits::eDepth :
vk::ImageAspectFlagBits::eColor,
0, 0, 1
),
srcOffsets,
vk::ImageSubresourceLayers(
dstDepth?
vk::ImageAspectFlagBits::eDepth :
vk::ImageAspectFlagBits::eColor,
0, 0, 1
),
dstOffsets
);
cmdBuffer.blitImage(
m_ImageManager->getVulkanImage(src),
vk::ImageLayout::eTransferSrcOptimal,
m_ImageManager->getVulkanImage(dst),
vk::ImageLayout::eTransferDstOptimal,
1,
&blit,
filterType == SamplerFilterType::LINEAR?
vk::Filter::eLinear :
vk::Filter::eNearest
);
}, nullptr);
}
void Core::setSwapchainImages( SwapchainHandle handle ) {
Swapchain swapchain = m_SwapchainManager->getSwapchain(handle);
const auto swapchainImages = m_SwapchainManager->getSwapchainImages(handle);
const auto swapchainImageViews = m_SwapchainManager->createSwapchainImageViews(handle);
m_ImageManager->setSwapchainImages(
swapchainImages,
swapchainImageViews,
swapchain.getExtent().width,
swapchain.getExtent().height,
swapchain.getFormat()
);
}
static void setDebugObjectLabel(const vk::Device& device, const vk::ObjectType& type,
uint64_t handle, const std::string& label) {
#ifdef VULKAN_DEBUG_LABELS
static PFN_vkSetDebugUtilsObjectNameEXT setDebugLabel = reinterpret_cast<PFN_vkSetDebugUtilsObjectNameEXT>(
device.getProcAddr("vkSetDebugUtilsObjectNameEXT")
);
if (!setDebugLabel) {
return;
}
const vk::DebugUtilsObjectNameInfoEXT debug (
type,
handle,
label.c_str()
);
setDebugLabel(static_cast<VkDevice>(device), &(static_cast<const VkDebugUtilsObjectNameInfoEXT&>(debug)));
#endif
}
void Core::setDebugLabel(const BufferHandle &handle, const std::string &label) {
if (!handle) {
vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle");
return;
}
setDebugObjectLabel(
m_Context.getDevice(),
vk::ObjectType::eBuffer,
uint64_t(static_cast<VkBuffer>(
m_BufferManager->getBuffer(handle)
)),
label
);
}
void Core::setDebugLabel(const PassHandle &handle, const std::string &label) {
if (!handle) {
vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle");
return;
}
setDebugObjectLabel(
m_Context.getDevice(),
vk::ObjectType::eRenderPass,
uint64_t(static_cast<VkRenderPass>(
m_PassManager->getVkPass(handle)
)),
label
);
}
void Core::setDebugLabel(const GraphicsPipelineHandle &handle, const std::string &label) {
if (!handle) {
vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle");
return;
}
setDebugObjectLabel(
m_Context.getDevice(),
vk::ObjectType::ePipeline,
uint64_t(static_cast<VkPipeline>(
m_PipelineManager->getVkPipeline(handle)
)),
label
);
}
void Core::setDebugLabel(const ComputePipelineHandle &handle, const std::string &label) {
if (!handle) {
vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle");
return;
}
setDebugObjectLabel(
m_Context.getDevice(),
vk::ObjectType::ePipeline,
uint64_t(static_cast<VkPipeline>(
m_ComputePipelineManager->getVkPipeline(handle)
)),
label
);
}
void Core::setDebugLabel(const DescriptorSetHandle &handle, const std::string &label) {
if (!handle) {
vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle");
return;
}
setDebugObjectLabel(
m_Context.getDevice(),
vk::ObjectType::eDescriptorSet,
uint64_t(static_cast<VkDescriptorSet>(
m_DescriptorManager->getDescriptorSet(handle).vulkanHandle
)),
label
);
}
void Core::setDebugLabel(const SamplerHandle &handle, const std::string &label) {
if (!handle) {
vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle");
return;
}
setDebugObjectLabel(
m_Context.getDevice(),
vk::ObjectType::eSampler,
uint64_t(static_cast<VkSampler>(
m_SamplerManager->getVulkanSampler(handle)
)),
label
);
}
void Core::setDebugLabel(const ImageHandle &handle, const std::string &label) {
if (!handle) {
vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle");
return;
} else
if (handle.isSwapchainImage()) {
vkcv_log(LogLevel::WARNING, "Can't set debug label to swapchain image");
return;
}
setDebugObjectLabel(
m_Context.getDevice(),
vk::ObjectType::eImage,
uint64_t(static_cast<VkImage>(
m_ImageManager->getVulkanImage(handle)
)),
label
);
}
void Core::setDebugLabel(const CommandStreamHandle &handle, const std::string &label) {
if (!handle) {
vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle");
return;
}
setDebugObjectLabel(
m_Context.getDevice(),
vk::ObjectType::eCommandBuffer,
uint64_t(static_cast<VkCommandBuffer>(
m_CommandStreamManager->getStreamCommandBuffer(handle)
)),
label
);
}
}