ADD: aspect ratio preserving completed
This commit is contained in:
36
shaders/present_letterbox.frag
Normal file
36
shaders/present_letterbox.frag
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location=0) in vec2 inUV;
|
||||||
|
layout(location=0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout(set=0, binding=0) uniform sampler2D uSrc;
|
||||||
|
|
||||||
|
layout(push_constant) uniform Push
|
||||||
|
{
|
||||||
|
vec2 rect_min; // normalized (0..1) min corner in swapchain UV space
|
||||||
|
vec2 rect_size; // normalized size in swapchain UV space
|
||||||
|
} pc;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
if (pc.rect_size.x <= 0.0 || pc.rect_size.y <= 0.0)
|
||||||
|
{
|
||||||
|
outColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 rect_max = pc.rect_min + pc.rect_size;
|
||||||
|
vec2 uv = inUV;
|
||||||
|
|
||||||
|
if (uv.x < pc.rect_min.x || uv.y < pc.rect_min.y || uv.x > rect_max.x || uv.y > rect_max.y)
|
||||||
|
{
|
||||||
|
outColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 local = (uv - pc.rect_min) / pc.rect_size;
|
||||||
|
local = clamp(local, vec2(0.0), vec2(1.0));
|
||||||
|
|
||||||
|
outColor = texture(uSrc, local);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
#include <core/device/images.h>
|
#include <core/device/images.h>
|
||||||
#include <core/util/initializers.h>
|
#include <core/util/initializers.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
|
|
||||||
@@ -137,6 +140,89 @@ void vkutil::copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage de
|
|||||||
vkCmdBlitImage2(cmd, &blitInfo);
|
vkCmdBlitImage2(cmd, &blitInfo);
|
||||||
}
|
}
|
||||||
//< copyimg
|
//< copyimg
|
||||||
|
|
||||||
|
VkRect2D vkutil::compute_letterbox_rect(VkExtent2D srcSize, VkExtent2D dstSize)
|
||||||
|
{
|
||||||
|
VkRect2D rect{};
|
||||||
|
rect.offset = {0, 0};
|
||||||
|
rect.extent = dstSize;
|
||||||
|
if (srcSize.width == 0 || srcSize.height == 0 || dstSize.width == 0 || dstSize.height == 0)
|
||||||
|
{
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double srcAspect = double(srcSize.width) / double(srcSize.height);
|
||||||
|
const double dstAspect = double(dstSize.width) / double(dstSize.height);
|
||||||
|
|
||||||
|
if (dstAspect > srcAspect)
|
||||||
|
{
|
||||||
|
// Fit by height, bars on left/right.
|
||||||
|
const double scale = double(dstSize.height) / double(srcSize.height);
|
||||||
|
uint32_t scaledWidth = static_cast<uint32_t>(std::lround(double(srcSize.width) * scale));
|
||||||
|
scaledWidth = std::min(scaledWidth, dstSize.width);
|
||||||
|
const uint32_t offsetX = (dstSize.width - scaledWidth) / 2u;
|
||||||
|
rect.offset = {static_cast<int32_t>(offsetX), 0};
|
||||||
|
rect.extent = {scaledWidth, dstSize.height};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fit by width, bars on top/bottom.
|
||||||
|
const double scale = double(dstSize.width) / double(srcSize.width);
|
||||||
|
uint32_t scaledHeight = static_cast<uint32_t>(std::lround(double(srcSize.height) * scale));
|
||||||
|
scaledHeight = std::min(scaledHeight, dstSize.height);
|
||||||
|
const uint32_t offsetY = (dstSize.height - scaledHeight) / 2u;
|
||||||
|
rect.offset = {0, static_cast<int32_t>(offsetY)};
|
||||||
|
rect.extent = {dstSize.width, scaledHeight};
|
||||||
|
}
|
||||||
|
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vkutil::copy_image_to_image_letterboxed(VkCommandBuffer cmd,
|
||||||
|
VkImage source,
|
||||||
|
VkImage destination,
|
||||||
|
VkExtent2D srcSize,
|
||||||
|
VkExtent2D dstSize,
|
||||||
|
VkFilter filter)
|
||||||
|
{
|
||||||
|
VkRect2D dstRect = compute_letterbox_rect(srcSize, dstSize);
|
||||||
|
|
||||||
|
VkImageBlit2 blitRegion{ .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, .pNext = nullptr };
|
||||||
|
|
||||||
|
blitRegion.srcOffsets[1].x = static_cast<int32_t>(srcSize.width);
|
||||||
|
blitRegion.srcOffsets[1].y = static_cast<int32_t>(srcSize.height);
|
||||||
|
blitRegion.srcOffsets[1].z = 1;
|
||||||
|
|
||||||
|
blitRegion.dstOffsets[0].x = dstRect.offset.x;
|
||||||
|
blitRegion.dstOffsets[0].y = dstRect.offset.y;
|
||||||
|
blitRegion.dstOffsets[0].z = 0;
|
||||||
|
|
||||||
|
blitRegion.dstOffsets[1].x = dstRect.offset.x + static_cast<int32_t>(dstRect.extent.width);
|
||||||
|
blitRegion.dstOffsets[1].y = dstRect.offset.y + static_cast<int32_t>(dstRect.extent.height);
|
||||||
|
blitRegion.dstOffsets[1].z = 1;
|
||||||
|
|
||||||
|
blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
blitRegion.srcSubresource.baseArrayLayer = 0;
|
||||||
|
blitRegion.srcSubresource.layerCount = 1;
|
||||||
|
blitRegion.srcSubresource.mipLevel = 0;
|
||||||
|
|
||||||
|
blitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
blitRegion.dstSubresource.baseArrayLayer = 0;
|
||||||
|
blitRegion.dstSubresource.layerCount = 1;
|
||||||
|
blitRegion.dstSubresource.mipLevel = 0;
|
||||||
|
|
||||||
|
VkBlitImageInfo2 blitInfo{ .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, .pNext = nullptr };
|
||||||
|
blitInfo.dstImage = destination;
|
||||||
|
blitInfo.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
blitInfo.srcImage = source;
|
||||||
|
blitInfo.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||||
|
blitInfo.filter = filter;
|
||||||
|
blitInfo.regionCount = 1;
|
||||||
|
blitInfo.pRegions = &blitRegion;
|
||||||
|
|
||||||
|
vkCmdBlitImage2(cmd, &blitInfo);
|
||||||
|
}
|
||||||
|
|
||||||
//> mipgen
|
//> mipgen
|
||||||
static inline int compute_full_mip_count(VkExtent2D imageSize)
|
static inline int compute_full_mip_count(VkExtent2D imageSize)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,17 @@ namespace vkutil {
|
|||||||
|
|
||||||
void transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout);
|
void transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout);
|
||||||
|
|
||||||
|
// Compute a letterboxed destination rect inside dstSize that preserves srcSize aspect ratio.
|
||||||
|
VkRect2D compute_letterbox_rect(VkExtent2D srcSize, VkExtent2D dstSize);
|
||||||
|
|
||||||
void copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize);
|
void copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize);
|
||||||
|
// Blit source into a letterboxed rect in destination (preserves aspect ratio).
|
||||||
|
void copy_image_to_image_letterboxed(VkCommandBuffer cmd,
|
||||||
|
VkImage source,
|
||||||
|
VkImage destination,
|
||||||
|
VkExtent2D srcSize,
|
||||||
|
VkExtent2D dstSize,
|
||||||
|
VkFilter filter = VK_FILTER_LINEAR);
|
||||||
void generate_mipmaps(VkCommandBuffer cmd, VkImage image, VkExtent2D imageSize);
|
void generate_mipmaps(VkCommandBuffer cmd, VkImage image, VkExtent2D imageSize);
|
||||||
// Variant that generates exactly mipLevels levels (starting at base level 0).
|
// Variant that generates exactly mipLevels levels (starting at base level 0).
|
||||||
void generate_mipmaps_levels(VkCommandBuffer cmd, VkImage image, VkExtent2D imageSize, int mipLevels);
|
void generate_mipmaps_levels(VkCommandBuffer cmd, VkImage image, VkExtent2D imageSize, int mipLevels);
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ void SwapchainManager::init_swapchain()
|
|||||||
// On creation we also push a cleanup lambda to _deletionQueue for final shutdown.
|
// On creation we also push a cleanup lambda to _deletionQueue for final shutdown.
|
||||||
// On resize we will flush that queue first to destroy previous resources.
|
// On resize we will flush that queue first to destroy previous resources.
|
||||||
|
|
||||||
// depth/draw/gbuffer sized to current window extent
|
// depth/draw/gbuffer sized to fixed logical render extent (letterboxed)
|
||||||
auto create_frame_images = [this]() {
|
auto create_frame_images = [this]() {
|
||||||
VkExtent3D drawImageExtent = { _windowExtent.width, _windowExtent.height, 1 };
|
VkExtent3D drawImageExtent = { kRenderWidth, kRenderHeight, 1 };
|
||||||
|
|
||||||
// Draw HDR target
|
// Draw HDR target
|
||||||
_drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
|
_drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||||
@@ -118,7 +118,7 @@ void SwapchainManager::create_swapchain(uint32_t width, uint32_t height)
|
|||||||
//use vsync present mode
|
//use vsync present mode
|
||||||
.set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR)
|
.set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR)
|
||||||
.set_desired_extent(width, height)
|
.set_desired_extent(width, height)
|
||||||
.add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT)
|
.add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
|
||||||
.build()
|
.build()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
@@ -127,6 +127,7 @@ void SwapchainManager::create_swapchain(uint32_t width, uint32_t height)
|
|||||||
_swapchain = vkbSwapchain.swapchain;
|
_swapchain = vkbSwapchain.swapchain;
|
||||||
_swapchainImages = vkbSwapchain.get_images().value();
|
_swapchainImages = vkbSwapchain.get_images().value();
|
||||||
_swapchainImageViews = vkbSwapchain.get_image_views().value();
|
_swapchainImageViews = vkbSwapchain.get_image_views().value();
|
||||||
|
_swapchainImageLayouts.assign(_swapchainImages.size(), VK_IMAGE_LAYOUT_UNDEFINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwapchainManager::destroy_swapchain() const
|
void SwapchainManager::destroy_swapchain() const
|
||||||
@@ -142,83 +143,36 @@ void SwapchainManager::destroy_swapchain() const
|
|||||||
|
|
||||||
void SwapchainManager::resize_swapchain(struct SDL_Window *window)
|
void SwapchainManager::resize_swapchain(struct SDL_Window *window)
|
||||||
{
|
{
|
||||||
|
int w, h;
|
||||||
|
// HiDPI-aware drawable size for correct pixel dimensions
|
||||||
|
SDL_Vulkan_GetDrawableSize(window, &w, &h);
|
||||||
|
if (w <= 0 || h <= 0)
|
||||||
|
{
|
||||||
|
// Window may be minimized or in a transient resize state; keep current swapchain.
|
||||||
|
resize_requested = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vkDeviceWaitIdle(_deviceManager->device());
|
vkDeviceWaitIdle(_deviceManager->device());
|
||||||
|
|
||||||
destroy_swapchain();
|
destroy_swapchain();
|
||||||
|
|
||||||
// Destroy per-frame images before recreating them
|
|
||||||
_deletionQueue.flush();
|
|
||||||
|
|
||||||
int w, h;
|
|
||||||
// HiDPI-aware drawable size for correct pixel dimensions
|
|
||||||
SDL_Vulkan_GetDrawableSize(window, &w, &h);
|
|
||||||
_windowExtent.width = w;
|
_windowExtent.width = w;
|
||||||
_windowExtent.height = h;
|
_windowExtent.height = h;
|
||||||
|
|
||||||
create_swapchain(_windowExtent.width, _windowExtent.height);
|
create_swapchain(_windowExtent.width, _windowExtent.height);
|
||||||
|
|
||||||
// Recreate frame images at the new size
|
|
||||||
// (duplicate the same logic used at init time)
|
|
||||||
VkExtent3D drawImageExtent = { _windowExtent.width, _windowExtent.height, 1 };
|
|
||||||
|
|
||||||
_drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
|
|
||||||
_drawImage.imageExtent = drawImageExtent;
|
|
||||||
|
|
||||||
VkImageUsageFlags drawImageUsages{};
|
|
||||||
drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
||||||
drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT;
|
|
||||||
drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
||||||
drawImageUsages |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
||||||
|
|
||||||
VkImageCreateInfo rimg_info = vkinit::image_create_info(_drawImage.imageFormat, drawImageUsages, drawImageExtent);
|
|
||||||
VmaAllocationCreateInfo rimg_allocinfo = {};
|
|
||||||
rimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
|
||||||
rimg_allocinfo.requiredFlags = static_cast<VkMemoryPropertyFlags>(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
|
||||||
vmaCreateImage(_deviceManager->allocator(), &rimg_info, &rimg_allocinfo, &_drawImage.image, &_drawImage.allocation,
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
VkImageViewCreateInfo rview_info = vkinit::imageview_create_info(_drawImage.imageFormat, _drawImage.image,
|
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT);
|
|
||||||
VK_CHECK(vkCreateImageView(_deviceManager->device(), &rview_info, nullptr, &_drawImage.imageView));
|
|
||||||
|
|
||||||
_depthImage.imageFormat = VK_FORMAT_D32_SFLOAT;
|
|
||||||
_depthImage.imageExtent = drawImageExtent;
|
|
||||||
VkImageUsageFlags depthImageUsages{};
|
|
||||||
depthImageUsages |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
|
||||||
VkImageCreateInfo dimg_info = vkinit::image_create_info(_depthImage.imageFormat, depthImageUsages, drawImageExtent);
|
|
||||||
vmaCreateImage(_deviceManager->allocator(), &dimg_info, &rimg_allocinfo, &_depthImage.image,
|
|
||||||
&_depthImage.allocation, nullptr);
|
|
||||||
|
|
||||||
VkImageViewCreateInfo dview_info = vkinit::imageview_create_info(_depthImage.imageFormat, _depthImage.image,
|
|
||||||
VK_IMAGE_ASPECT_DEPTH_BIT);
|
|
||||||
VK_CHECK(vkCreateImageView(_deviceManager->device(), &dview_info, nullptr, &_depthImage.imageView));
|
|
||||||
|
|
||||||
_gBufferPosition = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R16G16B16A16_SFLOAT,
|
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
|
||||||
_gBufferNormal = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R16G16B16A16_SFLOAT,
|
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
|
||||||
_gBufferAlbedo = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R8G8B8A8_UNORM,
|
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
|
||||||
_gBufferExtra = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R16G16B16A16_SFLOAT,
|
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
|
|
||||||
_idBuffer = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R32_UINT,
|
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
|
||||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
|
||||||
VK_IMAGE_USAGE_SAMPLED_BIT);
|
|
||||||
|
|
||||||
_deletionQueue.push_function([=]() {
|
|
||||||
vkDestroyImageView(_deviceManager->device(), _drawImage.imageView, nullptr);
|
|
||||||
vmaDestroyImage(_deviceManager->allocator(), _drawImage.image, _drawImage.allocation);
|
|
||||||
|
|
||||||
vkDestroyImageView(_deviceManager->device(), _depthImage.imageView, nullptr);
|
|
||||||
vmaDestroyImage(_deviceManager->allocator(), _depthImage.image, _depthImage.allocation);
|
|
||||||
|
|
||||||
_resourceManager->destroy_image(_gBufferPosition);
|
|
||||||
_resourceManager->destroy_image(_gBufferNormal);
|
|
||||||
_resourceManager->destroy_image(_gBufferAlbedo);
|
|
||||||
_resourceManager->destroy_image(_gBufferExtra);
|
|
||||||
_resourceManager->destroy_image(_idBuffer);
|
|
||||||
});
|
|
||||||
|
|
||||||
resize_requested = false;
|
resize_requested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkImageLayout SwapchainManager::swapchain_image_layout(uint32_t index) const
|
||||||
|
{
|
||||||
|
if (index >= _swapchainImageLayouts.size()) return VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
return _swapchainImageLayouts[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapchainManager::set_swapchain_image_layout(uint32_t index, VkImageLayout layout)
|
||||||
|
{
|
||||||
|
if (index >= _swapchainImageLayouts.size()) return;
|
||||||
|
_swapchainImageLayouts[index] = layout;
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ public:
|
|||||||
VkExtent2D swapchainExtent() const { return _swapchainExtent; }
|
VkExtent2D swapchainExtent() const { return _swapchainExtent; }
|
||||||
const std::vector<VkImage> &swapchainImages() const { return _swapchainImages; }
|
const std::vector<VkImage> &swapchainImages() const { return _swapchainImages; }
|
||||||
const std::vector<VkImageView> &swapchainImageViews() const { return _swapchainImageViews; }
|
const std::vector<VkImageView> &swapchainImageViews() const { return _swapchainImageViews; }
|
||||||
|
VkImageLayout swapchain_image_layout(uint32_t index) const;
|
||||||
|
void set_swapchain_image_layout(uint32_t index, VkImageLayout layout);
|
||||||
|
|
||||||
AllocatedImage drawImage() const { return _drawImage; }
|
AllocatedImage drawImage() const { return _drawImage; }
|
||||||
AllocatedImage depthImage() const { return _depthImage; }
|
AllocatedImage depthImage() const { return _depthImage; }
|
||||||
@@ -47,6 +49,7 @@ private:
|
|||||||
|
|
||||||
std::vector<VkImage> _swapchainImages;
|
std::vector<VkImage> _swapchainImages;
|
||||||
std::vector<VkImageView> _swapchainImageViews;
|
std::vector<VkImageView> _swapchainImageViews;
|
||||||
|
std::vector<VkImageLayout> _swapchainImageLayouts;
|
||||||
|
|
||||||
AllocatedImage _drawImage = {};
|
AllocatedImage _drawImage = {};
|
||||||
AllocatedImage _depthImage = {};
|
AllocatedImage _depthImage = {};
|
||||||
|
|||||||
@@ -759,19 +759,32 @@ void VulkanEngine::draw()
|
|||||||
|
|
||||||
uint32_t swapchainImageIndex;
|
uint32_t swapchainImageIndex;
|
||||||
|
|
||||||
VkResult e = vkAcquireNextImageKHR(_deviceManager->device(), _swapchainManager->swapchain(), 1000000000,
|
VkResult e = vkAcquireNextImageKHR(_deviceManager->device(),
|
||||||
|
_swapchainManager->swapchain(),
|
||||||
|
1000000000,
|
||||||
get_current_frame()._swapchainSemaphore,
|
get_current_frame()._swapchainSemaphore,
|
||||||
nullptr, &swapchainImageIndex);
|
nullptr,
|
||||||
|
&swapchainImageIndex);
|
||||||
if (e == VK_ERROR_OUT_OF_DATE_KHR)
|
if (e == VK_ERROR_OUT_OF_DATE_KHR)
|
||||||
{
|
{
|
||||||
resize_requested = true;
|
resize_requested = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (e == VK_SUBOPTIMAL_KHR)
|
||||||
|
{
|
||||||
|
// Acquire succeeded and signaled the semaphore. Keep rendering this frame
|
||||||
|
// so the semaphore gets waited on, but schedule a resize soon.
|
||||||
|
resize_requested = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VK_CHECK(e);
|
||||||
|
}
|
||||||
|
|
||||||
_drawExtent.height = std::min(_swapchainManager->swapchainExtent().height,
|
// Fixed logical render resolution (letterboxed): draw extent is derived
|
||||||
_swapchainManager->drawImage().imageExtent.height) * renderScale;
|
// from the engine's logical render size instead of the swapchain/window.
|
||||||
_drawExtent.width = std::min(_swapchainManager->swapchainExtent().width,
|
_drawExtent.width = static_cast<uint32_t>(static_cast<float>(_logicalRenderExtent.width) * renderScale);
|
||||||
_swapchainManager->drawImage().imageExtent.width) * renderScale;
|
_drawExtent.height = static_cast<uint32_t>(static_cast<float>(_logicalRenderExtent.height) * renderScale);
|
||||||
|
|
||||||
VK_CHECK(vkResetFences(_deviceManager->device(), 1, &get_current_frame()._renderFence));
|
VK_CHECK(vkResetFences(_deviceManager->device(), 1, &get_current_frame()._renderFence));
|
||||||
|
|
||||||
@@ -1041,7 +1054,11 @@ void VulkanEngine::draw()
|
|||||||
presentInfo.pImageIndices = &swapchainImageIndex;
|
presentInfo.pImageIndices = &swapchainImageIndex;
|
||||||
|
|
||||||
VkResult presentResult = vkQueuePresentKHR(_deviceManager->graphicsQueue(), &presentInfo);
|
VkResult presentResult = vkQueuePresentKHR(_deviceManager->graphicsQueue(), &presentInfo);
|
||||||
if (presentResult == VK_ERROR_OUT_OF_DATE_KHR)
|
if (_swapchainManager)
|
||||||
|
{
|
||||||
|
_swapchainManager->set_swapchain_image_layout(swapchainImageIndex, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||||
|
}
|
||||||
|
if (presentResult == VK_ERROR_OUT_OF_DATE_KHR || presentResult == VK_SUBOPTIMAL_KHR)
|
||||||
{
|
{
|
||||||
resize_requested = true;
|
resize_requested = true;
|
||||||
}
|
}
|
||||||
@@ -1065,13 +1082,23 @@ void VulkanEngine::run()
|
|||||||
if (e.type == SDL_QUIT) bQuit = true;
|
if (e.type == SDL_QUIT) bQuit = true;
|
||||||
if (e.type == SDL_WINDOWEVENT)
|
if (e.type == SDL_WINDOWEVENT)
|
||||||
{
|
{
|
||||||
if (e.window.event == SDL_WINDOWEVENT_MINIMIZED)
|
switch (e.window.event)
|
||||||
{
|
{
|
||||||
|
case SDL_WINDOWEVENT_MINIMIZED:
|
||||||
freeze_rendering = true;
|
freeze_rendering = true;
|
||||||
}
|
break;
|
||||||
if (e.window.event == SDL_WINDOWEVENT_RESTORED)
|
case SDL_WINDOWEVENT_RESTORED:
|
||||||
{
|
|
||||||
freeze_rendering = false;
|
freeze_rendering = false;
|
||||||
|
resize_requested = true;
|
||||||
|
_last_resize_event_ms = SDL_GetTicks();
|
||||||
|
break;
|
||||||
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||||
|
case SDL_WINDOWEVENT_RESIZED:
|
||||||
|
resize_requested = true;
|
||||||
|
_last_resize_event_ms = SDL_GetTicks();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e.type == SDL_MOUSEMOTION)
|
if (e.type == SDL_MOUSEMOTION)
|
||||||
@@ -1189,8 +1216,13 @@ void VulkanEngine::run()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (resize_requested)
|
if (resize_requested)
|
||||||
|
{
|
||||||
|
const uint32_t now_ms = SDL_GetTicks();
|
||||||
|
if (now_ms - _last_resize_event_ms >= RESIZE_DEBOUNCE_MS)
|
||||||
{
|
{
|
||||||
_swapchainManager->resize_swapchain(_window);
|
_swapchainManager->resize_swapchain(_window);
|
||||||
|
resize_requested = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin frame: wait for the GPU, resolve pending ID-buffer picks,
|
// Begin frame: wait for the GPU, resolve pending ID-buffer picks,
|
||||||
|
|||||||
@@ -251,4 +251,8 @@ private:
|
|||||||
void init_mesh_pipeline();
|
void init_mesh_pipeline();
|
||||||
|
|
||||||
void init_default_data();
|
void init_default_data();
|
||||||
|
|
||||||
|
// Debounce swapchain recreation during live window resizing.
|
||||||
|
uint32_t _last_resize_event_ms{0};
|
||||||
|
static constexpr uint32_t RESIZE_DEBOUNCE_MS = 150;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,12 +2,18 @@
|
|||||||
#include <core/context.h>
|
#include <core/context.h>
|
||||||
#include <core/device/images.h>
|
#include <core/device/images.h>
|
||||||
#include <core/util/initializers.h>
|
#include <core/util/initializers.h>
|
||||||
|
#include <core/pipeline/manager.h>
|
||||||
|
#include <core/descriptor/descriptors.h>
|
||||||
|
#include <core/descriptor/manager.h>
|
||||||
|
#include <core/frame/resources.h>
|
||||||
|
#include <core/pipeline/sampler.h>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include <core/device/swapchain.h>
|
#include <core/device/swapchain.h>
|
||||||
#include <core/util/initializers.h>
|
#include <core/util/initializers.h>
|
||||||
@@ -17,6 +23,8 @@
|
|||||||
#include "core/device/device.h"
|
#include "core/device/device.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "assets/manager.h"
|
||||||
|
|
||||||
void RenderGraph::init(EngineContext *ctx)
|
void RenderGraph::init(EngineContext *ctx)
|
||||||
{
|
{
|
||||||
_context = ctx;
|
_context = ctx;
|
||||||
@@ -694,11 +702,17 @@ void RenderGraph::execute(VkCommandBuffer cmd)
|
|||||||
VkRenderingAttachmentInfo depthInfo{};
|
VkRenderingAttachmentInfo depthInfo{};
|
||||||
bool hasDepth = false;
|
bool hasDepth = false;
|
||||||
|
|
||||||
// Choose renderArea as the min of all attachment extents and the desired draw extent
|
// Choose renderArea as the min of all attachment extents.
|
||||||
VkExtent2D chosenExtent{_context->getDrawExtent()};
|
// Do not pre-clamp to drawExtent here: swapchain passes (ImGui, present)
|
||||||
|
// should be able to use the full window extent.
|
||||||
|
VkExtent2D chosenExtent{0, 0};
|
||||||
auto clamp_min = [](VkExtent2D a, VkExtent2D b) {
|
auto clamp_min = [](VkExtent2D a, VkExtent2D b) {
|
||||||
return VkExtent2D{std::min(a.width, b.width), std::min(a.height, b.height)};
|
return VkExtent2D{std::min(a.width, b.width), std::min(a.height, b.height)};
|
||||||
};
|
};
|
||||||
|
auto set_or_clamp = [&](VkExtent2D e) {
|
||||||
|
if (chosenExtent.width == 0 || chosenExtent.height == 0) chosenExtent = e;
|
||||||
|
else chosenExtent = clamp_min(chosenExtent, e);
|
||||||
|
};
|
||||||
|
|
||||||
// Resolve color attachments
|
// Resolve color attachments
|
||||||
VkExtent2D firstColorExtent{0,0};
|
VkExtent2D firstColorExtent{0,0};
|
||||||
@@ -714,7 +728,7 @@ void RenderGraph::execute(VkCommandBuffer cmd)
|
|||||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
if (!a.store) info.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
if (!a.store) info.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||||
colorInfos.push_back(info);
|
colorInfos.push_back(info);
|
||||||
if (rec->extent.width && rec->extent.height) chosenExtent = clamp_min(chosenExtent, rec->extent);
|
if (rec->extent.width && rec->extent.height) set_or_clamp(rec->extent);
|
||||||
if (firstColorExtent.width == 0 && firstColorExtent.height == 0)
|
if (firstColorExtent.width == 0 && firstColorExtent.height == 0)
|
||||||
{
|
{
|
||||||
firstColorExtent = rec->extent;
|
firstColorExtent = rec->extent;
|
||||||
@@ -748,10 +762,15 @@ void RenderGraph::execute(VkCommandBuffer cmd)
|
|||||||
else depthInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
else depthInfo.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||||
if (!p.depthAttachment.store) depthInfo.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
if (!p.depthAttachment.store) depthInfo.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||||
hasDepth = true;
|
hasDepth = true;
|
||||||
if (rec->extent.width && rec->extent.height) chosenExtent = clamp_min(chosenExtent, rec->extent);
|
if (rec->extent.width && rec->extent.height) set_or_clamp(rec->extent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chosenExtent.width == 0 || chosenExtent.height == 0)
|
||||||
|
{
|
||||||
|
chosenExtent = _context->getDrawExtent();
|
||||||
|
}
|
||||||
|
|
||||||
VkRenderingInfo ri{};
|
VkRenderingInfo ri{};
|
||||||
ri.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
ri.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||||
ri.renderArea = VkRect2D{VkOffset2D{0, 0}, chosenExtent};
|
ri.renderArea = VkRect2D{VkOffset2D{0, 0}, chosenExtent};
|
||||||
@@ -800,17 +819,86 @@ void RenderGraph::add_present_chain(RGImageHandle sourceDraw,
|
|||||||
if (!sourceDraw.valid() || !targetSwapchain.valid()) return;
|
if (!sourceDraw.valid() || !targetSwapchain.valid()) return;
|
||||||
|
|
||||||
add_pass(
|
add_pass(
|
||||||
"CopyToSwapchain",
|
"PresentLetterbox",
|
||||||
RGPassType::Transfer,
|
RGPassType::Graphics,
|
||||||
[sourceDraw, targetSwapchain](RGPassBuilder &builder, EngineContext *) {
|
[sourceDraw, targetSwapchain](RGPassBuilder &builder, EngineContext *) {
|
||||||
builder.read(sourceDraw, RGImageUsage::TransferSrc);
|
builder.read(sourceDraw, RGImageUsage::SampledFragment);
|
||||||
builder.write(targetSwapchain, RGImageUsage::TransferDst);
|
VkClearValue clear{};
|
||||||
|
clear.color = {{0.f, 0.f, 0.f, 1.f}};
|
||||||
|
builder.write_color(targetSwapchain, true, clear);
|
||||||
},
|
},
|
||||||
[sourceDraw, targetSwapchain](VkCommandBuffer cmd, const RGPassResources &res, EngineContext *ctx) {
|
[sourceDraw, targetSwapchain](VkCommandBuffer cmd, const RGPassResources &res, EngineContext *ctx) {
|
||||||
VkImage src = res.image(sourceDraw);
|
if (!ctx || !ctx->currentFrame || !ctx->pipelines) return;
|
||||||
VkImage dst = res.image(targetSwapchain);
|
|
||||||
if (src == VK_NULL_HANDLE || dst == VK_NULL_HANDLE) return;
|
VkImageView srcView = res.image_view(sourceDraw);
|
||||||
vkutil::copy_image_to_image(cmd, src, dst, ctx->getDrawExtent(), ctx->getSwapchain()->swapchainExtent());
|
VkImageView dstView = res.image_view(targetSwapchain);
|
||||||
|
if (srcView == VK_NULL_HANDLE || dstView == VK_NULL_HANDLE) return;
|
||||||
|
|
||||||
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||||
|
VkPipelineLayout layout = VK_NULL_HANDLE;
|
||||||
|
if (!ctx->pipelines->getGraphics("present_letterbox", pipeline, layout))
|
||||||
|
{
|
||||||
|
GraphicsPipelineCreateInfo info{};
|
||||||
|
info.vertexShaderPath = ctx->getAssets()->shaderPath("fullscreen.vert.spv");
|
||||||
|
info.fragmentShaderPath = ctx->getAssets()->shaderPath("present_letterbox.frag.spv");
|
||||||
|
info.setLayouts = { ctx->getDescriptorLayouts()->singleImageLayout() };
|
||||||
|
|
||||||
|
VkPushConstantRange pcr{};
|
||||||
|
pcr.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
pcr.offset = 0;
|
||||||
|
pcr.size = sizeof(glm::vec4);
|
||||||
|
info.pushConstants = { pcr };
|
||||||
|
|
||||||
|
VkFormat swapFmt = ctx->getSwapchain()->swapchainImageFormat();
|
||||||
|
info.configure = [swapFmt](PipelineBuilder &b) {
|
||||||
|
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||||
|
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
|
||||||
|
b.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
|
||||||
|
b.set_multisampling_none();
|
||||||
|
b.disable_depthtest();
|
||||||
|
b.disable_blending();
|
||||||
|
b.set_color_attachment_format(swapFmt);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!ctx->pipelines->createGraphicsPipeline("present_letterbox", info))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ctx->pipelines->getGraphics("present_letterbox", pipeline, layout))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDevice device = ctx->getDevice()->device();
|
||||||
|
VkDescriptorSetLayout setLayout = ctx->getDescriptorLayouts()->singleImageLayout();
|
||||||
|
VkDescriptorSet set = ctx->currentFrame->_frameDescriptors.allocate(device, setLayout);
|
||||||
|
DescriptorWriter writer;
|
||||||
|
writer.write_image(0, srcView, ctx->getSamplers()->defaultLinear(),
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||||
|
writer.update_set(device, set);
|
||||||
|
|
||||||
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, 1, &set, 0, nullptr);
|
||||||
|
|
||||||
|
VkExtent2D srcSize = ctx->getDrawExtent();
|
||||||
|
VkExtent2D dstSize = ctx->getSwapchain()->swapchainExtent();
|
||||||
|
VkRect2D dstRect = vkutil::compute_letterbox_rect(srcSize, dstSize);
|
||||||
|
|
||||||
|
float minX = dstSize.width > 0 ? float(dstRect.offset.x) / float(dstSize.width) : 0.f;
|
||||||
|
float minY = dstSize.height > 0 ? float(dstRect.offset.y) / float(dstSize.height) : 0.f;
|
||||||
|
float sizeX = dstSize.width > 0 ? float(dstRect.extent.width) / float(dstSize.width) : 1.f;
|
||||||
|
float sizeY = dstSize.height > 0 ? float(dstRect.extent.height) / float(dstSize.height) : 1.f;
|
||||||
|
|
||||||
|
glm::vec4 pc{minX, minY, sizeX, sizeY};
|
||||||
|
vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(glm::vec4), &pc);
|
||||||
|
|
||||||
|
VkViewport vp{0.f, 0.f, float(dstSize.width), float(dstSize.height), 0.f, 1.f};
|
||||||
|
VkRect2D sc{{0, 0}, dstSize};
|
||||||
|
vkCmdSetViewport(cmd, 0, 1, &vp);
|
||||||
|
vkCmdSetScissor(cmd, 0, 1, &sc);
|
||||||
|
vkCmdDraw(cmd, 3, 1, 0, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (appendExtra)
|
if (appendExtra)
|
||||||
@@ -990,9 +1078,8 @@ RGImageHandle RenderGraph::import_swapchain_image(uint32_t index)
|
|||||||
d.imageView = views[index];
|
d.imageView = views[index];
|
||||||
d.format = _context->getSwapchain()->swapchainImageFormat();
|
d.format = _context->getSwapchain()->swapchainImageFormat();
|
||||||
d.extent = _context->getSwapchain()->swapchainExtent();
|
d.extent = _context->getSwapchain()->swapchainExtent();
|
||||||
// On first use after swapchain creation, images are in UNDEFINED layout.
|
// Track actual layout across frames. After present, images are in PRESENT_SRC_KHR.
|
||||||
// Start from UNDEFINED so the graph inserts the necessary transition.
|
d.currentLayout = _context->getSwapchain()->swapchain_image_layout(index);
|
||||||
d.currentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
||||||
return import_image(d);
|
return import_image(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <core/pipeline/manager.h>
|
#include <core/pipeline/manager.h>
|
||||||
#include <core/assets/manager.h>
|
#include <core/assets/manager.h>
|
||||||
#include <core/device/device.h>
|
#include <core/device/device.h>
|
||||||
|
#include <core/device/swapchain.h>
|
||||||
#include <core/device/resource.h>
|
#include <core/device/resource.h>
|
||||||
#include <core/pipeline/sampler.h>
|
#include <core/pipeline/sampler.h>
|
||||||
#include <render/graph/graph.h>
|
#include <render/graph/graph.h>
|
||||||
@@ -23,6 +24,11 @@ void FxaaPass::init(EngineContext *context)
|
|||||||
|
|
||||||
_inputSetLayout = _context->getDescriptorLayouts()->singleImageLayout();
|
_inputSetLayout = _context->getDescriptorLayouts()->singleImageLayout();
|
||||||
|
|
||||||
|
const VkFormat ldrFormat =
|
||||||
|
(_context && _context->getSwapchain())
|
||||||
|
? _context->getSwapchain()->swapchainImageFormat()
|
||||||
|
: VK_FORMAT_B8G8R8A8_UNORM;
|
||||||
|
|
||||||
GraphicsPipelineCreateInfo info{};
|
GraphicsPipelineCreateInfo info{};
|
||||||
info.vertexShaderPath = _context->getAssets()->shaderPath("fullscreen.vert.spv");
|
info.vertexShaderPath = _context->getAssets()->shaderPath("fullscreen.vert.spv");
|
||||||
info.fragmentShaderPath = _context->getAssets()->shaderPath("fxaa.frag.spv");
|
info.fragmentShaderPath = _context->getAssets()->shaderPath("fxaa.frag.spv");
|
||||||
@@ -34,14 +40,14 @@ void FxaaPass::init(EngineContext *context)
|
|||||||
pcr.size = sizeof(FxaaPush);
|
pcr.size = sizeof(FxaaPush);
|
||||||
info.pushConstants = { pcr };
|
info.pushConstants = { pcr };
|
||||||
|
|
||||||
info.configure = [this](PipelineBuilder &b) {
|
info.configure = [ldrFormat](PipelineBuilder &b) {
|
||||||
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||||
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
|
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
|
||||||
b.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
|
b.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
|
||||||
b.set_multisampling_none();
|
b.set_multisampling_none();
|
||||||
b.disable_depthtest();
|
b.disable_depthtest();
|
||||||
b.disable_blending();
|
b.disable_blending();
|
||||||
b.set_color_attachment_format(VK_FORMAT_R8G8B8A8_UNORM);
|
b.set_color_attachment_format(ldrFormat);
|
||||||
};
|
};
|
||||||
|
|
||||||
_context->pipelines->createGraphicsPipeline("fxaa", info);
|
_context->pipelines->createGraphicsPipeline("fxaa", info);
|
||||||
@@ -70,9 +76,14 @@ RGImageHandle FxaaPass::register_graph(RenderGraph *graph, RGImageHandle ldrInpu
|
|||||||
return ldrInput;
|
return ldrInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VkFormat ldrFormat =
|
||||||
|
(_context && _context->getSwapchain())
|
||||||
|
? _context->getSwapchain()->swapchainImageFormat()
|
||||||
|
: VK_FORMAT_B8G8R8A8_UNORM;
|
||||||
|
|
||||||
RGImageDesc desc{};
|
RGImageDesc desc{};
|
||||||
desc.name = "ldr.fxaa";
|
desc.name = "ldr.fxaa";
|
||||||
desc.format = VK_FORMAT_R8G8B8A8_UNORM;
|
desc.format = ldrFormat;
|
||||||
desc.extent = _context->getDrawExtent();
|
desc.extent = _context->getDrawExtent();
|
||||||
desc.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
desc.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||||||
| VK_IMAGE_USAGE_SAMPLED_BIT
|
| VK_IMAGE_USAGE_SAMPLED_BIT
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <core/pipeline/manager.h>
|
#include <core/pipeline/manager.h>
|
||||||
#include <core/assets/manager.h>
|
#include <core/assets/manager.h>
|
||||||
#include <core/device/device.h>
|
#include <core/device/device.h>
|
||||||
|
#include <core/device/swapchain.h>
|
||||||
#include <core/device/resource.h>
|
#include <core/device/resource.h>
|
||||||
#include <core/pipeline/sampler.h>
|
#include <core/pipeline/sampler.h>
|
||||||
#include <render/graph/graph.h>
|
#include <render/graph/graph.h>
|
||||||
@@ -28,6 +29,11 @@ void TonemapPass::init(EngineContext *context)
|
|||||||
|
|
||||||
_inputSetLayout = _context->getDescriptorLayouts()->singleImageLayout();
|
_inputSetLayout = _context->getDescriptorLayouts()->singleImageLayout();
|
||||||
|
|
||||||
|
const VkFormat ldrFormat =
|
||||||
|
(_context && _context->getSwapchain())
|
||||||
|
? _context->getSwapchain()->swapchainImageFormat()
|
||||||
|
: VK_FORMAT_B8G8R8A8_UNORM;
|
||||||
|
|
||||||
GraphicsPipelineCreateInfo info{};
|
GraphicsPipelineCreateInfo info{};
|
||||||
info.vertexShaderPath = _context->getAssets()->shaderPath("fullscreen.vert.spv");
|
info.vertexShaderPath = _context->getAssets()->shaderPath("fullscreen.vert.spv");
|
||||||
info.fragmentShaderPath = _context->getAssets()->shaderPath("tonemap.frag.spv");
|
info.fragmentShaderPath = _context->getAssets()->shaderPath("tonemap.frag.spv");
|
||||||
@@ -39,14 +45,14 @@ void TonemapPass::init(EngineContext *context)
|
|||||||
pcr.size = sizeof(TonemapPush);
|
pcr.size = sizeof(TonemapPush);
|
||||||
info.pushConstants = { pcr };
|
info.pushConstants = { pcr };
|
||||||
|
|
||||||
info.configure = [this](PipelineBuilder &b) {
|
info.configure = [ldrFormat](PipelineBuilder &b) {
|
||||||
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||||
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
|
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
|
||||||
b.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
|
b.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
|
||||||
b.set_multisampling_none();
|
b.set_multisampling_none();
|
||||||
b.disable_depthtest();
|
b.disable_depthtest();
|
||||||
b.disable_blending();
|
b.disable_blending();
|
||||||
b.set_color_attachment_format(VK_FORMAT_R8G8B8A8_UNORM);
|
b.set_color_attachment_format(ldrFormat);
|
||||||
};
|
};
|
||||||
|
|
||||||
_context->pipelines->createGraphicsPipeline("tonemap", info);
|
_context->pipelines->createGraphicsPipeline("tonemap", info);
|
||||||
@@ -71,9 +77,14 @@ RGImageHandle TonemapPass::register_graph(RenderGraph *graph, RGImageHandle hdrI
|
|||||||
{
|
{
|
||||||
if (!graph || !hdrInput.valid()) return {};
|
if (!graph || !hdrInput.valid()) return {};
|
||||||
|
|
||||||
|
const VkFormat ldrFormat =
|
||||||
|
(_context && _context->getSwapchain())
|
||||||
|
? _context->getSwapchain()->swapchainImageFormat()
|
||||||
|
: VK_FORMAT_B8G8R8A8_UNORM;
|
||||||
|
|
||||||
RGImageDesc desc{};
|
RGImageDesc desc{};
|
||||||
desc.name = "ldr.tonemap";
|
desc.name = "ldr.tonemap";
|
||||||
desc.format = VK_FORMAT_R8G8B8A8_UNORM;
|
desc.format = ldrFormat;
|
||||||
desc.extent = _context->getDrawExtent();
|
desc.extent = _context->getDrawExtent();
|
||||||
desc.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
desc.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||||||
| VK_IMAGE_USAGE_SAMPLED_BIT
|
| VK_IMAGE_USAGE_SAMPLED_BIT
|
||||||
|
|||||||
@@ -265,10 +265,22 @@ void SceneManager::update_scene()
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Keep projection FOV in sync with the camera so that CPU ray picking
|
// Keep projection FOV in sync with the camera so that CPU ray picking
|
||||||
// matches what is rendered on-screen.
|
// matches what is rendered inside the fixed logical render area (letterboxed).
|
||||||
const float fov = glm::radians(mainCamera.fovDegrees);
|
const float fov = glm::radians(mainCamera.fovDegrees);
|
||||||
const float aspect = (float) _context->getSwapchain()->windowExtent().width /
|
|
||||||
(float) _context->getSwapchain()->windowExtent().height;
|
// Derive aspect ratio from the fixed logical render size instead of the window/swapchain.
|
||||||
|
VkExtent2D logicalExtent{ kRenderWidth, kRenderHeight };
|
||||||
|
if (_context)
|
||||||
|
{
|
||||||
|
VkExtent2D ctxExtent = _context->getLogicalRenderExtent();
|
||||||
|
if (ctxExtent.width > 0 && ctxExtent.height > 0)
|
||||||
|
{
|
||||||
|
logicalExtent = ctxExtent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const float aspect = static_cast<float>(logicalExtent.width) /
|
||||||
|
static_cast<float>(logicalExtent.height);
|
||||||
const float nearPlane = 0.1f;
|
const float nearPlane = 0.1f;
|
||||||
glm::mat4 projection = makeReversedInfinitePerspective(fov, aspect, nearPlane);
|
glm::mat4 projection = makeReversedInfinitePerspective(fov, aspect, nearPlane);
|
||||||
// Vulkan NDC has inverted Y.
|
// Vulkan NDC has inverted Y.
|
||||||
|
|||||||
Reference in New Issue
Block a user