mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Fix, Test] (DirectVulkan, GLImpl): size a 1D-array texture's mip chain and layered attachment by the axis that carries its layers
This commit is contained in:
@@ -86,9 +86,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return ToStorageArrayLayer(texture, face);
|
return ToStorageArrayLayer(texture, face);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The attachment's size is GL geometry, and GL_TEXTURE_1D_ARRAY keeps its layer count in the
|
||||||
|
// state-side HEIGHT rather than in z (see ToVulkanLevelExtent, which exists for exactly this
|
||||||
|
// remap). Reading z directly gave every layered 1D-array attachment layerCount = 1, so a
|
||||||
|
// geometry shader writing gl_Layer = 1..n had its output silently dropped and the parent's
|
||||||
|
// upper layers were never written at all.
|
||||||
static Uint32 ResolveAttachmentLayerCount(const MG_State::GLState::FramebufferAttachmentObject& attachment) {
|
static Uint32 ResolveAttachmentLayerCount(const MG_State::GLState::FramebufferAttachmentObject& attachment) {
|
||||||
if (attachment.IsLayered()) {
|
if (attachment.IsLayered()) {
|
||||||
return static_cast<Uint32>(std::max(attachment.GetSize().z(), 1));
|
const auto& texture = attachment.GetTexture();
|
||||||
|
const TextureTarget target = texture != nullptr ? texture->GetTarget() : TextureTarget::Unknown;
|
||||||
|
return static_cast<Uint32>(std::max(ToVulkanLevelExtent(target, attachment.GetSize()).z(), 1));
|
||||||
}
|
}
|
||||||
return 1u;
|
return 1u;
|
||||||
}
|
}
|
||||||
@@ -1060,8 +1067,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
.key = VkClearManager::MakePendingClearKey(att)
|
.key = VkClearManager::MakePendingClearKey(att)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const IntVec2 attachmentExtent =
|
// Same remap as ResolveAttachmentLayerCount, for the same reason: a
|
||||||
ResolveRenderPassFramebufferExtent(isDefaultFbo, att.GetSize(), swapchainExtent);
|
// 1D-array attachment's GL height is its layer count, and using it as the
|
||||||
|
// framebuffer height asks for a framebuffer taller than the VK_IMAGE_TYPE_1D
|
||||||
|
// image it is built over.
|
||||||
|
const IntVec2 attachmentExtent = ResolveRenderPassFramebufferExtent(
|
||||||
|
isDefaultFbo, ToVulkanLevelExtent(texture->GetTarget(), att.GetSize()), swapchainExtent);
|
||||||
if (width == 0)
|
if (width == 0)
|
||||||
width = attachmentExtent.x();
|
width = attachmentExtent.x();
|
||||||
if (height == 0)
|
if (height == 0)
|
||||||
@@ -1211,9 +1222,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
depthAttachmentDescription.format = depthTextureResource->format;
|
depthAttachmentDescription.format = depthTextureResource->format;
|
||||||
depthAttachmentSampleCount = depthTextureResource->sampleCount;
|
depthAttachmentSampleCount = depthTextureResource->sampleCount;
|
||||||
depthAttachmentId = static_cast<Int>(texture.GetExternalIndex());
|
depthAttachmentId = static_cast<Int>(texture.GetExternalIndex());
|
||||||
attachmentExtent =
|
attachmentExtent = ResolveRenderPassFramebufferExtent(
|
||||||
ResolveRenderPassFramebufferExtent(isDefaultFbo, selectedDepthStencilAttachment->GetSize(),
|
isDefaultFbo,
|
||||||
swapchainExtent);
|
ToVulkanLevelExtent(texture.GetTarget(), selectedDepthStencilAttachment->GetSize()),
|
||||||
|
swapchainExtent);
|
||||||
} else {
|
} else {
|
||||||
const auto& renderbuffer = selectedDepthStencilAttachment->GetRenderbuffer();
|
const auto& renderbuffer = selectedDepthStencilAttachment->GetRenderbuffer();
|
||||||
depthRenderbufferResource = GetOrCreateRenderbufferResource(renderbuffer);
|
depthRenderbufferResource = GetOrCreateRenderbufferResource(renderbuffer);
|
||||||
|
|||||||
@@ -4911,7 +4911,13 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
for (const auto uploadTarget : textureObject->GetUploadTargets()) {
|
for (const auto uploadTarget : textureObject->GetUploadTargets()) {
|
||||||
for (GLsizei level = 0; level < levels; ++level) {
|
for (GLsizei level = 0; level < levels; ++level) {
|
||||||
const GLsizei levelWidth = std::max<GLsizei>(1, width >> level);
|
const GLsizei levelWidth = std::max<GLsizei>(1, width >> level);
|
||||||
const GLsizei levelHeight = std::max<GLsizei>(1, height >> level);
|
// GL 4.6 core 8.19: for GL_TEXTURE_1D_ARRAY the state-side HEIGHT is the LAYER
|
||||||
|
// COUNT, and layers do not halve down the mip chain - level i is
|
||||||
|
// (max(1, width >> i), height). Shrinking it made every mipmapped 1D array
|
||||||
|
// level report fewer layers than it has.
|
||||||
|
const Bool heightIsLayerCount = textureObject->GetTarget() == TextureTarget::Texture1DArray;
|
||||||
|
const GLsizei levelHeight =
|
||||||
|
heightIsLayerCount ? height : std::max<GLsizei>(1, height >> level);
|
||||||
const SizeT byteSize =
|
const SizeT byteSize =
|
||||||
static_cast<SizeT>(levelWidth) * static_cast<SizeT>(levelHeight) * bytesPerPixel;
|
static_cast<SizeT>(levelWidth) * static_cast<SizeT>(levelHeight) * bytesPerPixel;
|
||||||
textureMipmapObject->AllocateStorage(uploadTarget, level, {{levelWidth, levelHeight, 1}, byteSize});
|
textureMipmapObject->AllocateStorage(uploadTarget, level, {{levelWidth, levelHeight, 1}, byteSize});
|
||||||
|
|||||||
@@ -1642,6 +1642,38 @@ TEST_F(TextureTest, TexStorage2DTrimsALongerPreExistingMipChain) {
|
|||||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GL 4.6 core 8.19: for GL_TEXTURE_1D_ARRAY the `height` argument of glTexStorage2D is the LAYER
|
||||||
|
// COUNT, and an array texture's layer count "stays put all the way down the chain" (8.14.3) - only
|
||||||
|
// the image's own axes halve. Shrinking it made level i report height >> i layers, which is also
|
||||||
|
// what ComputeMipmapCompleteForFilter reads (it holds component 1 constant for this target), so
|
||||||
|
// every mipmapped 1D array texture judged itself incomplete.
|
||||||
|
TEST_F(TextureTest, TexStorage2DKeepsA1DArrayLayerCountConstantDownTheMipChain) {
|
||||||
|
GLuint texture = 0;
|
||||||
|
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||||
|
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_1D_ARRAY, texture);
|
||||||
|
|
||||||
|
constexpr GLsizei kLevels = 3;
|
||||||
|
constexpr GLsizei kWidth = 4;
|
||||||
|
constexpr GLsizei kLayers = 4;
|
||||||
|
MG_Impl::GLImpl::TexStorage2D(GL_TEXTURE_1D_ARRAY, kLevels, GL_RGBA8, kWidth, kLayers);
|
||||||
|
ASSERT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||||
|
auto* mipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||||
|
ASSERT_NE(mipmapObject, nullptr);
|
||||||
|
ASSERT_EQ(mipmapObject->GetMipmapLevelCount(), static_cast<Uint>(kLevels));
|
||||||
|
|
||||||
|
for (GLsizei level = 0; level < kLevels; ++level) {
|
||||||
|
const IntVec3 size =
|
||||||
|
mipmapObject->GetMipmapTexelSize(TextureUploadTarget::Texture1DArray, static_cast<Uint>(level));
|
||||||
|
EXPECT_EQ(size.x(), std::max<GLsizei>(1, kWidth >> level)) << "level " << level << " width";
|
||||||
|
EXPECT_EQ(size.y(), kLayers) << "level " << level << " must keep every layer";
|
||||||
|
}
|
||||||
|
|
||||||
|
// The completeness walk is the reason this matters beyond the reported extent.
|
||||||
|
EXPECT_TRUE(textureObject->IsComplete());
|
||||||
|
}
|
||||||
|
|
||||||
// glTexImage2D used to reject every GL_COMPRESSED_* internal format with GL_INVALID_ENUM, because
|
// glTexImage2D used to reject every GL_COMPRESSED_* internal format with GL_INVALID_ENUM, because
|
||||||
// none of them mapped to a TextureInternalFormat and the "unknown format" gate fired. They now
|
// none of them mapped to a TextureInternalFormat and the "unknown format" gate fired. They now
|
||||||
// resolve to the uncompressed storage that backs them - what GL prescribes for the generic formats,
|
// resolve to the uncompressed storage that backs them - what GL prescribes for the generic formats,
|
||||||
|
|||||||
Reference in New Issue
Block a user