mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Fix] (DirectVulkan): generate mipmaps for cube-map-array and 1D-array targets, and stop shrinking an array's layer count down the chain
This commit is contained in:
@@ -1418,6 +1418,48 @@ void main() {
|
|||||||
return {width, height, depth};
|
return {width, height, depth};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// How many components of a GL-space texel size actually halve down the mip chain. An array
|
||||||
|
// texture's LAYER count is not a dimension of the image (GL 4.6 core 8.14.3): it stays put
|
||||||
|
// all the way down, and GetMipmapTexelSize parks it in the slot after the image's own
|
||||||
|
// dimensions. This is the same split IsMipmapCompleteForFilter applies, and the two have to
|
||||||
|
// agree - allocating a chain whose layer count shrinks builds levels the completeness rule
|
||||||
|
// then rejects. Vulkan-space extents need none of this: layers live in arrayLayers there,
|
||||||
|
// so resource->depth is already 1 for every array target.
|
||||||
|
static Int MipShrinkingComponentCount(TextureTarget target) {
|
||||||
|
switch (target) {
|
||||||
|
case TextureTarget::Texture1DArray:
|
||||||
|
return 1;
|
||||||
|
case TextureTarget::Texture2DArray:
|
||||||
|
case TextureTarget::TextureCubeMapArray:
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static IntVec3 ComputeMipTexelSizeWithFixedComponents(const IntVec3& baseTexelSize, Uint32 relativeMipLevel,
|
||||||
|
Int shrinkingComponents) {
|
||||||
|
IntVec3 size = baseTexelSize;
|
||||||
|
for (Int component = 0; component < shrinkingComponents && component < 3; ++component) {
|
||||||
|
size[component] = std::max<Int>(size[component] >> static_cast<Int>(relativeMipLevel), 1);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint32 ComputeFullMipLevelCountWithFixedComponents(const IntVec3& baseTexelSize,
|
||||||
|
Int shrinkingComponents) {
|
||||||
|
Int maxDimension = 1;
|
||||||
|
for (Int component = 0; component < shrinkingComponents && component < 3; ++component) {
|
||||||
|
maxDimension = std::max<Int>(maxDimension, baseTexelSize[component]);
|
||||||
|
}
|
||||||
|
Uint32 mipLevelCount = 1;
|
||||||
|
while (maxDimension > 1) {
|
||||||
|
maxDimension = std::max<Int>(maxDimension / 2, 1);
|
||||||
|
++mipLevelCount;
|
||||||
|
}
|
||||||
|
return mipLevelCount;
|
||||||
|
}
|
||||||
|
|
||||||
static Bool EnsureGenerateMipmapStorageAllocated(::MobileGL::MG_State::GLState::TextureObjectMipmap& texture,
|
static Bool EnsureGenerateMipmapStorageAllocated(::MobileGL::MG_State::GLState::TextureObjectMipmap& texture,
|
||||||
Uint32 baseMipLevel) {
|
Uint32 baseMipLevel) {
|
||||||
const Uint32 existingMipLevelCount = static_cast<Uint32>(texture.GetMipmapLevelCount());
|
const Uint32 existingMipLevelCount = static_cast<Uint32>(texture.GetMipmapLevelCount());
|
||||||
@@ -1430,6 +1472,8 @@ void main() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Int shrinkingComponents = MipShrinkingComponentCount(texture.GetTarget());
|
||||||
|
|
||||||
for (const auto uploadTarget : uploadTargets) {
|
for (const auto uploadTarget : uploadTargets) {
|
||||||
const IntVec3 baseTexelSize = texture.GetMipmapTexelSize(uploadTarget, baseMipLevel);
|
const IntVec3 baseTexelSize = texture.GetMipmapTexelSize(uploadTarget, baseMipLevel);
|
||||||
const SizeT baseByteSize = texture.GetMipmapByteSize(uploadTarget, baseMipLevel);
|
const SizeT baseByteSize = texture.GetMipmapByteSize(uploadTarget, baseMipLevel);
|
||||||
@@ -1446,13 +1490,15 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SizeT bytesPerTexel = baseByteSize / baseTexelCount;
|
const SizeT bytesPerTexel = baseByteSize / baseTexelCount;
|
||||||
const Uint32 requiredMipLevelCount = baseMipLevel + ComputeFullMipLevelCount(baseTexelSize);
|
const Uint32 requiredMipLevelCount =
|
||||||
|
baseMipLevel + ComputeFullMipLevelCountWithFixedComponents(baseTexelSize, shrinkingComponents);
|
||||||
if (existingMipLevelCount >= requiredMipLevelCount) {
|
if (existingMipLevelCount >= requiredMipLevelCount) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Uint32 level = existingMipLevelCount; level < requiredMipLevelCount; ++level) {
|
for (Uint32 level = existingMipLevelCount; level < requiredMipLevelCount; ++level) {
|
||||||
const IntVec3 levelTexelSize = ComputeMipTexelSize(baseTexelSize, level - baseMipLevel);
|
const IntVec3 levelTexelSize = ComputeMipTexelSizeWithFixedComponents(
|
||||||
|
baseTexelSize, level - baseMipLevel, shrinkingComponents);
|
||||||
const SizeT levelByteSize = bytesPerTexel * static_cast<SizeT>(levelTexelSize.x()) *
|
const SizeT levelByteSize = bytesPerTexel * static_cast<SizeT>(levelTexelSize.x()) *
|
||||||
static_cast<SizeT>(levelTexelSize.y()) *
|
static_cast<SizeT>(levelTexelSize.y()) *
|
||||||
static_cast<SizeT>(levelTexelSize.z());
|
static_cast<SizeT>(levelTexelSize.z());
|
||||||
@@ -10495,15 +10541,23 @@ void main() {
|
|||||||
|
|
||||||
void VulkanRenderer::GenerateMipmap(GLenum target) {
|
void VulkanRenderer::GenerateMipmap(GLenum target) {
|
||||||
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||||
// The other mipmappable targets - 1D, 1D array, cube map array - are legal GL and the front
|
// Whatever is left here is a coverage gap in this backend, not a broken invariant, so it
|
||||||
// end lets them through, so reaching one here is a coverage gap in this backend, not a
|
// declines (leaving the mip chain unwritten) rather than asserting the process down. What
|
||||||
// broken invariant. Declining leaves the mip chain unwritten; asserting took the process
|
// remains is the multisample targets, which GL 4.6 core 8.14.4 forbids to glGenerateMipmap
|
||||||
// down with it.
|
// outright.
|
||||||
|
//
|
||||||
|
// Every ARRAY target - 1D array, 2D array, cube map array - needs no blit code of its own:
|
||||||
|
// its layers live in the VkImage's arrayLayers, so resource->extent/depth already describe
|
||||||
|
// one layer's image and the loop below already copies every layer per level via
|
||||||
|
// srcSubresource.layerCount = resource->arrayLayers. The one thing they DO need is that the
|
||||||
|
// GL-space storage allocation not shrink the layer count down the chain, which
|
||||||
|
// MipShrinkingComponentCount handles.
|
||||||
if (textureTarget != TextureTarget::Texture2D && textureTarget != TextureTarget::Texture2DArray &&
|
if (textureTarget != TextureTarget::Texture2D && textureTarget != TextureTarget::Texture2DArray &&
|
||||||
textureTarget != TextureTarget::Texture3D && textureTarget != TextureTarget::TextureCubeMap &&
|
textureTarget != TextureTarget::Texture3D && textureTarget != TextureTarget::TextureCubeMap &&
|
||||||
|
textureTarget != TextureTarget::TextureCubeMapArray &&
|
||||||
// A 1D texture needs nothing special: its storage extent is {width, 1, 1}, so the blit
|
// A 1D texture needs nothing special: its storage extent is {width, 1, 1}, so the blit
|
||||||
// loop below already emits the y and z offsets of 0 and 1 that a 1D image requires.
|
// loop below already emits the y and z offsets of 0 and 1 that a 1D image requires.
|
||||||
textureTarget != TextureTarget::Texture1D) {
|
textureTarget != TextureTarget::Texture1D && textureTarget != TextureTarget::Texture1DArray) {
|
||||||
MGLOG_W_ONCE("GenerateMipmap: unsupported target %s", MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
|
MGLOG_W_ONCE("GenerateMipmap: unsupported target %s", MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user