mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Fix] (MG_Impl/GLImpl): TexImage3D - apply ConvertInternalFormatToSized like 2D/1D so unsized-internal 3D uploads get channel/type conversion, skip proxy shadow allocation, auto-generate mipmaps; TexSubImage3D - bound level and region against the target mip
This commit is contained in:
@@ -779,6 +779,12 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get()),
|
||||
"Texture object here should always be an object with mipmap");
|
||||
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||
if (static_cast<Uint>(level) >= textureMipmapObject->GetMipmapLevelCount()) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture level is out of range."));
|
||||
return;
|
||||
}
|
||||
|
||||
const void* originalPixels = pixels;
|
||||
const auto& pixelUnpackBufferObject =
|
||||
@@ -811,6 +817,14 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
const SizeT destRowSize = static_cast<SizeT>(texelSize.x()) * internalBpp;
|
||||
const SizeT destSliceSize = static_cast<SizeT>(texelSize.y()) * destRowSize;
|
||||
|
||||
if (xoffset + width > static_cast<GLsizei>(texelSize.x()) ||
|
||||
yoffset + height > static_cast<GLsizei>(texelSize.y()) ||
|
||||
zoffset + depth > static_cast<GLsizei>(texelSize.z())) {
|
||||
MGLOG_E("TexSubImage3D_State: Specified region exceeds texture level dimensions");
|
||||
free(processedPixels);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* srcData = static_cast<const Uint8*>(processedPixels);
|
||||
Uint8* destData = static_cast<Uint8*>(textureMipmapObject->MapMipmapData(textureUploadTarget, level));
|
||||
if (destData) {
|
||||
@@ -1378,6 +1392,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
// target and data is not evenly divisible into the number of bytes needed to store in memory a datum
|
||||
// indicated by type.
|
||||
// ======================= Processing ================================
|
||||
textureInternalFormat =
|
||||
MG_Util::ConvertInternalFormatToSized(textureInternalFormat, textureInputFormat, texturePixelDataType);
|
||||
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
||||
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
||||
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
||||
@@ -1419,7 +1435,11 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||
|
||||
// Allocate in TextureObject
|
||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, height, depth}, internalBytes});
|
||||
if (isProxy) {
|
||||
MGLOG_D("%s: isProxy = true, not allocating", __func__);
|
||||
} else {
|
||||
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, height, depth}, internalBytes});
|
||||
}
|
||||
|
||||
if (!originalPixels) {
|
||||
MGLOG_D("%s: No input pixel and no PBO bound, no pixel transfer", __func__);
|
||||
@@ -1446,6 +1466,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
|
||||
|
||||
free(processedPixels);
|
||||
MaybeAutoGenerateMipmap(target, textureObject, isProxy, level);
|
||||
}
|
||||
|
||||
void TexImage2D_State(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border,
|
||||
|
||||
@@ -774,6 +774,167 @@ TEST_F(TextureTest, TextureStorage3DAndSubImageModifyNamedObjectOnly) {
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
namespace {
|
||||
const Uint8* GetBoundTexture3DLevelBytes(GLuint texture, Uint level = 0) {
|
||||
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||
auto* mipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
||||
return static_cast<const Uint8*>(mipmapObject->MapMipmapData(TextureUploadTarget::Texture3D, level));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_F(TextureTest, BoundTexImage3DUnsizedRgbaInfersRgba8AndUnpacksBgra8888Rev) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, texture);
|
||||
|
||||
const Uint8 pixels[] = {
|
||||
10, 20, 30, 40,
|
||||
50, 60, 70, 80,
|
||||
90, 100, 110, 120,
|
||||
130, 140, 150, 160,
|
||||
};
|
||||
MG_Impl::GLImpl::TexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 2, 1, 2, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
|
||||
|
||||
const auto textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
||||
EXPECT_EQ(textureObject->GetFormat(), TextureInternalFormat::RGBA8);
|
||||
|
||||
const auto* stored = GetBoundTexture3DLevelBytes(texture);
|
||||
ASSERT_NE(stored, nullptr);
|
||||
const Uint8 expected[] = {
|
||||
30, 20, 10, 40,
|
||||
70, 60, 50, 80,
|
||||
110, 100, 90, 120,
|
||||
150, 140, 130, 160,
|
||||
};
|
||||
for (SizeT i = 0; i < sizeof(expected); ++i) {
|
||||
EXPECT_EQ(stored[i], expected[i]) << "byte " << i;
|
||||
}
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(TextureTest, BoundTexImage3DHonorsImageHeightAndSkipImages) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, texture);
|
||||
|
||||
// Source cuboid is 2x3 per image (IMAGE_HEIGHT = 3) with one leading image skipped;
|
||||
// the upload reads a 2x2x2 sub-cuboid.
|
||||
const Uint8 pixels[] = {
|
||||
// image 0 (skipped)
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
// image 1: rows 0-1 are slice 0, row 2 is padding
|
||||
1, 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14, 15, 16,
|
||||
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
|
||||
// image 2: rows 0-1 are slice 1, row 2 is padding
|
||||
17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32,
|
||||
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
|
||||
};
|
||||
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 3);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_IMAGES, 1);
|
||||
MG_Impl::GLImpl::TexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
|
||||
|
||||
const auto* stored = GetBoundTexture3DLevelBytes(texture);
|
||||
ASSERT_NE(stored, nullptr);
|
||||
const Uint8 expected[] = {
|
||||
1, 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32,
|
||||
};
|
||||
for (SizeT i = 0; i < sizeof(expected); ++i) {
|
||||
EXPECT_EQ(stored[i], expected[i]) << "byte " << i;
|
||||
}
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(TextureTest, BoundTexImage3DConvertsRedToRgba8WithImageHeightAndSkips) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, texture);
|
||||
|
||||
// Source cuboid: ROW_LENGTH = 3 (1-byte texels, alignment 1), IMAGE_HEIGHT = 2,
|
||||
// skip 1 image, 0 rows, 1 pixel; upload a 2x1x2 sub-cuboid of GL_RED texels.
|
||||
const Uint8 pixels[] = {
|
||||
// image 0 (skipped)
|
||||
90, 91, 92,
|
||||
93, 94, 95,
|
||||
// image 1: row 0 holds slice 0 at x offset 1, row 1 is padding
|
||||
80, 11, 12,
|
||||
81, 82, 83,
|
||||
// image 2: row 0 holds slice 1 at x offset 1, row 1 is padding
|
||||
84, 21, 22,
|
||||
85, 86, 87,
|
||||
};
|
||||
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ROW_LENGTH, 3);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_PIXELS, 1);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 2);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_IMAGES, 1);
|
||||
MG_Impl::GLImpl::TexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 1, 2, 0, GL_RED, GL_UNSIGNED_BYTE, pixels);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
|
||||
MG_Impl::GLImpl::PixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
|
||||
|
||||
const auto* stored = GetBoundTexture3DLevelBytes(texture);
|
||||
ASSERT_NE(stored, nullptr);
|
||||
const Uint8 expected[] = {
|
||||
11, 0, 0, 255, 12, 0, 0, 255,
|
||||
21, 0, 0, 255, 22, 0, 0, 255,
|
||||
};
|
||||
for (SizeT i = 0; i < sizeof(expected); ++i) {
|
||||
EXPECT_EQ(stored[i], expected[i]) << "byte " << i;
|
||||
}
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(TextureTest, BoundTexSubImage3DUnpacksPackedBgra8888RevIntoCorrectSlice) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, texture);
|
||||
|
||||
const Uint8 zeros[2 * 2 * 2 * 4] = {};
|
||||
MG_Impl::GLImpl::TexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, zeros);
|
||||
|
||||
const Uint8 pixels[] = {10, 20, 30, 40};
|
||||
MG_Impl::GLImpl::TexSubImage3D(GL_TEXTURE_3D, 0, 1, 1, 1, 1, 1, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
|
||||
|
||||
const auto* stored = GetBoundTexture3DLevelBytes(texture);
|
||||
ASSERT_NE(stored, nullptr);
|
||||
Uint8 expected[2 * 2 * 2 * 4] = {};
|
||||
expected[28] = 30;
|
||||
expected[29] = 20;
|
||||
expected[30] = 10;
|
||||
expected[31] = 40;
|
||||
for (SizeT i = 0; i < sizeof(expected); ++i) {
|
||||
EXPECT_EQ(stored[i], expected[i]) << "byte " << i;
|
||||
}
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(TextureTest, BoundTexSubImage3DRejectsOutOfRangeLevel) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::GenTextures(1, &texture);
|
||||
MG_Impl::GLImpl::BindTexture(GL_TEXTURE_3D, texture);
|
||||
|
||||
const Uint8 zeros[2 * 2 * 2 * 4] = {};
|
||||
MG_Impl::GLImpl::TexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, zeros);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||
|
||||
const Uint8 pixels[] = {1, 2, 3, 4};
|
||||
MG_Impl::GLImpl::TexSubImage3D(GL_TEXTURE_3D, 3, 0, 0, 0, 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_INVALID_VALUE);
|
||||
}
|
||||
|
||||
TEST_F(TextureTest, NamedTextureVectorParametersAndGettersWorkWithoutBinding) {
|
||||
GLuint texture = 0;
|
||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture);
|
||||
|
||||
Reference in New Issue
Block a user