Files
Destrum/destrum/src/Graphics/Resources/Cubemap.cpp
T

337 lines
11 KiB
C++

#include <destrum/Graphics/Resources/Cubemap.h>
#include <destrum/Graphics/GfxDevice.h>
#include <destrum/Graphics/RenderResources.h>
#include <destrum/Graphics/Pipeline.h>
#include <destrum/Graphics/Util.h>
#include <glm/ext/matrix_clip_space.hpp>
#include <cmath>
#include <stdexcept>
#include <utility>
#include "volk.h"
#include "spdlog/spdlog.h"
CubeMap::CubeMap()
{
m_projection = glm::perspective(
glm::radians(90.0f),
1.0f,
0.1f,
10.0f
);
}
CubeMap::~CubeMap() = default;
void CubeMap::cleanup(GfxDevice& gfxDevice)
{
VkDevice device = gfxDevice.getDevice();
m_cubemapPipeline.reset();
if (m_cubemapPipelineLayout != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(device, m_cubemapPipelineLayout, nullptr);
m_cubemapPipelineLayout = VK_NULL_HANDLE;
}
m_cubemapImageID = NULL_IMAGE_ID;
m_hdrImage = NULL_IMAGE_ID;
}
void CubeMap::LoadCubeMap(
GfxDevice& gfxDevice,
RenderResources& resources,
const std::filesystem::path& directoryPath)
{
m_hdrImage = resources.loadImageFromFile(
gfxDevice,
directoryPath,
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
false
);
}
void CubeMap::RenderToCubemap(
GfxDevice& gfxDevice,
RenderResources& resources,
ImageID inputImage,
VkImage outputImage,
std::array<VkImageView, 6> faceViews,
std::uint32_t size)
{
if (!m_cubemapPipeline || m_cubemapPipelineLayout == VK_NULL_HANDLE) {
throw std::runtime_error(
"Cubemap pipeline not initialized. Call InitCubemapPipeline first."
);
}
gfxDevice.getImmediateExecuter().immediateSubmit([&](VkCommandBuffer cmd) {
vkutil::ScopedCmdLabel label(cmd, "HDRI To Cubemap", vkutil::labelColors::sky);
VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = outputImage;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 6;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
vkCmdPipelineBarrier(
cmd,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(size);
viewport.height = static_cast<float>(size);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = {size, size};
for (std::uint32_t face = 0; face < 6; ++face) {
vkutil::cmdBeginLabelFormatted(cmd, vkutil::labelColors::draw, "Cubemap Face %u", face);
PC pc{};
pc.viewMtx = viewMatrices[face];
pc.projMtx = m_projection;
pc.inputImageId = static_cast<std::uint32_t>(inputImage);
VkRenderingAttachmentInfoKHR colorAttachment{
VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR
};
colorAttachment.imageView = faceViews[face];
colorAttachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.clearValue.color = {0.f, 0.f, 0.f, 1.f};
VkRenderingInfoKHR renderingInfo{
VK_STRUCTURE_TYPE_RENDERING_INFO_KHR
};
renderingInfo.renderArea.offset = {0, 0};
renderingInfo.renderArea.extent = {size, size};
renderingInfo.layerCount = 1;
renderingInfo.colorAttachmentCount = 1;
renderingInfo.pColorAttachments = &colorAttachment;
vkCmdBeginRendering(cmd, &renderingInfo);
vkCmdSetViewport(cmd, 0, 1, &viewport);
vkCmdSetScissor(cmd, 0, 1, &scissor);
m_cubemapPipeline->bind(cmd);
vkCmdSetPolygonModeEXT(cmd, VK_POLYGON_MODE_FILL);
resources.bindBindlessDescSet(cmd, m_cubemapPipelineLayout);
vkCmdPushConstants(
cmd,
m_cubemapPipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
0,
sizeof(PC),
&pc
);
vkCmdDraw(cmd, 36, 1, 0, 0);
vkCmdEndRendering(cmd);
vkutil::cmdEndLabel(cmd);
}
barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(
cmd,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
});
}
void CubeMap::CreateCubeMap(
GfxDevice& gfxDevice,
RenderResources& resources)
{
if (m_hdrImage == NULL_IMAGE_ID) {
throw std::runtime_error(
"Cannot create cubemap before loading HDR image. Call LoadCubeMap first."
);
}
GPUImage cubeMapImage = gfxDevice.createImageRaw({
.format = VK_FORMAT_R32G32B32A32_SFLOAT,
.usage =
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT,
.extent = VkExtent3D{
.width = m_cubeMapSize,
.height = m_cubeMapSize,
.depth = 1,
},
.numLayers = 6,
.mipMap = false,
.isCubemap = true
});
cubeMapImage.debugName = "skybox cubemap";
vkutil::addDebugLabel(gfxDevice.getDevice(), cubeMapImage.image, cubeMapImage.debugName.c_str());
std::array<VkImageView, 6> faceViews{};
try {
for (std::uint32_t face = 0; face < 6; ++face) {
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = cubeMapImage.image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = VK_FORMAT_R32G32B32A32_SFLOAT;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = face;
viewInfo.subresourceRange.layerCount = 1;
VK_CHECK(vkCreateImageView(
gfxDevice.getDevice(),
&viewInfo,
nullptr,
&faceViews[face]));
const std::string faceViewName = "skybox cubemap face " + std::to_string(face) + " (view)";
vkutil::addDebugLabel(gfxDevice.getDevice(), faceViews[face], faceViewName.c_str());
}
spdlog::info("HDRI image id = {}", m_hdrImage);
RenderToCubemap(
gfxDevice,
resources,
m_hdrImage,
cubeMapImage.image,
faceViews,
m_cubeMapSize
);
for (std::uint32_t face = 0; face < 6; ++face) {
cubeMapImage.setLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, face);
}
m_cubemapImageID = resources.addImageToCache(std::move(cubeMapImage));
for (VkImageView& view : faceViews) {
if (view != VK_NULL_HANDLE) {
vkDestroyImageView(gfxDevice.getDevice(), view, nullptr);
view = VK_NULL_HANDLE;
}
}
} catch (...) {
for (VkImageView& view : faceViews) {
if (view != VK_NULL_HANDLE) {
vkDestroyImageView(gfxDevice.getDevice(), view, nullptr);
}
}
gfxDevice.destroyImage(cubeMapImage);
throw;
}
}
ImageID CubeMap::GetCubeMapImageID() const
{
return m_cubemapImageID;
}
void CubeMap::InitCubemapPipeline(
GfxDevice& gfxDevice,
RenderResources& resources,
const std::string& vertPath,
const std::string& fragPath)
{
if (m_cubemapPipeline) {
return;
}
VkDevice device = gfxDevice.getDevice();
m_cubemapVert = vertPath;
m_cubemapFrag = fragPath;
VkPushConstantRange pushConstantRange{};
pushConstantRange.stageFlags =
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
pushConstantRange.offset = 0;
pushConstantRange.size = sizeof(PC);
const auto layouts = std::array{
resources.getBindlessDescSetLayout()
};
VkPipelineLayoutCreateInfo pipelineLayoutInfo{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO
};
pipelineLayoutInfo.setLayoutCount = static_cast<std::uint32_t>(layouts.size());
pipelineLayoutInfo.pSetLayouts = layouts.data();
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
if (vkCreatePipelineLayout(
device,
&pipelineLayoutInfo,
nullptr,
&m_cubemapPipelineLayout) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create cubemap pipeline layout.");
}
PipelineConfigInfo pipelineConfig{};
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
pipelineConfig.name = "HDRI To Cubemap Pipeline";
pipelineConfig.vertexAttributeDescriptions = {};
pipelineConfig.vertexBindingDescriptions = {};
pipelineConfig.pipelineLayout = m_cubemapPipelineLayout;
pipelineConfig.colorAttachments = {VK_FORMAT_R32G32B32A32_SFLOAT};
pipelineConfig.depthAttachment = VK_FORMAT_UNDEFINED;
pipelineConfig.depthStencilInfo.depthTestEnable = VK_FALSE;
pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE;
pipelineConfig.rasterizationInfo.cullMode = VK_CULL_MODE_NONE;
m_cubemapPipeline = std::make_unique<Pipeline>(
gfxDevice,
vertPath,
fragPath,
pipelineConfig
);
}