From d2b878c077250ba2f35093308da0801dc3b68668 Mon Sep 17 00:00:00 2001 From: atdunbg Date: Wed, 23 Sep 2026 19:25:32 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20Cubemap=20=E6=8B=86=E6=8E=89=2017?= =?UTF-8?q?=20=E5=8F=82=E8=B6=85=E7=BA=A7=E6=9E=84=E9=80=A0=E2=80=94?= =?UTF-8?q?=E2=80=94=E8=B5=84=E6=BA=90=E4=B8=89=E5=85=83=E7=BB=84=20EnvTex?= =?UTF-8?q?ture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - image/view/显存分配收口为 EnvTexture 单元(RHI 惯例:三者同生共死), Cubemap = 4 个 EnvTexture + 2 采样器的组合,构造 17 参 → 8 参 - UploadCube/UploadTexture2D 直接返回完整资源单元(view 创建收进 helper),工厂函数只做"生成数据 → 上传 → 组装" - 析构循环成对释放,杜绝半套泄漏;删除无调用者的 Get()/GetFaceSize()/ GetPrefilteredMipCount()(shader mip 上限以常量契约) --- Engine/include/Engine/Renderer/Cubemap.h | 74 ++++------- Engine/src/Renderer/Cubemap.cpp | 156 ++++++++++------------- 2 files changed, 92 insertions(+), 138 deletions(-) diff --git a/Engine/include/Engine/Renderer/Cubemap.h b/Engine/include/Engine/Renderer/Cubemap.h index 241a462..1af705f 100644 --- a/Engine/include/Engine/Renderer/Cubemap.h +++ b/Engine/include/Engine/Renderer/Cubemap.h @@ -26,6 +26,15 @@ namespace Engine { class Device; + // GPU 纹理资源三元组(RHI 惯例):image/view/显存分配同生共死, + // 析构统一释放——杜绝"建了 image 丢了 view"类半套泄漏。 + // 上传 helper 直接以此为单位产出,消费端只拿引用计数语义的整单元 + struct EnvTexture { + vk::Image image{}; + vk::ImageView view{}; + MemoryAllocator::Allocation allocation; + }; + class Cubemap { public: // 从等距柱状 HDR 生成(CPU 重采样 + irradiance/prefilter/BRDF 预积分) @@ -45,68 +54,35 @@ namespace Engine { Cubemap(const Cubemap&) = delete; Cubemap& operator=(const Cubemap&) = delete; - // ---- 环境 cube(Skybox 背景)---- - vk::Image Get() const { return m_EnvImage; } - vk::ImageView GetView() const { return m_EnvView; } + // ---- 消费端契约(SkyboxPass:背景;SceneGpu:PBR ambient)---- + vk::ImageView GetView() const { return m_Env.view; } // 环境 cube + vk::ImageView GetIrradianceView() const { return m_Irr.view; } // 漫反射 + vk::ImageView GetPrefilteredView() const { return m_Pref.view; } // 镜面 + vk::ImageView GetBrdfLutView() const { return m_Lut.view; } // LUT - // ---- 漫反射辐照度 cube(PBR ambient diffuse 项)---- - vk::ImageView GetIrradianceView() const { return m_IrrView; } - - // ---- pre-filtered cube(PBR ambient specular 项,按 mip 分 roughness)---- - vk::ImageView GetPrefilteredView() const { return m_PrefView; } - u32 GetPrefilteredMipCount() const { return m_PrefMipLevels; } - - // ---- BRDF LUT(envBRDF 查表,vec2(NdotV, roughness) → F 缩放/偏置)---- - vk::ImageView GetBrdfLutView() const { return m_LutView; } + // cube 资源共享采样器(maxLod 覆盖 pre-filter mip 链) + vk::Sampler GetSampler() const { return m_EnvSampler; } // LUT 专用采样器(clamp-to-edge):共享的 border 白采样器会在 // uv=1.0 处混入 50% 白色 border,导致高 roughness 镜面偏大 vk::Sampler GetBrdfLutSampler() const { return m_LutSampler; } - vk::Sampler GetSampler() const { return m_Sampler; } - u32 GetFaceSize() const { return m_FaceSize; } - private: Cubemap(Device& device, MemoryAllocator& allocator, - vk::Image envImage, vk::ImageView envView, - vk::Image irrImage, vk::ImageView irrView, - vk::Image prefImage, vk::ImageView prefView, u32 prefMips, - vk::Image lutImage, vk::ImageView lutView, - vk::Sampler sampler, vk::Sampler lutSampler, - MemoryAllocator::Allocation envAlloc, - MemoryAllocator::Allocation irrAlloc, - MemoryAllocator::Allocation prefAlloc, - MemoryAllocator::Allocation lutAlloc, u32 faceSize) + EnvTexture env, EnvTexture irr, EnvTexture pref, + EnvTexture lut, vk::Sampler envSampler, vk::Sampler lutSampler) : m_Device(device), m_Allocator(allocator), - m_EnvImage(envImage), m_EnvView(envView), - m_IrrImage(irrImage), m_IrrView(irrView), - m_PrefImage(prefImage), m_PrefView(prefView), - m_PrefMipLevels(prefMips), - m_LutImage(lutImage), m_LutView(lutView), - m_Sampler(sampler), m_LutSampler(lutSampler), - m_EnvAllocation(envAlloc), m_IrrAllocation(irrAlloc), - m_PrefAllocation(prefAlloc), m_LutAllocation(lutAlloc), - m_FaceSize(faceSize) {} + m_Env(env), m_Irr(irr), m_Pref(pref), m_Lut(lut), + m_EnvSampler(envSampler), m_LutSampler(lutSampler) {} Device& m_Device; MemoryAllocator& m_Allocator; - vk::Image m_EnvImage; - vk::ImageView m_EnvView; - vk::Image m_IrrImage; - vk::ImageView m_IrrView; - vk::Image m_PrefImage; - vk::ImageView m_PrefView; - u32 m_PrefMipLevels; - vk::Image m_LutImage; - vk::ImageView m_LutView; - vk::Sampler m_Sampler; + EnvTexture m_Env; // 环境 cube(Skybox 背景) + EnvTexture m_Irr; // 漫反射辐照度 cube + EnvTexture m_Pref; // pre-filtered cube(mip 分 roughness) + EnvTexture m_Lut; // BRDF 积分 LUT(2D) + vk::Sampler m_EnvSampler; vk::Sampler m_LutSampler; - - MemoryAllocator::Allocation m_EnvAllocation; - MemoryAllocator::Allocation m_IrrAllocation; - MemoryAllocator::Allocation m_PrefAllocation; - MemoryAllocator::Allocation m_LutAllocation; - u32 m_FaceSize; }; } // namespace Engine diff --git a/Engine/src/Renderer/Cubemap.cpp b/Engine/src/Renderer/Cubemap.cpp index d5de00d..cbc42ba 100644 --- a/Engine/src/Renderer/Cubemap.cpp +++ b/Engine/src/Renderer/Cubemap.cpp @@ -247,17 +247,39 @@ namespace Engine // ---- 上传 ---- + vk::ImageView CreateCubeView(Device& device, vk::Image image, u32 mips) + { + return device.GetDevice().createImageView( + vk::ImageViewCreateInfo{} + .setImage(image) + .setViewType(vk::ImageViewType::eCube) + .setFormat(vk::Format::eR16G16B16A16Sfloat) + .setSubresourceRange({vk::ImageAspectFlagBits::eColor, + 0, mips, 0, kFaceCount})); + } + + vk::ImageView CreateLutView(Device& device, vk::Image image) + { + return device.GetDevice().createImageView( + vk::ImageViewCreateInfo{} + .setImage(image) + .setViewType(vk::ImageViewType::e2D) + .setFormat(vk::Format::eR16G16Sfloat) + .setSubresourceRange({vk::ImageAspectFlagBits::eColor, + 0, 1, 0, 1})); + } + // cube mip 链数据:[mip0: 6 面][mip1: 6 面]...,每面 faceSize²×8B struct CubeMipData { const u16* data; // 6 面连续 u32 faceSize; }; - // 上传 cube(含 mip 链)为 eCubeCompatible image(fp16) - MemoryAllocator::Allocation UploadCube(Device& device, - MemoryAllocator& allocator, - const std::vector& mips, - vk::Image& outImage) + // 上传 cube(含 mip 链)为 eCubeCompatible image(fp16), + // 返回完整资源单元(image + view + 显存分配) + EnvTexture UploadCube(Device& device, MemoryAllocator& allocator, + const std::vector& mips, + u32 viewMipCount) { const u32 mipCount = static_cast(mips.size()); @@ -303,7 +325,7 @@ namespace Engine auto imageAlloc = allocator.CreateImage(imageInfo, vk::MemoryPropertyFlagBits::eDeviceLocal); - outImage = imageAlloc.Image; + const vk::Image image = imageAlloc.Image; vk::CommandPoolCreateInfo poolInfo; poolInfo.setQueueFamilyIndex(device.GetGraphicsFamily()) @@ -327,7 +349,7 @@ namespace Engine .setDstAccessMask(vk::AccessFlagBits2::eTransferWrite) .setOldLayout(vk::ImageLayout::eUndefined) .setNewLayout(vk::ImageLayout::eTransferDstOptimal) - .setImage(outImage) + .setImage(image) .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, mipCount, 0, kFaceCount}); cmd.pipelineBarrier2( @@ -350,7 +372,7 @@ namespace Engine mip, f, 1}) .setImageOffset({0, 0, 0}) .setImageExtent({dim, dim, 1}); - cmd.copyBufferToImage(stagingAlloc.Buffer, outImage, + cmd.copyBufferToImage(stagingAlloc.Buffer, image, vk::ImageLayout::eTransferDstOptimal, region); } @@ -365,7 +387,7 @@ namespace Engine .setDstAccessMask(vk::AccessFlagBits2::eShaderRead) .setOldLayout(vk::ImageLayout::eTransferDstOptimal) .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal) - .setImage(outImage) + .setImage(image) .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, mipCount, 0, kFaceCount}); cmd.pipelineBarrier2( @@ -385,16 +407,16 @@ namespace Engine device.GetDevice().destroyCommandPool(pool); allocator.Destroy(stagingAlloc); - return imageAlloc; + return {imageAlloc.Image, + CreateCubeView(device, imageAlloc.Image, viewMipCount), + imageAlloc}; } - // 上传 BRDF LUT(2D,R16G16Sfloat,8B/px) - MemoryAllocator::Allocation UploadTexture2D(Device& device, - MemoryAllocator& allocator, - const std::vector& texels, - u32 size, - vk::Format format, - vk::Image& outImage) + // 上传 BRDF LUT(2D,R16G16Sfloat,8B/px),返回完整资源单元 + EnvTexture UploadTexture2D(Device& device, + MemoryAllocator& allocator, + const std::vector& texels, + u32 size, vk::Format format) { const vk::DeviceSize total = static_cast(size) * size * 8; @@ -422,7 +444,7 @@ namespace Engine auto imageAlloc = allocator.CreateImage(imageInfo, vk::MemoryPropertyFlagBits::eDeviceLocal); - outImage = imageAlloc.Image; + const vk::Image image = imageAlloc.Image; vk::CommandPoolCreateInfo poolInfo; poolInfo.setQueueFamilyIndex(device.GetGraphicsFamily()) @@ -445,7 +467,7 @@ namespace Engine .setDstAccessMask(vk::AccessFlagBits2::eTransferWrite) .setOldLayout(vk::ImageLayout::eUndefined) .setNewLayout(vk::ImageLayout::eTransferDstOptimal) - .setImage(outImage) + .setImage(image) .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); cmd.pipelineBarrier2( @@ -459,7 +481,7 @@ namespace Engine 0, 0, 1}) .setImageOffset({0, 0, 0}) .setImageExtent({size, size, 1}); - cmd.copyBufferToImage(stagingAlloc.Buffer, outImage, + cmd.copyBufferToImage(stagingAlloc.Buffer, image, vk::ImageLayout::eTransferDstOptimal, region); vk::ImageMemoryBarrier2 toShader; @@ -469,7 +491,7 @@ namespace Engine .setDstAccessMask(vk::AccessFlagBits2::eShaderRead) .setOldLayout(vk::ImageLayout::eTransferDstOptimal) .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal) - .setImage(outImage) + .setImage(image) .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); cmd.pipelineBarrier2( @@ -489,29 +511,8 @@ namespace Engine device.GetDevice().destroyCommandPool(pool); allocator.Destroy(stagingAlloc); - return imageAlloc; - } - - vk::ImageView CreateCubeView(Device& device, vk::Image image, u32 mips) - { - return device.GetDevice().createImageView( - vk::ImageViewCreateInfo{} - .setImage(image) - .setViewType(vk::ImageViewType::eCube) - .setFormat(vk::Format::eR16G16B16A16Sfloat) - .setSubresourceRange({vk::ImageAspectFlagBits::eColor, - 0, mips, 0, kFaceCount})); - } - - vk::ImageView CreateLutView(Device& device, vk::Image image) - { - return device.GetDevice().createImageView( - vk::ImageViewCreateInfo{} - .setImage(image) - .setViewType(vk::ImageViewType::e2D) - .setFormat(vk::Format::eR16G16Sfloat) - .setSubresourceRange({vk::ImageAspectFlagBits::eColor, - 0, 1, 0, 1})); + return {imageAlloc.Image, + CreateLutView(device, imageAlloc.Image), imageAlloc}; } // fp16 NaN 自检(指数全 1 且尾数非 0):预积分数据上传前的 @@ -676,26 +677,20 @@ namespace Engine return nullptr; } - vk::Image envImage, irrImage, prefImage, lutImage; - auto envAlloc = UploadCube(device, allocator, - {{envFaces.data(), faceSize}}, envImage); - auto irrAlloc = UploadCube(device, allocator, - {{irrFaces.data(), irrSize}}, irrImage); - auto prefAlloc = UploadCube(device, allocator, prefMips, prefImage); - auto lutAlloc = UploadTexture2D(device, allocator, lutTexels, - kBrdfLutSize, - vk::Format::eR16G16Sfloat, lutImage); + EnvTexture env = UploadCube(device, allocator, + {{envFaces.data(), faceSize}}, 1); + EnvTexture irr = UploadCube(device, allocator, + {{irrFaces.data(), irrSize}}, 1); + EnvTexture pref = UploadCube(device, allocator, prefMips, + kPrefilterMips); + EnvTexture lut = UploadTexture2D(device, allocator, lutTexels, + kBrdfLutSize, + vk::Format::eR16G16Sfloat); envFaces.clear(); envFaces.shrink_to_fit(); irrFaces.clear(); irrFaces.shrink_to_fit(); prefFaces.clear(); prefFaces.shrink_to_fit(); - vk::ImageView envView = CreateCubeView(device, envImage, 1); - vk::ImageView irrView = CreateCubeView(device, irrImage, 1); - vk::ImageView prefView = CreateCubeView(device, prefImage, - kPrefilterMips); - vk::ImageView lutView = CreateLutView(device, lutImage); - // cube sampler 共享:maxLod 覆盖 pre-filter mip 链(textureLod 显式 // 采样会被 clamp 到 maxLod,必须 ≥ mipCount-1) vk::Sampler sampler = CreateEnvSampler(device, kPrefilterMips - 1); @@ -707,10 +702,7 @@ namespace Engine kPrefilterMips, kBrdfLutSize); return Scope(new Cubemap(device, allocator, - envImage, envView, irrImage, irrView, - prefImage, prefView, kPrefilterMips, - lutImage, lutView, sampler, lutSampler, - envAlloc, irrAlloc, prefAlloc, lutAlloc, faceSize)); + env, irr, pref, lut, sampler, lutSampler)); } Scope Cubemap::CreateNeutral(Device& device, @@ -742,46 +734,32 @@ namespace Engine } } - vk::Image envImage, irrImage, prefImage, lutImage; - auto envAlloc = UploadCube(device, allocator, {{faces.data(), 1}}, envImage); - auto irrAlloc = UploadCube(device, allocator, {{faces.data(), 1}}, irrImage); - auto prefAlloc = UploadCube(device, allocator, prefMips, prefImage); - auto lutAlloc = UploadTexture2D(device, allocator, lutTexels, - kBrdfLutSize, - vk::Format::eR16G16Sfloat, lutImage); - - vk::ImageView envView = CreateCubeView(device, envImage, 1); - vk::ImageView irrView = CreateCubeView(device, irrImage, 1); - vk::ImageView prefView = CreateCubeView(device, prefImage, 1); - vk::ImageView lutView = CreateLutView(device, lutImage); + EnvTexture env = UploadCube(device, allocator, {{faces.data(), 1}}, 1); + EnvTexture irr = UploadCube(device, allocator, {{faces.data(), 1}}, 1); + EnvTexture pref = UploadCube(device, allocator, prefMips, 1); + EnvTexture lut = UploadTexture2D(device, allocator, lutTexels, + kBrdfLutSize, vk::Format::eR16G16Sfloat); vk::Sampler sampler = CreateEnvSampler(device, kPrefilterMips - 1); vk::Sampler lutSampler = CreateLutSampler(device); ENGINE_INFO("Neutral IBL environment (constant fallback, real BRDF LUT)"); return Scope(new Cubemap(device, allocator, - envImage, envView, irrImage, irrView, - prefImage, prefView, kPrefilterMips, - lutImage, lutView, sampler, lutSampler, - envAlloc, irrAlloc, prefAlloc, lutAlloc, 1)); + env, irr, pref, lut, sampler, lutSampler)); } Cubemap::~Cubemap() { const auto& vkDevice = m_Device.GetDevice(); - vkDevice.destroySampler(m_Sampler); + vkDevice.destroySampler(m_EnvSampler); vkDevice.destroySampler(m_LutSampler); - vkDevice.destroyImageView(m_EnvView); - vkDevice.destroyImageView(m_IrrView); - vkDevice.destroyImageView(m_PrefView); - vkDevice.destroyImageView(m_LutView); - // image 资源与显存经 VMA 成对释放 - m_Allocator.Destroy(m_EnvAllocation); - m_Allocator.Destroy(m_IrrAllocation); - m_Allocator.Destroy(m_PrefAllocation); - m_Allocator.Destroy(m_LutAllocation); + // 三元组成对释放:view → (image + 显存) 经 VMA 成对销毁 + for (EnvTexture* tex : {&m_Env, &m_Irr, &m_Pref, &m_Lut}) { + vkDevice.destroyImageView(tex->view); + m_Allocator.Destroy(tex->allocation); + } } } // namespace Engine