ADD: changeable resolution

This commit is contained in:
2025-12-13 23:26:35 +09:00
parent d3ab4d18a8
commit eb5a04e95a
8 changed files with 285 additions and 104 deletions

View File

@@ -10,6 +10,10 @@ endif()
find_package(Vulkan REQUIRED)
# Third-party deps are vendored; keep builds offline-friendly by default.
# BVH2's CMake enables tests by default, which would FetchContent googletest.
set(BVH2_ENABLE_TESTS OFF CACHE BOOL "Disable BVH2 tests (offline builds)" FORCE)
add_subdirectory(third_party)
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin")

View File

@@ -16,83 +16,15 @@ void SwapchainManager::init_swapchain()
{
create_swapchain(_windowExtent.width, _windowExtent.height);
// Create images used across the frame (draw, depth, GBuffer)
// Split to helper so we can reuse on resize
// (Definition added below)
//
// 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.
// depth/draw/gbuffer sized to fixed logical render extent (letterboxed)
auto create_frame_images = [this]() {
VkExtent3D drawImageExtent = { kRenderWidth, kRenderHeight, 1 };
// Draw HDR target
_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;
// Post-processing (tonemap) samples HDR; allow sampling.
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));
// Depth
_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));
// GBuffer (SRGB not used to keep linear lighting)
_gBufferPosition = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R32G32B32A32_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);
});
};
create_frame_images();
// Create images used across the frame (draw, depth, GBuffer).
// These are sized to _renderExtent (independent of the swapchain extent)
// so the engine can render at a different internal resolution and then
// upscale/letterbox into the swapchain.
if (_renderExtent.width == 0 || _renderExtent.height == 0)
{
_renderExtent = _windowExtent;
}
resize_render_targets(_renderExtent);
}
void SwapchainManager::cleanup()
@@ -102,6 +34,104 @@ void SwapchainManager::cleanup()
fmt::print("SwapchainManager::cleanup()\n");
}
void SwapchainManager::resize_render_targets(VkExtent2D renderExtent)
{
if (!_deviceManager || !_resourceManager) return;
if (renderExtent.width == 0 || renderExtent.height == 0) return;
// Avoid doing work when nothing changes (common when called every frame).
if (_renderExtent.width == renderExtent.width &&
_renderExtent.height == renderExtent.height &&
_drawImage.image != VK_NULL_HANDLE &&
_depthImage.image != VK_NULL_HANDLE)
{
return;
}
// Ensure no in-flight work references these images before we destroy them.
vkDeviceWaitIdle(_deviceManager->device());
// Destroy previous targets (if any), then recreate at the new extent.
_deletionQueue.flush();
_renderExtent = renderExtent;
VkExtent3D drawImageExtent = { _renderExtent.width, _renderExtent.height, 1 };
// Draw HDR target
_drawImage = {};
_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;
// Post-processing (tonemap) samples HDR; allow sampling.
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));
// Depth
_depthImage = {};
_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));
// GBuffer (SRGB not used to keep linear lighting)
_gBufferPosition = _resourceManager->create_image(drawImageExtent, VK_FORMAT_R32G32B32A32_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([this]() {
if (_drawImage.imageView) vkDestroyImageView(_deviceManager->device(), _drawImage.imageView, nullptr);
if (_drawImage.image) vmaDestroyImage(_deviceManager->allocator(), _drawImage.image, _drawImage.allocation);
if (_depthImage.imageView) vkDestroyImageView(_deviceManager->device(), _depthImage.imageView, nullptr);
if (_depthImage.image) vmaDestroyImage(_deviceManager->allocator(), _depthImage.image, _depthImage.allocation);
if (_gBufferPosition.image) _resourceManager->destroy_image(_gBufferPosition);
if (_gBufferNormal.image) _resourceManager->destroy_image(_gBufferNormal);
if (_gBufferAlbedo.image) _resourceManager->destroy_image(_gBufferAlbedo);
if (_gBufferExtra.image) _resourceManager->destroy_image(_gBufferExtra);
if (_idBuffer.image) _resourceManager->destroy_image(_idBuffer);
_drawImage = {};
_depthImage = {};
_gBufferPosition = {};
_gBufferNormal = {};
_gBufferAlbedo = {};
_gBufferExtra = {};
_idBuffer = {};
});
}
void SwapchainManager::create_swapchain(uint32_t width, uint32_t height)
{
vkb::SwapchainBuilder swapchainBuilder{

View File

@@ -18,6 +18,7 @@ public:
void create_swapchain(uint32_t width, uint32_t height);
void destroy_swapchain() const;
void resize_swapchain(struct SDL_Window *window);
void resize_render_targets(VkExtent2D renderExtent);
VkSwapchainKHR swapchain() const { return _swapchain; }
VkFormat swapchainImageFormat() const { return _swapchainImageFormat; }
@@ -35,6 +36,8 @@ public:
AllocatedImage gBufferExtra() const { return _gBufferExtra; }
AllocatedImage idBuffer() const { return _idBuffer; }
VkExtent2D windowExtent() const { return _windowExtent; }
VkExtent2D renderExtent() const { return _renderExtent; }
void set_render_extent(VkExtent2D extent) { _renderExtent = extent; }
bool resize_requested{false};
@@ -46,6 +49,7 @@ private:
VkFormat _swapchainImageFormat = {};
VkExtent2D _swapchainExtent = {};
VkExtent2D _windowExtent{kRenderWidth, kRenderHeight};
VkExtent2D _renderExtent{kRenderWidth, kRenderHeight};
std::vector<VkImage> _swapchainImages;
std::vector<VkImageView> _swapchainImageViews;

View File

@@ -30,6 +30,7 @@
#include <filesystem>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <glm/gtx/transform.hpp>
#include "config.h"
@@ -58,6 +59,28 @@ void vk_engine_draw_debug_ui(VulkanEngine *eng);
VulkanEngine *loadedEngine = nullptr;
static VkExtent2D clamp_nonzero_extent(VkExtent2D extent)
{
if (extent.width == 0) extent.width = 1;
if (extent.height == 0) extent.height = 1;
return extent;
}
static VkExtent2D scaled_extent(VkExtent2D logicalExtent, float scale)
{
logicalExtent = clamp_nonzero_extent(logicalExtent);
if (!std::isfinite(scale)) scale = 1.0f;
scale = std::clamp(scale, 0.1f, 4.0f);
const float fw = static_cast<float>(logicalExtent.width) * scale;
const float fh = static_cast<float>(logicalExtent.height) * scale;
VkExtent2D out{};
out.width = static_cast<uint32_t>(std::max(1.0f, std::floor(fw)));
out.height = static_cast<uint32_t>(std::max(1.0f, std::floor(fh)));
return out;
}
static bool file_exists_nothrow(const std::string &path)
{
if (path.empty()) return false;
@@ -146,9 +169,11 @@ void VulkanEngine::init()
// We initialize SDL and create a window with it.
SDL_Init(SDL_INIT_VIDEO);
// Initialize fixed logical render resolution for the engine.
// Initialize default logical render resolution for the engine.
_logicalRenderExtent.width = kRenderWidth;
_logicalRenderExtent.height = kRenderHeight;
_logicalRenderExtent = clamp_nonzero_extent(_logicalRenderExtent);
_drawExtent = scaled_extent(_logicalRenderExtent, renderScale);
constexpr auto window_flags = static_cast<SDL_WindowFlags>(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
@@ -190,6 +215,7 @@ void VulkanEngine::init()
_context->logicalRenderExtent = _logicalRenderExtent;
_swapchainManager->init(_deviceManager.get(), _resourceManager.get());
_swapchainManager->set_render_extent(_drawExtent);
_swapchainManager->init_swapchain();
// Fill remaining context pointers now that managers exist
@@ -306,6 +332,45 @@ void VulkanEngine::init()
_isInitialized = true;
}
void VulkanEngine::set_logical_render_extent(VkExtent2D extent)
{
extent = clamp_nonzero_extent(extent);
if (_logicalRenderExtent.width == extent.width && _logicalRenderExtent.height == extent.height)
{
return;
}
_logicalRenderExtent = extent;
if (_context)
{
_context->logicalRenderExtent = _logicalRenderExtent;
}
VkExtent2D newDraw = scaled_extent(_logicalRenderExtent, renderScale);
if (_swapchainManager)
{
_swapchainManager->resize_render_targets(newDraw);
}
}
void VulkanEngine::set_render_scale(float scale)
{
if (!std::isfinite(scale)) scale = 1.0f;
scale = std::clamp(scale, 0.1f, 4.0f);
if (std::abs(renderScale - scale) < 1e-4f)
{
return;
}
renderScale = scale;
VkExtent2D newDraw = scaled_extent(_logicalRenderExtent, renderScale);
if (_swapchainManager)
{
_swapchainManager->resize_render_targets(newDraw);
}
}
void VulkanEngine::init_default_data()
{
//> default_img
@@ -758,6 +823,17 @@ void VulkanEngine::draw()
}
}
// Compute desired internal render-target extent from logical extent + render scale.
_drawExtent = scaled_extent(_logicalRenderExtent, renderScale);
if (_swapchainManager)
{
VkExtent2D current = _swapchainManager->renderExtent();
if (current.width != _drawExtent.width || current.height != _drawExtent.height)
{
_swapchainManager->resize_render_targets(_drawExtent);
}
}
uint32_t swapchainImageIndex;
VkResult e = vkAcquireNextImageKHR(_deviceManager->device(),
@@ -782,11 +858,6 @@ void VulkanEngine::draw()
VK_CHECK(e);
}
// Fixed logical render resolution (letterboxed): draw extent is derived
// from the engine's logical render size instead of the swapchain/window.
_drawExtent.width = static_cast<uint32_t>(static_cast<float>(_logicalRenderExtent.width) * renderScale);
_drawExtent.height = static_cast<uint32_t>(static_cast<float>(_logicalRenderExtent.height) * renderScale);
VK_CHECK(vkResetFences(_deviceManager->device(), 1, &get_current_frame()._renderFence));
//now that we are sure that the commands finished executing, we can safely reset the command buffer to begin recording again.

View File

@@ -203,6 +203,12 @@ public:
//run main loop
void run();
// Rendering resolution controls:
// - logicalRenderExtent controls camera aspect and picking (letterboxed view).
// - renderScale controls the internal render target pixel count (logical * scale).
void set_logical_render_extent(VkExtent2D extent);
void set_render_scale(float scale);
// Query a conservative streaming texture budget for the texture cache.
size_t query_texture_budget_bytes() const;

View File

@@ -51,7 +51,57 @@ namespace
ImGui::InputFloat4("data4", reinterpret_cast<float *>(&selected.data.data4));
ImGui::Separator();
ImGui::SliderFloat("Render Scale", &eng->renderScale, 0.3f, 1.f);
ImGui::TextUnformatted("Render Resolution");
static int pendingLogicalW = 0;
static int pendingLogicalH = 0;
if (pendingLogicalW <= 0 || pendingLogicalH <= 0)
{
pendingLogicalW = static_cast<int>(eng->_logicalRenderExtent.width);
pendingLogicalH = static_cast<int>(eng->_logicalRenderExtent.height);
}
ImGui::InputInt("Logical Width", &pendingLogicalW);
ImGui::InputInt("Logical Height", &pendingLogicalH);
if (ImGui::Button("Apply Logical Resolution"))
{
uint32_t w = static_cast<uint32_t>(pendingLogicalW > 0 ? pendingLogicalW : 1);
uint32_t h = static_cast<uint32_t>(pendingLogicalH > 0 ? pendingLogicalH : 1);
eng->set_logical_render_extent(VkExtent2D{w, h});
}
ImGui::SameLine();
if (ImGui::Button("720p"))
{
pendingLogicalW = 1280;
pendingLogicalH = 720;
}
ImGui::SameLine();
if (ImGui::Button("1080p"))
{
pendingLogicalW = 1920;
pendingLogicalH = 1080;
}
ImGui::SameLine();
if (ImGui::Button("1440p"))
{
pendingLogicalW = 2560;
pendingLogicalH = 1440;
}
static float pendingScale = 1.0f;
if (!ImGui::IsAnyItemActive())
{
pendingScale = eng->renderScale;
}
bool scaleChanged = ImGui::SliderFloat("Render Scale", &pendingScale, 0.25f, 2.0f);
bool applyScale = scaleChanged && ImGui::IsItemDeactivatedAfterEdit();
ImGui::SameLine();
applyScale = ImGui::Button("Apply Scale") || applyScale;
if (applyScale)
{
eng->set_render_scale(pendingScale);
}
}
// IBL test grid spawner (spheres varying metallic/roughness)

View File

@@ -45,22 +45,6 @@ void LightingPass::init(EngineContext *context)
nullptr, VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
}
// Allocate and write GBuffer descriptor set
_gBufferInputDescriptorSet = _context->getDescriptors()->allocate(
_context->getDevice()->device(), _gBufferInputDescriptorLayout);
{
DescriptorWriter writer;
writer.write_image(0, _context->getSwapchain()->gBufferPosition().imageView, _context->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
writer.write_image(1, _context->getSwapchain()->gBufferNormal().imageView, _context->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
writer.write_image(2, _context->getSwapchain()->gBufferAlbedo().imageView, _context->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
writer.write_image(3, _context->getSwapchain()->gBufferExtra().imageView, _context->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
writer.update_set(_context->getDevice()->device(), _gBufferInputDescriptorSet);
}
// Shadow map descriptor layout (set = 2, updated per-frame). Use array of cascades
{
DescriptorLayoutBuilder builder;
@@ -164,9 +148,9 @@ void LightingPass::register_graph(RenderGraph *graph,
builder.write_color(drawHandle);
},
[this, drawHandle, shadowCascades](VkCommandBuffer cmd, const RGPassResources &res, EngineContext *ctx)
[this, drawHandle, gbufferPosition, gbufferNormal, gbufferAlbedo, gbufferExtra, shadowCascades](VkCommandBuffer cmd, const RGPassResources &res, EngineContext *ctx)
{
draw_lighting(cmd, ctx, res, drawHandle, shadowCascades);
draw_lighting(cmd, ctx, res, drawHandle, gbufferPosition, gbufferNormal, gbufferAlbedo, gbufferExtra, shadowCascades);
});
}
@@ -174,6 +158,10 @@ void LightingPass::draw_lighting(VkCommandBuffer cmd,
EngineContext *context,
const RGPassResources &resources,
RGImageHandle drawHandle,
RGImageHandle gbufferPosition,
RGImageHandle gbufferNormal,
RGImageHandle gbufferAlbedo,
RGImageHandle gbufferExtra,
std::span<RGImageHandle> shadowCascades)
{
EngineContext *ctxLocal = context ? context : _context;
@@ -188,6 +176,15 @@ void LightingPass::draw_lighting(VkCommandBuffer cmd,
VkImageView drawView = resources.image_view(drawHandle);
if (drawView == VK_NULL_HANDLE) return;
VkImageView posView = resources.image_view(gbufferPosition);
VkImageView nrmView = resources.image_view(gbufferNormal);
VkImageView albView = resources.image_view(gbufferAlbedo);
VkImageView extView = resources.image_view(gbufferExtra);
if (posView == VK_NULL_HANDLE || nrmView == VK_NULL_HANDLE || albView == VK_NULL_HANDLE || extView == VK_NULL_HANDLE)
{
return;
}
// Choose RT only if TLAS is valid; otherwise fall back to non-RT.
const bool haveRTFeatures = ctxLocal->getDevice()->supportsAccelerationStructure();
const VkAccelerationStructureKHR tlas = (ctxLocal->ray ? ctxLocal->ray->tlas() : VK_NULL_HANDLE);
@@ -235,11 +232,27 @@ void LightingPass::draw_lighting(VkCommandBuffer cmd,
}
writer.update_set(deviceManager->device(), globalDescriptor);
// Allocate and write GBuffer descriptor set for this frame (set = 1).
VkDescriptorSet gbufferSet = ctxLocal->currentFrame->_frameDescriptors.allocate(
deviceManager->device(), _gBufferInputDescriptorLayout);
{
DescriptorWriter gbw;
gbw.write_image(0, posView, ctxLocal->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
gbw.write_image(1, nrmView, ctxLocal->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
gbw.write_image(2, albView, ctxLocal->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
gbw.write_image(3, extView, ctxLocal->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
gbw.update_set(deviceManager->device(), gbufferSet);
}
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipeline);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 0, 1, &globalDescriptor, 0,
nullptr);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 1, 1,
&_gBufferInputDescriptorSet, 0, nullptr);
&gbufferSet, 0, nullptr);
// Allocate and write shadow descriptor set for this frame (set = 2).
// When RT is enabled, TLAS is bound in the global set at (set=0, binding=1)

View File

@@ -27,7 +27,6 @@ private:
EngineContext *_context = nullptr;
VkDescriptorSetLayout _gBufferInputDescriptorLayout = VK_NULL_HANDLE;
VkDescriptorSet _gBufferInputDescriptorSet = VK_NULL_HANDLE;
VkDescriptorSetLayout _shadowDescriptorLayout = VK_NULL_HANDLE; // set=2 (array)
// Fallbacks if IBL is not loaded
AllocatedImage _fallbackIbl2D{}; // 1x1 black
@@ -41,6 +40,10 @@ private:
EngineContext *context,
const class RGPassResources &resources,
RGImageHandle drawHandle,
RGImageHandle gbufferPosition,
RGImageHandle gbufferNormal,
RGImageHandle gbufferAlbedo,
RGImageHandle gbufferExtra,
std::span<RGImageHandle> shadowCascades);
DeletionQueue _deletionQueue;