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:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
// 上传 cube(含 mip 链)为 eCubeCompatible image(fp16),
|
||||
// 返回完整资源单元(image + view + 显存分配)
|
||||
EnvTexture UploadCube(Device& device, MemoryAllocator& allocator,
|
||||
const std::vector<CubeMipData>& mips,
|
||||
vk::Image& outImage)
|
||||
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,
|
||||
// 上传 BRDF LUT(2D,R16G16Sfloat,8B/px),返回完整资源单元
|
||||
EnvTexture UploadTexture2D(Device& device,
|
||||
MemoryAllocator& allocator,
|
||||
const std::vector<u16>& texels,
|
||||
u32 size,
|
||||
vk::Format format,
|
||||
vk::Image& outImage)
|
||||
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,
|
||||
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, lutImage);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user