aboutsummaryrefslogtreecommitdiff
path: root/src/platform/vulkan/vulkan_cubemap.cpp
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-22 06:57:16 +0200
committerhachem <im@hachem.wtf>2026-09-22 06:57:16 +0200
commitd7c1ced08d5d78fcb239550404db4e67d4357a19 (patch)
tree9c0e218b096d138075cbf6a48e2562d33a8f94ed /src/platform/vulkan/vulkan_cubemap.cpp
parent270eda6f558c972ec86ad0580f1d3d18afc83a94 (diff)
fix: windows supportHEADmain
Diffstat (limited to 'src/platform/vulkan/vulkan_cubemap.cpp')
-rw-r--r--src/platform/vulkan/vulkan_cubemap.cpp33
1 files changed, 25 insertions, 8 deletions
diff --git a/src/platform/vulkan/vulkan_cubemap.cpp b/src/platform/vulkan/vulkan_cubemap.cpp
index 3ffc496..de0116e 100644
--- a/src/platform/vulkan/vulkan_cubemap.cpp
+++ b/src/platform/vulkan/vulkan_cubemap.cpp
@@ -2,6 +2,7 @@
#include "stb_image.h"
#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/packing.hpp>
namespace Donut::RHI
{
@@ -36,10 +37,22 @@ namespace Donut::RHI
csm.addressModeU = csm.addressModeV = csm.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
vkCreateSampler(m_device, &csm, nullptr, &tex->m_sampler);
+ // the capture shaders come first: if they're missing (no generated/ dir)
+ // take the same dark road as a missing hdr, rather than handing the driver
+ // two garbage module handles.
+ VkShaderModule vmod = VK_NULL_HANDLE, fmod = VK_NULL_HANDLE;
+ const bool shaders_ok = create_shader_module("assets/shaders/generated/equirect_to_cubemap.vertexMain.spv", vmod)
+ && create_shader_module("assets/shaders/generated/equirect_to_cubemap.fragmentMain.spv", fmod);
+ // bottom row first, same as the GL loader: SampleSphericalMap sends "up" to
+ // v = 1, so the top of the panorama has to be the last row. without this the
+ // whole sky is upside down (the old projection flip was half-hiding that).
int w = 0, h = 0, ch = 0;
- float* pixels = stbi_loadf(path.c_str(), &w, &h, &ch, 4);
+ stbi_set_flip_vertically_on_load(true);
+ float* pixels = shaders_ok ? stbi_loadf(path.c_str(), &w, &h, &ch, 4) : nullptr;
if (!pixels)
{
+ if (vmod) vkDestroyShaderModule(m_device, vmod, nullptr);
+ if (fmod) vkDestroyShaderModule(m_device, fmod, nullptr);
DONUT_WARN("Vulkan RHI: HDRI '{}' could not be loaded; using a dark background", path);
VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
cbai.commandPool = m_command_pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
@@ -65,13 +78,14 @@ namespace Donut::RHI
}
const VkFormat eq_fmt = VK_FORMAT_R16G16B16A16_SFLOAT;
- size_t texel_count = (size_t)w * h * 4;
+ size_t texel_count = (size_t)w * (size_t)h * 4;
VkDeviceSize eq_size = (VkDeviceSize)texel_count * sizeof(uint16_t);
VkBuffer eq_staging; VkDeviceMemory eq_staging_mem;
create_buffer_raw(eq_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, host_vis, eq_staging, eq_staging_mem);
void* mp = nullptr; vkMapMemory(m_device, eq_staging_mem, 0, eq_size, 0, &mp);
uint16_t* dst = (uint16_t*)mp;
- for (size_t i = 0; i < texel_count; ++i) { __fp16 hf = (__fp16)pixels[i]; std::memcpy(&dst[i], &hf, sizeof(uint16_t)); }
+ // glm's half packing rather than __fp16: that one is a clang/ARM extension and MSVC has never heard of it
+ for (size_t i = 0; i < texel_count; ++i) dst[i] = glm::packHalf1x16(pixels[i]);
vkUnmapMemory(m_device, eq_staging_mem);
stbi_image_free(pixels);
@@ -141,9 +155,6 @@ namespace Donut::RHI
VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; dpci.maxSets = 6; dpci.poolSizeCount = 2; dpci.pPoolSizes = psizes;
vkCreateDescriptorPool(m_device, &dpci, nullptr, &pool);
- VkShaderModule vmod, fmod;
- create_shader_module("assets/shaders/generated/equirect_to_cubemap.vertexMain.spv", vmod);
- create_shader_module("assets/shaders/generated/equirect_to_cubemap.fragmentMain.spv", fmod);
VkPipelineLayout playout;
VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; plci.setLayoutCount = 1; plci.pSetLayouts = &set_layout;
vkCreatePipelineLayout(m_device, &plci, nullptr, &playout);
@@ -180,8 +191,11 @@ namespace Donut::RHI
create_buffer_raw(sizeof(cube_verts), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, host_vis, cube_vb, cube_vb_mem);
vkMapMemory(m_device, cube_vb_mem, 0, sizeof(cube_verts), 0, &mp); std::memcpy(mp, cube_verts, sizeof(cube_verts)); vkUnmapMemory(m_device, cube_vb_mem);
+ // no vulkan y-flip here. rendering into a texture and sampling it back is
+ // the same in both APIs (clip-space bottom lands on row 0 either way), so
+ // these are the exact GL capture matrices. the flip that used to sit here
+ // mirrored every face top to bottom and scrambled the sky.
glm::mat4 proj = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
- proj[1][1] *= -1.0f;
glm::mat4 views[6] = {
glm::lookAt(glm::vec3(0), glm::vec3( 1, 0, 0), glm::vec3(0, -1, 0)),
glm::lookAt(glm::vec3(0), glm::vec3(-1, 0, 0), glm::vec3(0, -1, 0)),
@@ -194,7 +208,10 @@ namespace Donut::RHI
for (uint32_t i = 0; i < 6; ++i)
{
create_buffer_raw(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, ubo[i], ubo_mem[i]);
- glm::mat4 mats[2] = { glm::transpose(proj), glm::transpose(views[i]) };
+ // straight glm, no transpose: the SPIR-V marks these RowMajor and does
+ // v * M, which reads glm's column-major memory as-is (same as every
+ // other UBO here). only GL's flattened loose uniforms need the transpose.
+ glm::mat4 mats[2] = { proj, views[i] };
vkMapMemory(m_device, ubo_mem[i], 0, 128, 0, &mp); std::memcpy(mp, mats, 128); vkUnmapMemory(m_device, ubo_mem[i]);
VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; dsai.descriptorPool = pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &set_layout;
vkAllocateDescriptorSets(m_device, &dsai, &sets[i]);