Work on character rendering and frustrum culling etc

This commit is contained in:
Kelsi 2026-02-22 05:58:45 -08:00
parent fc5294eb0f
commit 7dd1dada5f
16 changed files with 559 additions and 138 deletions

View file

@ -9,9 +9,10 @@ VkRenderTarget::~VkRenderTarget() {
// Must call destroy() explicitly with device/allocator before destruction
}
bool VkRenderTarget::create(VkContext& ctx, uint32_t width, uint32_t height, VkFormat format) {
bool VkRenderTarget::create(VkContext& ctx, uint32_t width, uint32_t height, VkFormat format, bool withDepth) {
VkDevice device = ctx.getDevice();
VmaAllocator allocator = ctx.getAllocator();
hasDepth_ = withDepth;
// Create color image (COLOR_ATTACHMENT + SAMPLED for reading in subsequent passes)
colorImage_ = createImage(device, allocator, width, height, format,
@ -22,6 +23,17 @@ bool VkRenderTarget::create(VkContext& ctx, uint32_t width, uint32_t height, VkF
return false;
}
// Create depth image if requested
if (withDepth) {
depthImage_ = createImage(device, allocator, width, height,
VK_FORMAT_D32_SFLOAT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
if (!depthImage_.image) {
LOG_ERROR("VkRenderTarget: failed to create depth image (", width, "x", height, ")");
destroy(device, allocator);
return false;
}
}
// Create sampler (linear filtering, clamp to edge)
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
@ -41,44 +53,77 @@ bool VkRenderTarget::create(VkContext& ctx, uint32_t width, uint32_t height, VkF
}
// Create render pass
// Color attachment: UNDEFINED -> COLOR_ATTACHMENT_OPTIMAL (during pass)
// -> SHADER_READ_ONLY_OPTIMAL (final layout, ready for sampling)
VkAttachmentDescription colorAttachment{};
colorAttachment.format = format;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkAttachmentDescription attachments[2]{};
// Color attachment: UNDEFINED -> COLOR_ATTACHMENT_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL
attachments[0].format = format;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Depth attachment (only used when withDepth)
attachments[1].format = VK_FORMAT_D32_SFLOAT;
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference colorRef{};
colorRef.attachment = 0;
colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference depthRef{};
depthRef.attachment = 1;
depthRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorRef;
if (withDepth) subpass.pDepthStencilAttachment = &depthRef;
// Dependency: external -> subpass 0 (wait for previous reads to finish)
VkSubpassDependency dependency{};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
// Dependencies
VkSubpassDependency dependencies[2]{};
uint32_t depCount = 1;
// Input dependency: wait for previous fragment shader reads before writing
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
if (withDepth) {
dependencies[0].dstStageMask |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependencies[0].dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
// Output dependency (depth targets only): ensure writes complete before fragment reads
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
depCount = 2;
}
VkRenderPassCreateInfo rpInfo{};
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
rpInfo.attachmentCount = 1;
rpInfo.pAttachments = &colorAttachment;
rpInfo.attachmentCount = withDepth ? 2u : 1u;
rpInfo.pAttachments = attachments;
rpInfo.subpassCount = 1;
rpInfo.pSubpasses = &subpass;
rpInfo.dependencyCount = 1;
rpInfo.pDependencies = &dependency;
rpInfo.dependencyCount = depCount;
rpInfo.pDependencies = dependencies;
if (vkCreateRenderPass(device, &rpInfo, nullptr, &renderPass_) != VK_SUCCESS) {
LOG_ERROR("VkRenderTarget: failed to create render pass");
@ -87,11 +132,12 @@ bool VkRenderTarget::create(VkContext& ctx, uint32_t width, uint32_t height, VkF
}
// Create framebuffer
VkImageView fbAttachments[2] = { colorImage_.imageView, depthImage_.imageView };
VkFramebufferCreateInfo fbInfo{};
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = renderPass_;
fbInfo.attachmentCount = 1;
fbInfo.pAttachments = &colorImage_.imageView;
fbInfo.attachmentCount = withDepth ? 2u : 1u;
fbInfo.pAttachments = fbAttachments;
fbInfo.width = width;
fbInfo.height = height;
fbInfo.layers = 1;
@ -102,7 +148,7 @@ bool VkRenderTarget::create(VkContext& ctx, uint32_t width, uint32_t height, VkF
return false;
}
LOG_INFO("VkRenderTarget created (", width, "x", height, ")");
LOG_INFO("VkRenderTarget created (", width, "x", height, withDepth ? ", with depth)" : ")");
return true;
}
@ -119,7 +165,9 @@ void VkRenderTarget::destroy(VkDevice device, VmaAllocator allocator) {
vkDestroySampler(device, sampler_, nullptr);
sampler_ = VK_NULL_HANDLE;
}
destroyImage(device, allocator, depthImage_);
destroyImage(device, allocator, colorImage_);
hasDepth_ = false;
}
void VkRenderTarget::beginPass(VkCommandBuffer cmd, const VkClearColorValue& clear) {
@ -130,10 +178,11 @@ void VkRenderTarget::beginPass(VkCommandBuffer cmd, const VkClearColorValue& cle
rpBegin.renderArea.offset = {0, 0};
rpBegin.renderArea.extent = getExtent();
VkClearValue clearValue{};
clearValue.color = clear;
rpBegin.clearValueCount = 1;
rpBegin.pClearValues = &clearValue;
VkClearValue clearValues[2]{};
clearValues[0].color = clear;
clearValues[1].depthStencil = {1.0f, 0};
rpBegin.clearValueCount = hasDepth_ ? 2u : 1u;
rpBegin.pClearValues = clearValues;
vkCmdBeginRenderPass(cmd, &rpBegin, VK_SUBPASS_CONTENTS_INLINE);