refactor: Cubemap 拆掉 17 参超级构造——资源三元组 EnvTexture

- image/view/显存分配收口为 EnvTexture 单元(RHI 惯例:三者同生共死),
  Cubemap = 4 个 EnvTexture + 2 采样器的组合,构造 17 参 → 8 参
- UploadCube/UploadTexture2D 直接返回完整资源单元(view 创建收进
  helper),工厂函数只做"生成数据 → 上传 → 组装"
- 析构循环成对释放,杜绝半套泄漏;删除无调用者的 Get()/GetFaceSize()/
  GetPrefilteredMipCount()(shader mip 上限以常量契约)
This commit is contained in:
2026-09-23 19:25:32 +08:00
parent bd4ac6aa12
commit d2b878c077
2 changed files with 92 additions and 138 deletions
+67 -89
View File
@@ -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<CubeMipData>& mips,
vk::Image& outImage)
// 上传 cube(含 mip 链)为 eCubeCompatible image(fp16),
// 返回完整资源单元(image + view + 显存分配)
EnvTexture UploadCube(Device& device, MemoryAllocator& allocator,
const std::vector<CubeMipData>& mips,
u32 viewMipCount)
{
const u32 mipCount = static_cast<u32>(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<u16>& texels,
u32 size,
vk::Format format,
vk::Image& outImage)
// 上传 BRDF LUT(2D,R16G16Sfloat,8B/px),返回完整资源单元
EnvTexture UploadTexture2D(Device& device,
MemoryAllocator& allocator,
const std::vector<u16>& texels,
u32 size, vk::Format format)
{
const vk::DeviceSize total =
static_cast<vk::DeviceSize>(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<Cubemap>(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> 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<Cubemap>(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